chargement dynamique des classes en Python 2.6: RuntimeWarning: Module parent « plugins » introuvable lors de la manipulation importation absolue
-
20-09-2019 - |
Question
Je travaille sur un système de plug-in où les modules de plug-ins sont chargés comme ceci:
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
Le code fonctionne, mais pour chaque déclaration d'importation dans le code du plugin i obtenir un avertissement comme ceci:
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
Aucune erreur ne sont signalées pour le code principal du programme, et les plug-ins fonctionnent.
Quelqu'un peut-il expliquer ce que les moyens d'avertissement et ce que je fais mal. Ai-je besoin de créer un module plugins vide séparément et importer pour garder python heureux?
La solution
Si le plugins
de répertoire était un vrai paquet (contenu __init__.py
fine), vous pouvez facilement utiliser pkgutils d'énumérer ses fichiers de plugin et de les charger.
import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))
Cependant, il peut fonctionner sans un paquet de plug-in de toute façon, essayez ceci:
import pkgutil
list(pkgutil.iter_modules(["plugins"]))
En outre, il est possible de faire un paquet qui existe seulement à l'exécution:
import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]
sys.modules["plugins"] = plugins
import plugins.testplugin
Cependant, ce hack qui était la plupart du temps pour le plaisir!
Autres conseils
Si le répertoire plugins ne dispose pas d'un __init__.py
, ce n'est pas un paquet, donc quand vous créez plugins.whatever
, Python vous avertit qu'une telle chose ne devrait pas exister vraiment. (Il ne peut être créé par « import plugins.whatever
» peu importe ce que votre chemin est.)
En outre,
- Ne pas couper sur
/
, qui est non portable. Utilisezos.path.split
. - Ne pas utiliser
.split(".py")
pour obtenir le nom sans l'extension, qui est buggy. Utilisezos.path.splitext
. - Ne pas utiliser
getattr
avec une chaîne littérale.getattr(plugin, "__init__")
est orthographiéplugin.__init__
. - Je suis confus pourquoi vous appelez un module de niveau fonction
__init__
. Cela ne semble pas juste. Peut-être que vous voulez une fonction « set_logger » ou mieux, pour instancier une classe qui prend un enregistreur. - Ne pas utiliser
L = L + some_other_list
d'étendre la liste, utilisez la méthodeextend
, qui a de meilleures performances et plus idiomatiques. - Ne pas écraser par des exceptions
except Exception
inconnus. Si vous ne pouvez pas l'intention de faire quelque chose de sain d'esprit en réponse à une exception, votre programme ne peut pas continuer sanely.
Le problème est ici avec le point ( '') au nom du module:
imp.load_module('plugins.'+name, f, file, desc)
Ne pas inclure « » après 'plugins', ou Python pensera qu'il est un chemin de module.
Vous pouvez essayer d'ajouter ci-dessous déclaration au début des déclarations d'importation.
from __future__ import absolute_import
Le Python imp
la documentation a été mis à jour depuis cela a été répondu. Il aborde maintenant cette question spécifiquement dans la méthode find_module()
.
Cette fonction ne gère pas les noms de modules hiérarchiques (noms contenant des points). Pour trouver PM , qui est, sous-module M du paquet P , utilisez
find_module()
etload_module()
pour trouver et paquet de charge P , et puis utilisezfind_module()
avec chemin argumentation àP.__path__
. Lorsque P a lui-même un nom en pointillé, appliquez cette recette récursive.
Notez que P.__path__
est déjà une liste en fournissant à find_module()
. A noter également que le find_module()
la documentation dit de trouver des paquets.
Si le module est un package, fichier est
None
, chemin est le chemin de l'emballage et le dernier élément dans la Description tuple est < a href = "https://docs.python.org/2/library/imp.html#imp.PKG_DIRECTORY" rel = "nofollow">PKG_DIRECTORY
.
de la question de l'OP, d'importer le plug-in sans les avertissements RuntimeError
, suivez les instructions de la documentation Python mise à jour:
# 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)