I'm trying to figure out a (and the) way to correctly work with circular imports in python (3.4) packages. I fully understand that circular imports usually indicate a not-clean separation of code into modules, however annotating/type hinting with PEP-0484 will result in lots of circular imports in (I think) completely reasonable code. In this case also the usually suggested solution of putting imports in functions or at the bottom of a module, doesn't work.
Simple case (no package, no circular import):
[a.py]
import b
class A:
def __init__(self):
self.b = b.B(self)
def get_b():
return self.b
[b.py]
class B:
def __init__(self, parent):
self.parent = parent
[main.py]
import a
print(a.A())
Add PEP-0484 (no package, circular import, needs forward reference):
[a.py]
import b
class A:
def __init__(self):
self.b = b.B(self)
def get_b() -> b.B:
return self.b
[b.py]
import a
class B:
def __init__(self, parent: "a.A"):
self.parent = parent
[main.py]
import a
print(a.A())
So far, so good. However now I want to make this a package, and I can't get this to work.
[pack/__init__.py] (empty)
[pack/a.py]
from . import b
class A:
def __init__(self):
self.b = b.B(self)
def get_b() -> b.B:
return self.b
[pack/b.py]
from . import a
class B:
def __init__(self, parent: "a.A"):
self.parent = parent
[main.py]
from model import a
print(a.A())
The result for running python main.py
is
Traceback (most recent call last):
File "main.py", line 1, in <module>
from model import a
File "/private/tmp/circ/model/a.py", line 1, in <module>
from . import b
File "/private/tmp/circ/model/b.py", line 1, in <module>
from . import a
ImportError: cannot import name 'a'
My questions:
- I cannot understand why this fails with this error. As far as I understand from reading the import internals, the
from . import a
line inb.py
should result in an empty module being imported, that only will get filled after moduleb
is fully imported, which is fine. - What is the correct way to solve this? I need to refer to
a
in the module scope ofb
, and vice versa. No matter what combination of relative and absolute paths I try, importing both modules from the__init__.py
file or even removing that file all together, I can't seem to make this run.