2010-02-15 11 views
22

ich auf einem Plugin-System arbeite, wo Plugin-Module wie folgt geladen werden:Dynamische Laden von Klassen in Python 2.6: RuntimeWarning: Parent-Modul 'Plugins' nicht gefunden beim Umgang mit absolutem Import

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 

Der Code funktioniert, aber für jede Import-Anweisung in dem Plugin-Code erhalte ich eine Warnung wie folgen aus:

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

keine Fehler für den Hauptprogrammcode gemeldet werden, und die Plug-in arbeiten.

Kann jemand erklären, was die Warnung bedeutet und was ich falsch mache. Muss ich ein leeres Plugins-Modul separat erstellen und es importieren, um Python glücklich zu machen?

+1

der Ordnung halber: ' 'plugins'' in' Geordnetes Modul' plugins 'not found' kommt vom 'name'-Wert, der an [imp.load_module] (https://docs.python.org/2/library/imp.html#imp.load_module) übergeben wurde, z. '" plugins.something "' in 'imp.load_module (" plugins.something ")'. In meinem Fall war der 'name' Wert wie' ".etwas" 'und somit enthielt die Nachricht' '' 'anstelle von' 'plugins''. – n611x007

Antwort

14

Wenn das Verzeichnis plugins ein echtes Paket ist (enthalten __init__.py fein), könnte man leicht pkgutils verwenden, um seine Plugin-Dateien auflisten und sie zu laden.

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

Es kann aber trotzdem ohne Plugin-Paket funktioniert, versuchen Sie dies:

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

Auch ist es möglich, ein Paket zu machen, die erst zur Laufzeit vorhanden ist:

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

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

jedoch dieser Hack, der hauptsächlich zum Spaß war!

15

Wenn das Verzeichnis plugins keine __init__.py hat, ist es kein Paket, also, wenn Sie plugins.whatever erstellen, warnt Python Sie, dass so etwas nicht wirklich existieren sollte. (Es konnte nicht von „import plugins.whatever“ egal erstellt werden, was Ihr Weg ist.)

Auch

  • Sie spalten nicht auf /, die portabel ist. Verwenden Sie os.path.split.
  • Verwenden Sie nicht .split(".py"), um den Namen ohne die Erweiterung zu erhalten, die fehlerhaft ist. Verwenden Sie os.path.splitext.
  • Verwenden Sie nicht getattr mit einem Zeichenfolgenliteral. getattr(plugin, "__init__") ist plugin.__init__ geschrieben.
  • Ich bin verwirrt, warum Sie eine Modulebene __init__ Funktion aufrufen. Das scheint nicht richtig zu sein. Vielleicht möchten Sie eine "set_logger" -Funktion oder besser, um eine Klasse zu instanziieren, die einen Logger nimmt.
  • Verwenden Sie nicht L = L + some_other_list, um eine Liste zu erweitern, verwenden Sie die extend Methode, die eine bessere Leistung hat und idiomatischer ist.
  • Quetschen Sie keine unbekannten Ausnahmen durch except Exception. Wenn Sie als Reaktion auf eine Ausnahme nicht planen können, etwas Sinnvolles zu tun, kann Ihr Programm nicht vernünftig weitergehen.
+0

Beachten Sie, dass das Plugins-Verzeichnis kein Paket ist und nicht importiert wird (ich möchte Dateien daraus auswählen). Der Rest des Codes wird als Ei kompiliert und direkt mit "python -m" ausgeführt. Ich füge das Plugin-Verzeichnis niemals dem sys.path hinzu. – pehrs

+1

Richtig, aber wenn du ein Plugin 'foo' hast, hast du gesagt, dass es' plugins.foo' ist, also denkt es, dass es Teil des 'plugins' Pakets ist (welches nicht existiert). Wenn Sie innerhalb von 'foo.py'' os importieren' (oder, wenn Sie 'irgendjemandes 'importiert haben), erhalten Sie diese Warnung, weil' plugins' nicht wirklich existiert. –

+0

(Das bedeutet, dass Sie die Warnung ignorieren, das Benennungsschema ändern oder die Struktur so ändern müssen, dass Python dies erwartet.) –

4

Das Problem hier ist mit dem Punkt (' ') in dem Modulnamen:'.'

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

Sie einen nicht enthalten nach 'Plugins', oder Python wird denken, es ist ein Modul-Pfad.

+0

fantastisch! ich danke dir sehr! –

0

Die Python imp Dokumentation wurde aktualisiert, seit dieser Frage beantwortet wurde. Dieses Problem wird jetzt speziell in der Methode find_module() behandelt.

Diese Funktion behandelt keine hierarchischen Modulnamen (Namen mit Punkten). Um P. M, das heißt zu finden, Submodul M von Paket P, verwenden find_module() und load_module() zu finden und Last Paket P und dann find_module() mit dem Argumente P.__path__ set path verwenden. Wenn P selbst einen Punktnamen hat, wenden Sie dieses Rezept rekursiv an.

Beachten Sie, dass P.__path__ ist bereits eine Liste, wenn es um find_module() liefern. Beachten Sie auch, was in der Dokumentation find_module() zum Finden von Paketen steht.

Wenn das Modul ist ein Paket, DateiNone ist, Pfadname der Paketpfad ist und das letzte Element in der Beschreibung Tupel PKG_DIRECTORY ist.

So von der Frage des OP, das Plugin ohne die RuntimeError Warnungen zu importieren, die Anweisungen in der aktualisierten Python-Dokumentation folgen:

# 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)