4

I've been getting an error I don't understand and I managed to boil it down to this. Here is the directory structure:

temp
  temp2
    __init__.py
    mod.py
    mod2.py

Here are the contents of the files:

#__init__.py
from temp.temp2.mod import *
from temp.temp2.mod2 import *

#mod.py
def f():
  pass

#mod2.py
import temp.temp2.mod as mod

Then I open a new python 3.6.8 console in ipython and try to load mod2 and i get this:

>>>import temp.temp2.mod2
Traceback (most recent call last):

  File "<ipython-input-1-...>", line 1, in <module>
    import temp.temp2.mod2

  File ".../temp/temp2/__init__.py", line 2, in <module>
    from temp.temp2.mod2 import *

  File ".../temp/temp2/mod2.py", line 1, in <module>
    import temp.temp2.mod as mod
AttributeError: module 'temp' has no attribute 'temp2'

Some other facts.

First, if I change the file mod2.py to either import temp.temp2.mod or from temp.temp2.mod import f then it works fine with no error. It is only the import as that results in an error.

Second, if I don't have the nested modules, and just put mod.py, mod2.py and __init__.py inside of temp (and change the imports accordingly), then I also don't get any error.

Any idea what is going on?

On Edit:

Attribute not added to module after importing gives a partial explanation as to what is happening. The reason temp has no attribute temp2 is that the package temp.temp2 is still loading at this time, and the attribute temp2 in the package temp is not set until after this load is complete. But it still doesn't answer the question as to why import and import as cause different behaviors.

To reiterate, this is the only mod2.py file that causes the error:

#mod2.py  -- results in error
import temp.temp2.mod as mod

These all work fine:

#mod2.py  -- works OK
import temp.temp2.mod

#mod2.py  -- works OK
from temp.temp2.mod import f

#mod2.py  -- works OK
from temp.temp2 import mod

#mod2.py  -- works OK
from temp.temp2 import mod as mod

So, at least to me, the fact that the attribute temp2 in the package temp doesn't get set until after temp.temp2 is loaded doesn't explain why these four versions of "mod2.py" work while the one above gives an error.

Obviously, I have workarounds I can use, but I want to know what is going on behind the scenes that is causing this, preferably if it is documented. I've tried to find some documentation explaining details of module loading that would explain this behavior, but I haven't found the answer.

wim
  • 338,267
  • 99
  • 616
  • 750
mrip
  • 14,913
  • 4
  • 40
  • 58
  • If you put an empty `__init__.py` directly under the `temp` folder, then retry, what happens? – C.Nivs Apr 09 '19 at 15:52
  • Then it works fine. It also works fine if i comment out `#from temp.temp2.mod2 import *` in `__init__.py` – mrip Apr 09 '19 at 15:56
  • `import temp.whatever` should *never* work if your `temp` is a directory that doesn't have a `__init.py__` in it. The errors aren't surprising; that you ever *fail* to get them is. – Charles Duffy Apr 09 '19 at 17:12
  • Putting an `__init__.py` file in `temp` does not change the behavior at all -- the error still occurs. – mrip Apr 09 '19 at 18:00
  • @CharlesDuffy the import *somehow* works. From `sys.modules` after `import temp.temp2.mod`: `{'temp': , 'temp.temp2': , 'temp.temp2.mod': , 'temp.temp2.mod2': }` – C.Nivs Apr 09 '19 at 19:22
  • The behavior is indeed baffling, but this all boils down to the `as`. If you `import temp.temp2.mod`, you will see that `temp` is imported (not `mod`) and running `dir(temp)` shows that there *is* a `temp2` attribute – C.Nivs Apr 09 '19 at 19:23
  • @CharlesDuffy: Modern Python has “namespace packages” with no `__init__.py` that can be assembled from multiple `sys.path` entries. – Davis Herring Apr 09 '19 at 23:59
  • @DavisHerring: thanks for the link. See the edit. That is a partial explanation, but IMO it doesn't explain why some of the versions work and others don't. – mrip Apr 10 '19 at 13:55
  • @CharlesDuffy Wrong. This is making an [implicit namespace package](https://www.python.org/dev/peps/pep-0420/). – wim Apr 10 '19 at 15:51
  • @mrip From which directory are you entering IPython? Is there any good reason you are using an implicit namespace package, or you just didn't know how to structure a normal package properly? It looks like `temp` should be your project root, and `temp2` is the package root, therefore the import statements should not be like `temp.temp2.mod` but rather like `temp2.mod`. – wim Apr 10 '19 at 15:57
  • 2
    Also note this issue has been fixed in [Python 3.7](https://bugs.python.org/issue30024). – a_guest Apr 10 '19 at 15:58
  • @a_guest good find. – wim Apr 10 '19 at 16:03

0 Answers0