2010-02-15 16 views
22

estoy trabajando en un sistema de plugins donde los módulos plug-in se cargan de esta manera:Clase dinámica en Python 2.6: RuntimeWarning: Módulo de Padres 'plugins' no encontrado durante la manipulación de importación absoluta

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 plugin recibo una advertencia como esta:

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

se informa de ningún error para el código principal del programa, y ​​el trabajo de plugins.

¿Alguien puede explicar lo que significa la advertencia y lo que estoy haciendo mal? ¿Debo crear un módulo de complementos vacío por separado e importarlo para mantener a python satisfecho?

+1

para el registro: ' 'plugins'' en módulo' Padre' plugins 'not found' proviene del valor 'name' pasado a [imp.load_module] (https://docs.python.org/2/library/imp.html#imp.load_module), ej. '" plugins.something "' en 'imp.load_module (" plugins.something ")'. En mi caso, el valor 'name' era como' ".something" 'y, por lo tanto, el mensaje contenía' '' 'en lugar de' 'plugins''. – n611x007

Respuesta

14

Si el directorio plugins fuera un verdadero paquete (figura __init__.py fina), 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 esto:

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 ¡ese truco fue principalmente por diversión!

15

Si el directorio de complementos no tiene un __init__.py, no es un paquete, por lo que cuando crea plugins.whatever, Python le advierte que tal cosa no debería 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. Use os.path.split.
  • No use .split(".py") para obtener el nombre sin la extensión, que tiene errores. Use os.path.splitext.
  • No use getattr con una cadena literal. getattr(plugin, "__init__") se deletrea plugin.__init__.
  • No entiendo por qué llamas a una función de nivel de módulo __init__. Esto no parece correcto. Quizás desee una función "set_logger" o mejor, para instanciar una clase que tenga un registrador.
  • No utilice L = L + some_other_list para extender una lista, utilice el método extend, que tiene un mejor rendimiento y es más idiomático.
  • No squash excepciones desconocidas por except Exception. Si no puede planear hacer algo sensato en respuesta a una excepción, su programa no puede seguir sano.
+0

Tenga en cuenta que el directorio de complementos no está destinado a ser un paquete, y no se importa (quiero elegir los archivos desde él). El resto del código se compila como un huevo y se ejecuta directamente usando "python -m". Nunca agrego el directorio del complemento a sys.path. – pehrs

+1

Correcto, pero si tiene un plugin 'foo', le ha dicho que es' plugins.foo', por lo que cree que es parte del paquete 'plugins' (que no existe). Cuando dentro de 'foo.py' tu' import os' (o si 'import anythingelse' importado), obtienes esa advertencia porque 'plugins' realmente no existe. –

+0

(Esto significa que sus opciones son ignorar la advertencia, cambiar su esquema de nombres o cambiar su estructura para que coincida con lo que Python espera). –

4

El problema aquí es con el punto (' ') en el nombre del módulo:'.'

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

no incluyen una después de 'complementos', o Python pensará que es una ruta de módulo.

+0

¡fantástico! ¡Muchas gracias! –

0

La documentación de Python imp se ha actualizado porque se ha respondido. Ahora aborda este problema específicamente en el método find_module().

Esta función no maneja los nombres de los módulos jerárquicos (nombres que contienen puntos). Con el fin de encontrar P.M, es decir, submódulo M de paquete P, utilice find_module() y load_module() para encontrar y el paquete de carga P, y luego usar find_module() con el camino argumento establecido en P.__path__. Cuando P tiene un nombre punteado, aplique esta receta recursivamente.

Tenga en cuenta que P.__path__ ya es una lista al suministrarla a find_module(). También tenga en cuenta lo que dice la documentación find_module() 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 artículo en la descripción tupla es PKG_DIRECTORY.

lo tanto, desde la pregunta de la OP, para importar el plugin sin los RuntimeError advertencias, siga las instrucciones de la documentación de Python actualización:

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