Question

Possible Duplicate:
Cyclic module dependencies and relative imports in Python

Consider the following example of cyclic imports in python:

main.py:

from pkg import foo

pkg/__init.py__:

# empty

pkg/foo.py:

from pkg import bar

pkg/bar.py:

from pkg import foo

Running main.py will cause an exception:

Traceback (most recent call last):
  File "/path/to/main.py", line 1, in <module>
    from pkg import foo
  File "/path/to/pkg/foo.py", line 1, in <module>
    from pkg import bar
  File "/path/to/pkg/bar.py", line 1, in <module>
    from pkg import foo
ImportError: cannot import name foo

Changing bar.py to:

# from pkg import foo
import pkg.foo 

will make the example work.

Why does this happen? Shouldn't *import package.module" and "from package import module" be equivalent (except for the extra name binding in the latter)?

Was it helpful?

Solution

First of all you have to understand that:

  • The sys.modules variable play the role of a cache in the import mechanize which mean that if we import a module the first time, an entry containing the name of this module is added to sys.modules, so that when we try to import this same module next time, we will only get the already cached module from sys.modules and we will not execute the module again.

  • The difference between import pkg.foo and from pkg import foo is that the second is equivalent too: import pkg.foo followed by getattr(pkg, 'foo'). (check my answer here for more detail)

Now what happen in your first example is the following:

1- In main.py: we start by executing the line from pkg import foo, so first the entry pkg.foo is added to sys.modules (i.e. 'pkg.foo' in sys.modules == True) than we try to import foo.

2- In foo.py: While importing foo we end up executing this line: from pkg import bar and again the entry pkg.bar is added to sys.modules; and than we start importing bar, what is important to note here is that we are still executing foo.py, so we are kind off still executing this line from pkg import foo from main.py.

3- In bar.py: while now importing bar.py we end up executing the line: from pkg import foo, but remember we have already an entry in sys.modules for pkg.foo which mean that executing foo.py will be skipped and the import mechanize will get us instead the sys.modules['pkg.foo'] entry, and now we will excute getattr(pkg, 'foo') as explained above, but remember 'pkg.foo' didn't finish importing; which mean that there is no attribute foo which explain the error.

Now if you put in bar.py only import pkg.foo this will not have the line that cause us the problem last time which is: getattr(pkg, 'foo').

HTH,

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top