2009-02-18 10 views
18

Escribo un software en github. Básicamente es un icono de bandeja con algunas características adicionales. Quiero proporcionar un código de trabajo sin tener que hacer que el usuario instale lo que son esencialmente dependencias para funciones opcionales y no quiero importar cosas que no voy a usar, así que pensé que un código como este sería " buena solución ":¿Cuál es la buena práctica de Python para importar y ofrecer características opcionales?

---- IN LOADING FUNCTION ---- 
features = [] 

for path in sys.path: 
     if os.path.exists(os.path.join(path, 'pynotify')): 
       features.append('pynotify') 
     if os.path.exists(os.path.join(path, 'gnomekeyring.so')): 
       features.append('gnome-keyring') 

#user dialog to ask for stuff 
#notifications available, do you want them enabled? 
dlg = ConfigDialog(features) 

if not dlg.get_notifications(): 
    features.remove('pynotify') 


service_start(features ...) 

---- SOMEWHERE ELSE ------ 

def service_start(features, other_config): 

     if 'pynotify' in features: 
       import pynotify 
       #use pynotify... 

Sin embargo, hay algunos problemas. Si un usuario formatea su máquina e instala la última versión de su sistema operativo y vuelve a desplegar esta aplicación, las características desaparecen repentinamente sin previo aviso. La solución es presentar esto en la ventana de configuración:

if 'pynotify' in features: 
    #gtk checkbox 
else: 
    #gtk label reading "Get pynotify and enjoy notification pop ups!" 

Pero si esto es decir, un Mac, ¿cómo sé que no estoy enviando al usuario en una búsqueda inútil en busca de una dependencia que nunca pueden ¿llenar?

El segundo problema es el: tema

if os.path.exists(os.path.join(path, 'gnomekeyring.so')): 

. ¿Puedo estar seguro de que el archivo siempre se llama gnomekeyring.so en todas las distribuciones de Linux?

¿Cómo prueban otras personas estas características? El problema con el

básica
try: 
    import pynotify 
except: 
    pynotify = disabled 

es que el código es global, estos podrían ser esparcidos alrededor e incluso si el usuario no desea pynotify .... Está cargado de todos modos.

Entonces, ¿qué piensan las personas que es la mejor manera de resolver este problema?

Respuesta

10

Es posible que desee echar un vistazo a imp module, que básicamente hace lo que hace manualmente arriba. Por lo tanto, primero puede buscar un módulo con find_module() y luego cargarlo a través de load_module() o simplemente importándolo (después de verificar la configuración).

Y por cierto, si se usa, excepto: siempre agregaría la excepción específica (aquí ImportError) para no accidentalmente detectar errores no relacionados.

36

El método try: no necesita ser global, se puede usar en cualquier ámbito y, por lo tanto, los módulos pueden cargarse "por latencia" en tiempo de ejecución. Por ejemplo:

def foo(): 
    try: 
     import external_module 
    except ImportError: 
     pass 

    if external_module: 
     external_module.some_whizzy_feature() 
    else: 
     print "You could be using a whizzy feature right now, if you had external_module." 

Cuando se ejecuta la secuencia de comandos, no se hará ningún intento de cargar external_module. La primera vez que se llama al foo(), external_module está (si está disponible) cargado e insertado en el alcance local de la función. Las llamadas posteriores al foo() reinsertan external_module en su alcance sin necesidad de volver a cargar el módulo.

En general, es mejor dejar que Python maneje la lógica de importación, lo ha estado haciendo por un tiempo. :-)

+8

Creo que el bloque 'except ImportError' necesita establecer' external_module = None' o obtendrá un 'NameError' cuando intente acceder a él en el bloque if. – abhishekmukherg

+0

Al igual que, intente: excepto: (sin el tipo de excepción), es un anti-patrón, la captura de ImportError puede no ser lo que desea. Si el módulo existe, pero genera una excepción, lo detectará y continuará ocultando el error. Es mucho mejor verificar primero la existencia del módulo, decidir si quieres importarlo y luego * no * manejar el ImportError – user48956

0

Una forma de manejar el problema de las diferentes dependencias para diferentes características es implementar las características opcionales como complementos. De esta forma, el usuario tiene control sobre qué características están activadas en la aplicación, pero no es responsable de rastrear las dependencias ella misma. Esa tarea luego se maneja en el momento de la instalación de cada complemento.

Cuestiones relacionadas