warvariuc
warvariuc

Reputation: 59604

Import error - what is going on?

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

Answers (4)

warvariuc
warvariuc

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

Arseniy
Arseniy

Reputation: 1778

This should work:

start.py:

from scripts import main

scripts/main.py:

import install

scripts/install.py:

import main

Upvotes: 0

jfs
jfs

Reputation: 414215

main imports install and install imports main:

  • from scripts import main in start.py leads to
  • from . import install in main.py leads to
  • from scripts import main in install.py -- circular import

To 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

Martijn Pieters
Martijn Pieters

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

Related Questions