Experiment trying to avoid Python circular dependencies
Question: I have a testing environment to try to understand how python circular
dependencies can be avoided importing the modules with an `import x`
statement, instead of using a `from x import y`:

    
    
    test/
        __init__.py
            testing.py
        a/
            __init__.py
            m_a.py
        b/
            __init__.py
            m_b.py
    

The files have the following content:

**testing.py:**

    
    
    from a.m_a import A
    

**m_a.py:**

    
    
    import b.m_b
    print b.m_b
    class A:
        pass
    

**m_b.py:**

    
    
    import a.m_a
    print a.m_a
    class B:
        pass
    

There is a situation which I can't understand:

If I remove the print statements from modules `m_a.py` and `m_b.py` or only
from `m_b.py` this works OK, but if the print is present at `m_b.py`, then the
following error is thrown:

    
    
    File "testing.py", line 1, in <module>
      from a.m_a import A
    File "/home/enric/test/a/m_a.py", line 1, in <module>
      import b.m_b
    File "/home/enric/test/b/m_b.py", line 3, in <module>
      print a.m_a
    AttributeError: 'module' object has no attribute 'm_a'
    

Do you have any ideas?


Answer: It only "works" with the print statements removed because you're not actually
doing anything that depends on the imports. It's still a broken circular
import.

Either run this in the debugger, or add a `print` statement after each line,
and you'll see what happens:

  * testing.py: `from a.m_a import A`
  * a.m_a: `import b.m_b`
  * b.m_b: `import a.m_a`
  * b.m_b: `print a.m_a`

It's clearly trying to access `a.m_a` before the module finished importing.
(In fact, you can see the rest of `a.m_a` on the stack in your backtrace.)

If you dump out `sys.modules` at this point, you'll find two partial modules
named `a` and `a.m_a`, but if you `dir(a)`, there's no `m_a` there yet.

As far as I can tell, the fact that `m_a` doesn't get added to `a` until
`m_a.py` finishes evaluating is not documented anywhere in the Python 2.7
documentation. (3.x has much a more complete specification of the import
process—but it's also a very different import process.) So, you can't rely on
this either failing _or_ succeeding; either one is perfectly legal for an
implementation. (But it happens to fail in at least CPython and PyPy…)

* * *

More generally, using `import foo` instead of `from foo import bar` doesn't
magically solve all circular-import problems. It just solves one particular
class of circular-import problems (or, rather, makes that class moot). (I
realize there is some misleading text in [the
FAQ](http://docs.python.org/2/faq/programming.html#what-are-the-best-
practices-for-using-import-in-a-module) about this.)

* * *

There are various tricks to work around circular imports while still letting
you have circular top-level dependencies. But really, it's almost always
simpler to get rid of the circular top-level dependencies.

In this toy case, there's really no reason for `a.m_a` to depend on `b.m_b` at
all. If you need some that prints out `a.m_a`, there are better ways to get it
than from a completely independent package!

In real-life code, there probably is some stuff in `m_a` that `m_b` needs and
vice-versa. But usually, you can separate it out into two levels: stuff in
`m_a` that needs `m_b`, and stuff in `m_a` that's needed by `m_b`. So, just
split it into two modules. It's really the same thing as the common fix for a
bunch of modules that try to reach back up and `import main`: split a `utils`
off `main`.

What if there really is something that `m_b` needs from `m_a`, that also needs
`m_b`? Well, in that case, you may have to insert a level of indirection. For
example, maybe you can pass the thing-from-`m_b` into the
function/constructor/whatever from `m_a`, so it can access it as a local
parameter value instead of as a global. (It's hard to be more specific without
a more specific problem.)

If worst comes to worst, and you can't remove the import via indirection, you
have to move the import out of the way. That may again mean doing an import
inside a function call, etc. (as explained in the FAQ immediately after the
paragraph that set you off), or just moving some code above the import, or all
kinds of other possibilities. But consider these last-ditch solutions to
something which just can't be designed cleanly, not a roadmap to follow for
your designs.

