I'm puzzled by how circular imports are handled in Python. I've tried to distill a minimal question and I don't think this exact variant was asked before. Basically, I'm seeing a difference between
import lib.foo
and
import lib.foo as f
when I have a circular dependency between lib.foo
and lib.bar
. I had expected that both would work the same: the (possibly half-initialized) module would be found in sys.modules
and put into the local namespace. (From testing I noticed that import lib.foo
really puts lib
into the local namespace — okay, with that syntax I'll do lib.foo.something
anyway.)
However, if lib.foo
is already in sys.modules
, then import lib.foo as f
tries to access foo
as an attribute on lib
and raises AttributeError. Why does the behavior (seemingly) depend on the presence in sys.modules
?
Also, where is this behavior documented? I don't feel that the Python import
statement reference explains this behavior, or at least I couldn't extract it :-)
All in all I'm trying to change a code base to use the oft recommended style where you import modules, not symbols in the modules:
from project.package import moduleA
from project.package import moduleB
But that fails when there are circular imports between the two modules. I had expected it to work as long as the top-level definitions in the two modules don't depend on each other (e.g., no subclass in moduleB
with a base class in moduleA
).
Test script:
#!/bin/sh
rm -r lib; mkdir lib
touch lib/__init__.py
cat > lib/foo.py <<EOF
# lib.foo module
print '{ foo'
#import lib.bar # works
import lib.bar as b # also works
#from lib import bar # also works
print 'foo }'
EOF
cat > lib/bar.py <<EOF
# lib.bar module
print '{ bar'
#import lib.foo # works
import lib.foo as f # AttributeError: 'module' object has no attribute 'foo'
#from lib import foo # ImportError: cannot import name foo
print 'bar }'
EOF
python -c 'import lib.foo'
When you say import lib.foo as f
what you are telling Python to do is the equivalent of import lib.foo; f = lib.foo
at the bytecode level. You end up with an AttributeError in the issue being asked because lib
in this case doesn't have foo
set yet as an attribute. Python hasn't completed its import of lib.foo
when it tries to do the assignment and thus has not set the attribute yet on lib
; look at the Python 3.3 source for import where you can see where a module is loaded vs. couple statements farther down where a module is set on its parent.
This is where you end up with some circular import issues. You need to let the import for lib.foo
complete before you try to access lib.foo
, else the attribute on lib
simply won't exist yet for the bytecode to access. This might be why you think you are not using any top-level definitions directly in your code but in actuality you are through your import statements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With