After a few hours of isolating a bug, I have come up with the following MCVE example to demonstrate the problem I've had:
a.py:
from b import get_foo_indirectly
class Foo:
pass
if __name__ == '__main__':
print("Indirect:", isinstance(get_foo_indirectly(), Foo))
print("Direct:", isinstance(Foo(), Foo))
b.py:
def get_foo_indirectly():
from a import Foo
return Foo()
The expected output of a.py is:
Indirect: True
Direct: True
The actual output is:
Indirect: False
Direct: True
Moreover, if I create a separate module c.py, the output is as expected:
from a import Foo
from b import get_foo_indirectly
if __name__ == '__main__':
print("Indirect:", isinstance(get_foo_indirectly(), Foo))
print("Direct:", isinstance(Foo(), Foo))
Clearly, the interaction between isinstance
and the import machinery is not behaving quite like I expected it to. It seems like the use of circular imports has bitten me hard. Why? Is this Python's expected behavior?
Note that this is very oversimplified of the actual context in which I encountered this behavior; modules a and b were both large modules, and b was separated because it had a distinct purpose from a. Now that I've seen the consequences of circular imports, I will probably combine them, perhaps relegating some of the long-winded behavior in b.