I have a Python project with the following structure:
testapp/
├── __init__.py
├── api
│ ├── __init__.py
│ └── utils.py
└── utils.py
testapp/api/__init__.py
from testapp import utils
print "a", utils
from testapp.api.utils import x
print "b", utils
testapp/api/utils.py
x
x = 1
testapp.api
$ export PYTHONPATH=$PYTHONPATH:.
$ python -c "import testapp.api"
a <module 'testapp.utils' from 'testapp/utils.pyc'>
b <module 'testapp.api.utils' from 'testapp/api/utils.pyc'>
import
utils
The from form does not bind the module name: it goes through the list
of identifiers, looks each one of them up in the module found in step
(1), and binds the name in the local namespace to the object thus
found.
from ... import ...
>>> from os.path import abspath
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
testapp.api.utils
testapp.utils
From the import system documentation:
When a submodule is loaded using any mechanism (e.g.
importlib
APIs, theimport
orimport-from
statements, or built-in__import__()
) a binding is placed in the parent module’s namespace to the submodule object. For example, if packagespam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule. Let’s say you have the following directory structure:spam/ __init__.py foo.py bar.py
and
spam/__init__.py
has the following lines in it:from .foo import Foo from .bar import Bar
then executing the following puts a name binding to
foo
andbar
in thespam
module:>>> import spam >>> spam.foo <module 'spam.foo' from '/tmp/imports/spam/foo.py'> >>> spam.bar <module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Given Python’s familiar name binding rules this might seem surprising, but it’s actually a fundamental feature of the import system. The invariant holding is that if you have
sys.modules['spam']
andsys.modules['spam.foo']
(as you would after the above import), the latter must appear as thefoo
attribute of the former.
If you do from testapp.api.utils import x
, the import statement will not load utils
into the local namespace. However, the import machinery will load utils
into the testapp.api
namespace, to make further imports work right. It just happens that in your case, testapp.api
is also the local namespace, so you're getting a surprise.