Carga de clases dinámica en Python 2.6:Advertencia de tiempo de ejecución:No se encontraron los 'complementos' del módulo principal al manejar la importación absoluta
-
20-09-2019 - |
Pregunta
Estoy trabajando en un sistema de complementos donde los módulos de complementos se cargan así:
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
El código funciona, pero para cada declaración de importación en el código del complemento recibo una advertencia como esta:
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
No se informan errores para el código del programa principal y los complementos funcionan.
¿Alguien puede explicar qué significa la advertencia y qué estoy haciendo mal?¿Necesito crear un módulo de complementos vacío por separado e importarlo para mantener contento a Python?
Solución
Si el directorio plugins
fuera un verdadero paquete (contenida fina __init__.py
), se puede utilizar fácilmente pkgutils enumerar sus archivos de plugin y cargarlos.
import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))
Sin embargo, puede trabajar sin un paquete de plug-in de todos modos, intente lo siguiente:
import pkgutil
list(pkgutil.iter_modules(["plugins"]))
También es posible hacer un paquete que sólo existe en tiempo de ejecución:
import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]
sys.modules["plugins"] = plugins
import plugins.testplugin
Sin embargo, eso truco que era todo para la diversión!
Otros consejos
Si el directorio de plugins no tiene un __init__.py
, no es un paquete, por lo que cuando se crea plugins.whatever
, Python advierte de que tal cosa no debe existir realmente. (No se ha podido crear por "import plugins.whatever
" no importa lo que su trayectoria es.)
Además,
- No divida en
/
, que es no portables. Utiliceos.path.split
. - No utilice
.split(".py")
para obtener el nombre sin la extensión, que está libre de errores. Utiliceos.path.splitext
. - No utilice
getattr
con una cadena literal.getattr(plugin, "__init__")
se escribeplugin.__init__
. - Estoy confundido por qué está llamando una función
__init__
de nivel de módulo. Esto no parece estar bien. Tal vez usted quiere una función "set_logger" o mejor, para crear instancias de una clase que lleva un registrador. - No utilice
L = L + some_other_list
para extender una lista, utilice el métodoextend
, que tiene un mejor rendimiento y es más idiomática. - No aplastar excepciones desconocidas por
except Exception
. Si no se puede planificar para hacer algo cuerdo en respuesta a una excepción, su programa no puede continuar con cordura.
El problema aquí es con el punto ( '') en el nombre del módulo:
imp.load_module('plugins.'+name, f, file, desc)
No incluya una ''. después de 'plugins', o Python pensará que es una ruta del módulo.
Se puede tratar de añadir a continuación declaración al comienzo de las declaraciones de importación.
from __future__ import absolute_import
La pitón imp
La documentación se ha actualizado desde que se respondió esto.Ahora aborda esta cuestión específicamente en el find_module()
método.
Esta función no maneja nombres de módulos jerárquicos (nombres que contienen puntos).Para encontrar PM, es decir, submódulo METRO del paquete PAG, usar
find_module()
yload_module()
para buscar y cargar el paquete PAG, y luego usarfind_module()
con el camino argumento establecido enP.__path__
.Cuando PAG tiene un nombre con puntos, aplique esta receta de forma recursiva.
Tenga en cuenta que P.__path__
ya es una lista al suministrarla a find_module()
.Tenga en cuenta también lo que find_module()
La documentación dice sobre la búsqueda de paquetes.
Si el módulo es un paquete, archivo es
None
, nombre de ruta es la ruta del paquete y el último elemento del descripción tupla esPKG_DIRECTORY
.
Entonces, según la pregunta del OP, importar el complemento sin el RuntimeError
advertencias, siga las instrucciones en la documentación actualizada de Python:
# 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)