I have a strange (for me) problem with python imports and I would like to understand what is wrong with it.
I have created a demo project with the following directory structure:
├── entry.py
└── foo
├── __init__.py
└── bar
├── __init__.py
├── sdk.py
├── service.py
└── utils.py
If I run python entry.py
, in service.py
the following statement works:
from foo.bar import utils
while this does not:
import foo.bar.utils as utils
# entry.py:
from foo.bar.sdk import get_sdk
print(get_sdk())
# foo - __init__.py
# empty on purpose
# bar - __init__.py
from .service import Service
# sdk.py
def get_sdk():
return "SDK"
# service.py
import foo.bar.utils as utils # wrong import
# from foo.bar import utils # works!
class Service:
def do_service(self):
util = utils.get_utility_string()
print("Doing service call {}".format(util))
# utils.py
def get_utility_string():
return "This is useful"
By running python entry.py
I get:
Traceback (most recent call last):
File "entry.py", line 1, in <module>
from foo.bar.sdk import get_sdk
File "/Users/paloand/Downloads/p_demo/foo/bar/__init__.py", line 1, in <module>
from .service import Service
File "/Users/paloand/Downloads/p_demo/foo/bar/service.py", line 1, in <module>
import foo.bar.utils as utils
AttributeError: module 'foo' has no attribute 'bar'
From my understanding in entry.py
when the get_sdk
symbol is getting imoprted from sdk
module, foo
package is being imported as well. While importing foo
in bar
the __init__.py
file requests the import of the Service
symbol. So while foo
is being imported it is required again in service.py
.
Link to the uploaded demo project in my Dropbox: Demo
I have read this question and all the answer but there I could not extract any information there. I also found this bug but I am not sure if it applies here. I read also the common traps of import in python but could not find this one.
I want to understand the difference and what is really happening.
I am using python 3.6
Thank you
EDIT
Updated with error message
EDIT 2
I have tried python -vv entry.py
in both cases. The relevant logs are in the paste: https://pastebin.com/J9guMqJS.
In both cases the import order is the same, in the "bad" case this import fails:
import 'foo.bar.utils' # <_frozen_importlib_external.SourceFileLoader object at 0x1103ddf28>