Reputation: 59604
Python imports. Again...
I have this file structure:
[test]
start.py (from scripts import main)
[scripts]
__init__.py (empty)
main.py (from . import install)
install.py (from scripts import main # or # from . import main)
I get import error:
vic@wic:~/projects/test$ python3 start.py
Traceback (most recent call last):
File "start.py", line 2, in <module>
from scripts import main
File "/home/vic/projects/test/scripts/main.py", line 1, in <module>
from . import install
File "/home/vic/projects/test/scripts/install.py", line 1, in <module>
from scripts import main
ImportError: cannot import name main
vic@wic:~/projects/test$
I don't understand: first time from scripts import main
worked (by "worked" I mean it didn't fail with ImportError
), and second time the same code gives ImportError: cannot import name main
What is going on?
UPDATE:
My question is not about circular imports. I am confused by the fact that exactly the same code from scripts import main
first time works ok and then second time fails.
I guess there is some internal import mechanism which i don't understand.
First time a statement imports a module, second time exactly the same code tries to import a name from a module. How this works?
Upvotes: 2
Views: 4627
Reputation: 59604
This is a patch I use to override the default behavior:
import sys
import builtins
import importlib
def _import(name, globals=None, locals=None, fromlist=None, level=0, __import__=__import__):
"""A hack to to make names of imported modules to be available in the parent package before
they are fully imported. If a module is present in sys.modules event if it's not fully
imported, it should not be a problem.
I.e. ``from package import module1, module2`` will create variables ``package.module1`` and
``package.module2`` at start of the import, not when it's finished.
This helps in some situations with circular import.
"""
module = __import__(name, globals, locals, fromlist, level)
for attr in fromlist or ():
sub_module = sys.modules.get(module.__name__ + '.' + attr)
if sub_module: # is this a module?
# if subpackage is already being imported, even if not finished,
# inject its name into the parent package
setattr(module, attr, sub_module)
return module
builtins.__import__ = _import
Here is a project to test this: https://github.com/warvariuc/python_import_improver
Upvotes: 0
Reputation: 1778
This should work:
start.py:
from scripts import main
scripts/main.py:
import install
scripts/install.py:
import main
Upvotes: 0
Reputation: 414215
main
imports install
and install
imports main
:
from scripts import main
in start.py
leads tofrom . import install
in main.py
leads tofrom scripts import main
in install.py
-- circular importTo solve the circular import either combine main
and install
into a single module or create the 3rd module that both modules can import.
Upvotes: 3
Reputation: 1121864
You created a circular import. You can't do that. Avoid importing main
from install
.
What is happening is that a module, while being imported, is incomplete until the whole top level has been executed. If during that execution it imports another module that (directly or indirectly) tries to import the original module again, this will fail. The original module was not yet done importing yet.
In other words, you are creating a circular graph:
start -> scripts.main -> scripts.install
^ |
| |
----------------
You need to re-arrange your code to not need to import from main
from within your scripts
package.
See What are the “best practices” for using import in a module? for some tips on how to handle this.
Upvotes: 5