Python: do relative imports when using __import__?

Here are the files in this test:

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

The idea is to have an application that supports the plugin. New .py or .pyc files can be dropped into plugins that match my API.

I have an master.py -level master.py file that contains global variables and functions that any plugin can access, as well as the application itself. For the purposes of this test, an “application” consists of a test function in app / __ init__.py. In practice, the application is likely to be moved to a separate code file, but then I would simply use import master in this code file to provide a link to master .

Here is the contents of the file:

main.py:

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

Appendix / __ __ INIT ru :.

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

Application / master.py:

 myVar = 0 

application / plugin / __ __ INIT ru :.

 <empty file> 

application / plugin / p1.py:

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

application / plugin / p2.py:

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

Since I explicitly import the p1 module, everything works as expected. However, when I use __import__ to import p2, I get the following 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 <module> 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 <module> from .. import master ValueError: Attempted relative import in non-package 

Execution is performed completely through the test () function and errors exit correctly, since test2 () tries to execute the __import__ statement, which, in turn, tries to do relative imports (which does work when p1 is imported explicitly through the import statement, recall)

Clearly, using __import__ does something different than using the import statement. Python docs point out that using import simply translates to the __import__ statement inside, but should be more than it seems at first glance.

Since the application is plugin-based, encoding explicit import statements in the main application is, of course, impractical. Using the import itself inside

What am I missing here? How can I make Python behave as expected when manually importing modules using __import__ ? Perhaps I do not quite understand the idea of ​​relative imports, or that I just missed something regarding where the import occurs (i.e. inside a function, not in the root code file).

EDIT: I found the following possible but unsuccessful solutions:

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

(returns the same exact error as above)

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

(returns a link to app.plugin, not app.plugin.p2)

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

(returns a link to app.plugin, not app.plugin.p2)

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

(returns :)

 Traceback (most recent call last): File "main.py", line 4, in <module> 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 
+7
source share
3 answers

I never found a solution, so I decided to restructure the program.

What I did was set up the main application as a class. Then I also changed each plugin to a class. Then, when I load plugins using import , I also create an instance of the class inside each plugin with a predefined name and pass a link to the main class of the application.

This means that each class can directly read and manipulate variables in the host class, simply using a link. It is completely flexible because everything that the host class exports is available to all plugins.

This turns out to be more efficient and independent of relative paths and all of this. It also means that a single Python interpreter can theoretically run multiple instances of the host application at the same time (for example, for different threads), and plugins will still reference the correct host instance.

Here is basically what I did:

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 

It is possible to improve this, for example, check each imported .py file to see if it is a plugin and so on. But it works as expected.

+1
source

I had a similar problem.
__import__ only imports submodules if all parent __init__.py files are empty. You should use importlib instead

 import importlib p2 = importlib.import_module('plugin.p2') 
+3
source

You tried the following syntax:

How to use python __import __ () import function correctly

This worked for me with a similar problem ...

+1
source

All Articles