3

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 in b.py should result in an empty module being imported, that only will get filled after module b is fully imported, which is fine.
  • What is the correct way to solve this? I need to refer to a in the module scope of b, 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.
Claude
  • 8,806
  • 4
  • 41
  • 56
  • The first question is answered by the (very elaborate!) answer to this question: http://stackoverflow.com/questions/6351805/cyclic-module-dependencies-and-relative-imports-in-python – Claude Dec 15 '15 at 09:15
  • It seems that python 3.5 is more allowing for this kind of circular import, and since PEP-0484 is a python 3.5 thing, this question may be less valid. Having said that, there is still a strong advantage of using pep-0484-style annotations in 3.2 <= python < 3.5, so that re-valids the question. – Claude Dec 15 '15 at 12:35
  • See https://docs.python.org/3/whatsnew/3.5.html#other-language-changes : Circular imports involving relative imports are now supported. (Contributed by Brett Cannon and Antoine Pitrou in issue 17636.) – Claude Dec 16 '15 at 11:37

0 Answers0