1

This may be python's desired behavior, but I find it puzzling nonetheless -

I have the following file structure:

repo/
    foo/
        __init__.py
        foo/
            bar.py

where __init__.py is the following:

from .foo import bar

When I enter a python interpreter from within repo/, I am able to import bar like so:

>>> from foo import bar
>>> bar
<module 'foo.foo.bar' from '.../repo/foo/foo/bar.py'>

Great! However, the following does not work:

>>> import foo.bar as foobar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'foo.bar'

Perhaps I lack an understanding of how python's import statement works, but I would very much like to be able import as I showed above. If this is desired behavior, does anyone know of a workaround? Perhaps I can change something in __init__.py?

Out of concern for naming caveats, I have also tested this with the following file structure:

repo/
    foo/
        __init__.py
        baz/
            bar.py

... and I got the same result

I've tested this in python 3.6.2 and 3.6.6 on linux and macOS. I also saw the same behavior in python 2.7.13 (where, of course, I added an __init__.py into the lower foo/ directory.

If I am unable to make this style of import work, repo/ will have to undergo a bit of refactoring. Help me prevent this!

Battery_Al
  • 779
  • 1
  • 5
  • 20
  • Don't you mean `import foo.foo.bar as foobar` as you have 2 folders called 'foo' – N Chauhan Aug 10 '18 at 19:57
  • 1
    I was hoping that my `__init__.py` in the higher `foo/` would enable me to omit the second foo in my import statement. – Battery_Al Aug 10 '18 at 20:01
  • 2
    Yea, that name has to be the name of a module. There is no module with the name `foo.bar`, although the `foo` module has an attribute `bar` which is a reference to the module named `foo.foo.bar`. how about `from foo import bar as foobar`? – juanpa.arrivillaga Aug 10 '18 at 20:02

1 Answers1

2

The actual name of the module you want to import is foo.foo.bar. Your from .foo import bar in __init__.py doesn't change that.

from foo import bar will give you the bar attribute of the foo module object, and that actually is the module you want, thanks to that line in your __init__.py. import foo.bar needs an actual foo.bar module, though. If it does not find an entry for that name in sys.modules, it will throw an error. Do not be tempted to mess with sys.modules to "fix" this; that leads to really confusing double-import bugs.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Oof, I guess I'll let it go. Though this feels somewhat inconsistent to me - since `.` is typically used to access attributes in python, I would expect it to do the same in an import statement. What you're saying is that the "spam.eggs" in `import spam.eggs` is not accessing the attribute `eggs` of package `spam`, but rather, it is simply the name of the module? – Battery_Al Aug 10 '18 at 20:34