Python: realiza una importación relativa cuando usas __import__?

Aquí están los archivos en esta prueba:

main.py app/ |- __init__.py |- master.py |- plugin/ |- |- __init__.py |- |- p1.py |- |_ p2.py 

La idea es tener una aplicación capaz de plugin. Los nuevos archivos .py o .pyc se pueden colocar en complementos que se adhieran a mi API.

Tengo un archivo master.py en el nivel de la aplicación que contiene variables y funciones globales a las que todos los complementos pueden tener acceso, así como la aplicación en sí. Para los fines de esta prueba, la “aplicación” consiste en una función de prueba en app / __ init__.py. En la práctica, la aplicación probablemente se movería a uno o varios archivos de código separados, pero luego solo usaría import master en ese archivo de código para llevar la referencia al master .

Aquí está el contenido del archivo:

main.py:

 import app app.test() app.test2() 

app / __ init__.py:

 import sys, os from plugin import p1 def test(): print "__init__ in app is executing test" p1.test() def test2(): print "__init__ in app is executing test2" scriptDir = os.path.join ( os.path.dirname(os.path.abspath(__file__)), "plugin" ) print "The scriptdir is %s" % scriptDir sys.path.insert(0,scriptDir) m = __import__("p2", globals(), locals(), [], -1) m.test() 

app / master.py:

 myVar = 0 

app / plugin / __ init__.py:

  

aplicación / plugin / p1.py:

 from .. import master def test(): print "test in p1 is running" print "from p1: myVar = %d" % master.myVar 

aplicación / plugin / p2.py:

 from .. import master def test(): master.myVar = 2 print "test in p2 is running" print "from p2, myVar: %d" % master.myVar 

Desde que p1 explícitamente el módulo p1 , todo funciona como se esperaba. Sin embargo, cuando uso __import__ para importar p2, obtengo el siguiente error:

 __init__ in app is executing test test in p1 is running from p1: myVar = 0 __init__ in app is executing test2 The scriptdir is ....../python/test1/app/plugin Traceback (most recent call last): File "main.py", line 4, in  app.test2() File "....../python/test1/app/__init__.py", line 17, in test2 m = __import__("p2", globals(), locals(), [], -1) File "....../python/test1/app/plugin/p2.py", line 1, in  from .. import master ValueError: Attempted relative import in non-package 

La ejecución avanza completamente a través de la función test () y los errores salen bien cuando test2 () intenta ejecutar su statement __import__ , que a su vez p2 intenta realizar una importación relativa (que funciona cuando p1 se importa explícitamente a través de la statement de importación). recordar)

Está claro que usar __import__ está haciendo algo diferente que usar la statement de import . Los documentos de Python afirman que el uso de la importación simplemente se traduce en una statement __import__ internamente, pero tiene que haber más cosas de las que parece.

Como la aplicación está basada en complementos, la encoding de las declaraciones de importación explícitas en la aplicación principal, por supuesto, no sería factible. Usando importarse dentro de la

¿Que me estoy perdiendo aqui? ¿Cómo puedo hacer que Python se comporte como se espera cuando se importan módulos manualmente usando __import__ ? Parece que tal vez no entiendo completamente la idea de las importaciones relativas, o que simplemente me estoy perdiendo algo con respecto a dónde se está produciendo la importación (es decir, dentro de una función y no en la raíz del archivo de código)

EDITAR: Encontré las siguientes soluciones posibles, pero no exitosas:

 m = __import__("p2",globals(),locals(),"plugin") 

(devuelve el mismo error exacto que el anterior)

 m = __import__("plugin",fromlist="p2") 

(devuelve una referencia a app.plugin, no a app.plugin.p2)

 m = __import__("plugin.p2",globals(),locals()) 

(devuelve una referencia a app.plugin, no a app.plugin.p2)

 import importlib m = importlib.import_module("plugin.p2") 

(devoluciones:)

 Traceback (most recent call last): File "main.py", line 4, in  app.test2() File "....../python/test1/app/__init__.py", line 20, in test2 m = importlib.import_module("plugin.p2") File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module __import__(name) ImportError: No module named plugin.p2 

He tenido un problema similar.
__import__ solo importa submódulos si todos los archivos primarios __init__.py están vacíos. Deberías usar importlib en su lugar

 import importlib p2 = importlib.import_module('plugin.p2') 

Nunca encontré una solución, así que decidí reestructurar el progtwig.

Lo que hice fue configurar la aplicación principal como una clase. Luego, también cambié cada plugin en una clase. Luego, al cargar los complementos utilizando la importación , también hago una instancia de la clase dentro de cada complemento que tiene un nombre predefinido, y paso la referencia a la clase de la aplicación principal.

Esto significa que cada clase puede leer y manipular directamente las variables en la clase de host simplemente usando la referencia. Es totalmente flexible porque todos los complementos pueden acceder a todo lo que exporta la clase host.

Esto resulta ser más efectivo y no depende de las rutas relativas y nada de eso. También significa que un intérprete de Python podría, en teoría, ejecutar varias instancias de la aplicación del host simultáneamente (en diferentes subprocesos, por ejemplo) y los complementos seguirán remitiéndose a la instancia del host correcta.

Esto es básicamente lo que hice:

main.py:

 import os, os.path, sys class MyApp: _plugins = [] def __init__(self): self.myVar = 0 def loadPlugins(self): scriptDir = os.path.join ( os.path.dirname(os.path.abspath(__file__)), "plugin" ) sys.path.insert(0,scriptDir) for plug in os.listdir(scriptDir): if (plug[-3:].lower() == ".py"): m = __import__(os.path.basename(plug)[:-3]) self._plugins.append(m.Plugin(self)) def runTests(self): for p in self._plugins: p.test() if (__name__ == "__main__"): app = MyApp() app.loadPlugins() app.runTests() 

plugin / p1.py:

 class Plugin: def __init__(self, host): self.host = host def test(self): print "from p1: myVar = %d" % self.host.myVar 

plugin / p2.py:

 class Plugin: def __init__(self, host): self.host = host def test(self): print "from p2: variable set" self.host.myVar = 1 print "from p2: myVar = %d" % self.host.myVar 

Hay algo de espacio para mejorar esto, por ejemplo, validando cada archivo .py importado para ver si realmente es un complemento y así sucesivamente. Pero esto funciona como se esperaba.

¿Has probado la siguiente syntax:

Cómo utilizar correctamente la función de importación de Python __import __ ()

Me funcionó con un problema similar …