[New-bugs-announce] [issue16031] relative import headaches

James Hutchison report at bugs.python.org
Tue Sep 25 03:07:47 CEST 2012

New submission from James Hutchison:

This might even be a bug I've stumbled upon but I'm listing it as an enhancement for now.

I really feel that relative imports in Python should just work. Regardless of the __name__, I should be able to import below me. Likewise, it should work even if I've already done an import into the symbol table. It adds additional work to us as a developer to have to do some pythonpath or code gymnastics to get something rather trivial working. Additionally, the import errors from circular imports add another challenge to work around. In C/C++ you can force it to import a file once and only once, why can't Python work the same way?

Take the following example set-up:

a's code:
print("in a");
from subModule1 import b

b's code:
print("in b");
from subModule1 import a

test_a.py's code:
from .. import a

startPoint.py is empty, and the __init__.py files are also empty.

If I run a PyDev unit test on test_a.py this is what I get:

Finding files... done.
Importing test modules ... myname: subModule1.tests.test_a
in a
in b
myname: test_a
Traceback (most recent call last):
  File "C:\eclipse\plugins\org.python.pydev_2.6.0.2012062818\pysrc\pydev_runfiles.py", line 432, in __get_module_from_str
    mod = __import__(modname)
  File "C:\myfolder/relativeImportTest/subModule1/tests\test_a.py", line 6, in <module>
    from .. import a
ValueError: Attempted relative import in non-package

Clearly in this case, the exception given doesn't make any sense. I have __init__.py, the error says the relative import line is failing, and the error says it's because I'm in a non-package. Except, I'm in a package. It seems to go through the a_test.py file twice, even though I never explicitly import it. The first time through, I'm clearly in a package. The second time through, my name is NOT __main__ but yet I'm apparently no longer a package, which is where it fails.

Now if I change:
"from subModule1 import b" to "import subModule1.b"
"from subModule1 import a" to "import subModule1.a"

then everything works. But then that means I have to reference everything by the full name in my submodules. In this example, there's clearly a circular reference between a and b that wouldn't work anyways.

So lets change some things.

import subModule1.b

from subModule1 import a

Now the circular reference is gone between a and b. I really don't like having to do this as a means to work around a circular reference because it forces me to vary the import style of one file to another.

If we try the test code again however, it gets the same problem. If I swap which file does the relative import, then it works.

So lets make one last change:

import subModule1.b # added
from .. import a

This will work, seemingly magically. It only runs the code in test_a.py once. Recall that the code in a.py is "import subModule1.b"

So basically this brings up several issues:
1. "import a.b" isn't the same as "from a import b" by more than how you reference it in the code
2. submodules are re-imported as non-module without ever importing them if you import their parent module relatively. If this is documented I don't know where.
3. import order can matter drastically to if a code runs or not for seemingly magical reasons.

And back when I was a beginner Python user, the naming convention of the full path really threw a monkey wrench in my code when I would try to move a select number of files from one project to another, or would try relative imports. If relative imports cause such headaches with circular references then I should generally stick to the full module path when referencing things. But if the full module path isn't portable then I should use relative imports.

Likewise, if I run as a PyDev unitTest, my module name is NOT __main__, so special path checks for __main__ won't work

I think the bottom line is that the import system gave me headaches as a beginner user and as an advanced user it still does every now and then so it really should be changed to something more intuitive or forgiving. I really shouldn't have to sit and think "how do I reference a function in the file just one directory level below mine?"

If there is already some magic bullet for this then it should probably be more visible.

components: Interpreter Core
messages: 171208
nosy: Jimbofbx
priority: normal
severity: normal
status: open
title: relative import headaches
type: enhancement
versions: Python 3.2

Python tracker <report at bugs.python.org>

More information about the New-bugs-announce mailing list