2009-02-18 2 views
18

Sto scrivendo un pezzo di software su github. È fondamentalmente un'icona di vassoio con alcune funzionalità extra. Voglio fornire un pezzo di codice funzionante senza dover effettivamente installare l'utente quali sono essenzialmente le dipendenze per le funzionalità opzionali e in realtà non voglio importare cose che non userò, quindi ho pensato che un codice come questo sarebbe stato " buona soluzione ":Qual è la buona pratica di Python per l'importazione e l'offerta di funzionalità opzionali?

Ci sono alcuni problemi tuttavia. Se un utente formatta la sua macchina e installa la versione più recente del suo sistema operativo e ridistribuisce questa applicazione, le funzionalità scompaiono improvvisamente senza preavviso. La soluzione è quella di presentare questo nella finestra di configurazione:

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

Ma se questo è dire, un mac, come faccio a sapere che non mando l'utente su un inseguimento oca selvatica in cerca di una dipendenza non possono mai riempire?

Il secondo problema è il: problema

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

. Posso essere sicuro che il file sia sempre chiamato gnomekeyring.so attraverso tutte le distro di Linux?

In che modo altre persone verificano queste funzionalità? Il problema con la base

try: 
    import pynotify 
except: 
    pynotify = disabled 

è che il codice è globale, questi potrebbero essere disseminato in giro e anche se l'utente non vuole pynotify .... è caricato in ogni caso.

Quindi cosa pensa la gente è il modo migliore per risolvere questo problema?

risposta

10

Si potrebbe desiderare di dare un'occhiata allo imp module, che fondamentalmente fa ciò che si fa manualmente sopra. Quindi puoi prima cercare un modulo con find_module() e quindi caricarlo tramite load_module() o semplicemente importarlo (dopo aver controllato la configurazione).

E btw, se si utilizza eccetto: aggiungo sempre l'eccezione specifica (qui ImportError) per non catturare accidentalmente errori non correlati.

36

Il metodo try: non ha bisogno di essere globale: può essere utilizzato in qualsiasi ambito e quindi i moduli possono essere "lazy-loaded" in fase di esecuzione. Per esempio:

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." 

Quando lo script viene eseguito, nessun tentativo sarà fatto per caricare external_module. La prima volta che viene chiamato foo(), external_module è (se disponibile) caricato e inserito nell'ambito locale della funzione. Le chiamate successive a foo() reinseriscono external_module nel suo ambito senza dover ricaricare il modulo.

In generale, è meglio lasciare che Python gestisca la logica di importazione, lo sta facendo da un po '. :-)

+8

Penso che il blocco 'except ImportError' deve impostare' external_module = None' o si otterrà un 'NameError' quando si tenta di accedervi nel blocco if. – abhishekmukherg

+0

Molto simile, prova: tranne: (senza tipo di eccezione), è un Anti-pattern, che cattura ImportError potrebbe non essere quello che vuoi. Se il modulo esiste, ma solleva un'eccezione, lo prendi e continua, nascondendo l'errore. È molto meglio prima verificare l'esistenza del modulo, decidere se si desidera importarlo e quindi * non * gestire l'ImportError – user48956

0

Un modo per gestire il problema delle dipendenze diverse per funzionalità diverse è implementare le funzionalità opzionali come plug-in. In questo modo l'utente ha il controllo su quali funzionalità sono attivate nell'app ma non è responsabile della localizzazione delle dipendenze. L'attività viene quindi gestita al momento dell'installazione di ciascun plug-in.