2010-02-15 4 views
22

Sto lavorando su un sistema di plugin in cui moduli plugin sono caricati in questo modo:Classe dinamica in Python 2.6: RuntimeWarning: modulo Parent 'plugins' non trovato durante la manipolazione di importazione assoluta

def load_plugins(): 
    plugins=glob.glob("plugins/*.py") 
    instances=[] 
    for p in plugins: 
     try: 
     name=p.split("/")[-1] 
     name=name.split(".py")[0] 
     log.debug("Possible plugin: %s", name) 
     f, file, desc=imp.find_module(name, ["plugins"]) 
     plugin=imp.load_module('plugins.'+name, f, file, desc) 
     getattr(plugin, "__init__")(log) 
     instances=instances+plugin.get_instances() 
     except Exception as e: 
     log.info("Failed to load plugin: "+str(p)) 
     log.info("Error: %s " % (e)) 
     log.info(traceback.format_exc(e)) 
    return instances 

il codice funziona, ma per ogni istruzione import nel codice del plugin Ho ricevuto un avviso simile a questo:

plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import 
    import os 

Nessun errore vengono segnalati per il codice del programma principale, e il lavoro plugin.

Qualcuno può spiegare cosa significa l'avvertimento e cosa sto facendo male. Devo creare un modulo plugin vuoto separatamente e importarlo per mantenere Python felice?

+1

per la cronaca: ' 'plugins'' in' modulo Parent' i plugin 'non trovati' provengono dal valore 'name' passato a [imp.load_module] (https://docs.python.org/2/library/imp.html#imp.load_module), ad es. '" plugins.something "' in 'imp.load_module (" plugins.something ")'. Nel mio caso il valore 'name' era come' ".qualcosa" 'e quindi il messaggio conteneva' '' 'invece di' 'plugins''. – n611x007

risposta

14

Se la directory plugins fosse un vero pacchetto (contenuta __init__.py bene), si potrebbe facilmente utilizzare pkgutils per enumerare le sue file dei plugin e caricarli.

import pkgutil 
# import our package 
import plugins 
list(pkgutil.iter_modules(plugins.__path__)) 

Tuttavia, può funzionare senza un pacchetto di plug-in in ogni caso, provate questo:

import pkgutil 
list(pkgutil.iter_modules(["plugins"])) 

Inoltre è possibile fare un pacchetto che esiste solo in fase di esecuzione:

import types 
import sys 
plugins = types.ModuleType("plugins") 
plugins.__path__ = ["plugins"] 

sys.modules["plugins"] = plugins 
import plugins.testplugin 

Tuttavia quello hack che era principalmente per divertimento!

15

Se la directory dei plug-in non ha un __init__.py, non è un pacchetto, quindi quando si crea plugins.whatever, Python avverte che una cosa del genere non dovrebbe esistere realmente. (Non poteva essere creata da "import plugins.whatever" non importa quale sia il vostro percorso è.)

Inoltre,

  • Non dividere il /, che è portabile. Utilizzare os.path.split.
  • Non utilizzare .split(".py") per ottenere il nome senza estensione, che è bacato. Utilizzare os.path.splitext.
  • Non utilizzare getattr con una stringa letterale. getattr(plugin, "__init__") è scritto plugin.__init__.
  • Sono confuso perché si sta chiamando un livello di modulo __init__ funzione. Questo non sembra giusto. Forse vuoi una funzione "set_logger" o meglio, per istanziare una classe che prende un logger.
  • Non utilizzare L = L + some_other_list per estendere un elenco, utilizzare il metodo extend, che ha prestazioni migliori ed è più idiomatica.
  • Non schiacciare eccezioni sconosciuti except Exception. Se non puoi pianificare di fare qualcosa di sano in risposta a un'eccezione, il tuo programma non può andare bene.
+0

Si noti che la directory dei plug-in non è pensata per essere un pacchetto e non è importata (voglio selezionarne i file da essa). Il resto del codice è compilato come un uovo e viene eseguito direttamente usando "python -m". Non aggiungo mai la directory dei plugin a sys.path. – pehrs

+1

Giusto, ma se hai un plugin 'foo', hai detto che è' plugins.foo', quindi pensa che faccia parte del pacchetto 'plugins' (che non esiste). Quando entro "foo.py' tu" importa "(o se hai" importato qualcosa "), ricevi questo avviso perché" plugins "non esiste realmente. –

+0

(Ciò significa che le opzioni sono di ignorare l'avviso, modificare lo schema di denominazione o modificare la struttura in modo che corrisponda a ciò che Python si aspetta). –

4

Il problema qui è con il punto (' ') nel nome del modulo:

imp.load_module('plugins.'+name, f, file, desc)

non includono un'.' dopo "plugins", o Python penserà che sia un percorso del modulo.

+0

fantastico! grazie mille! –

0

La documentazione di Python imp è stata aggiornata poiché è stata fornita una risposta. Ora risolve questo problema in modo specifico nel metodo find_module().

Questa funzione non gestisce i nomi dei moduli gerarchici (nomi contenenti punti). Per trovare pm, cioè modulo M del pacchetto P, utilizzare find_module() e load_module() trovare e pacchetto carico P, e quindi utilizzare find_module() con il percorso argomento impostato P.__path__. Se P ha un nome puntato, applicare questa ricetta in modo ricorsivo.

Nota che P.__path__ è già una lista quando forniscono a find_module(). Nota anche cosa dice la documentazione find_module() sulla ricerca dei pacchetti.

Se il modulo è un pacchetto, file di è None, percorso è il percorso del pacchetto e l'ultimo elemento della descrizione tupla è PKG_DIRECTORY.

Così da domanda del PO, per importare il plugin senza i RuntimeError avvertimenti, seguire le istruzioni fornite nella documentazione Python aggiornamento:

# first find and load the package assuming it is in 
# the current working directory, '.' 

f, file, desc = imp.find_module('plugins', ['.']) 
pkg = imp.load_module('plugins', f, file, desc) 

# then find the named plugin module using pkg.__path__ 
# and load the module using the dotted name 

f, file, desc = imp.find_module(name, pkg.__path__) 
plugin = imp.load_module('plugins.' + name, f, file, desc)