title: Are python circular imports an implementation detail?
body: <p>Is the behaviour of <em>circular imports</em> well-specified in Python, or is it just implementation-specific?</p>
  
  <p>For example in cpython, importing a submodule causes the submodule to be assigned as an attribute of the parent module, <em>but not until after</em> the submodule finishes executing. Is this stated anywhere in the docs or PEPs?</p>
  
  <p>More often then not, python circular imports just work (assisted by conventions like not executing any functions during import, and laying out the source code to define functions in the reverse of expected call order), but a consequence of the above behaviour is that you cannot refer by parent to a partially imported module. For example, if importing <code>package.submodule</code> causes an import of <code>othermodule</code>, then <code>othermodule</code> cannot do <code>import package.submodule as submod</code> but <em>can</em> do <code>from package import submodule as submod</code>. Is this explained/specified by the documentation? If not, is it likely to change?</p>
  
  <p>In this case, the discrepancy is because <code>import package.submodule as submod</code> is implemented as a call to <code>__import__</code> (that returns <code>package</code>) followed by an attribute lookup (to retrieve <code>submodule</code> so that it can be assigned to <code>submod</code>). In contrast, <code>from package import submodule as submod</code> causes <code>__import__</code> to directly return <code>submodule</code> (which seems to work even if <code>submodule</code> is still only half-complete).  You could figure some of this out if you delve through the cpython bytecode (documented in the <code>dis</code> section of the standard library) and study the <code>__import__</code> semantics. However, since resolving circular imports is a common issue in python, it would be useful to find an official high-level summary of what to expect?</p>
  
  <p>Example:</p>
  
  <pre class="lang-py prettyprint-override"><code>mkdir package
  touch package/__init__.py
  cat &gt; package/submodule.py &lt;&lt; EOF
  def f():
    print('OK')
  import othermodule
  othermodule.g()
  def notyetdefined():
    pass
  EOF
  cat &gt; othermodule.py &lt;&lt; EOF
  def g():
    try:
      import package.submodule as submod # this fails
    except AttributeError:
      print('FAIL')
    from package import submodule as submod # this works ok
    submod.f()
    #submod.notyetdefined() # this cannot be invoked
  EOF
  python -c 'import package.submodule'
  </code></pre>
  
  <p>Output in python 3.6.7:</p>
  
  <pre><code>FAIL
  OK
  </code></pre>