开发者

Understanding A Chain of Imports in Python

I know there are several similar questions, but I'm struggling to understand the error I'm getting and browsing the docs and similar questions hasn't helped yet. If anything, the similar questions make me feel like what I'm doing is right.

I have the following files:

src/main.py

from pack import pack

if __name__ == '__main__':
    pack.exec("Hello Universe!")

src/pack/pack.py

import util

def exec(text):
    util.write(text)

if __name__ == '__main__':
    exec("Hello World!")

src/pack/util.py

def write(text):
    print(text)

*src/pack/_init_.py*

EMPTY FILE

When I run python pack.py from the src/pack directory, it works (prints "Hello World!"). However when I run python main.py from the src directory I get the following exception:

Traceback (most recent call last):
  File ".../src/main.py", line 1, in <module>
    from pack import pack
  File ".../src/pack/pack.py", line 1, in <module>
    import util
ImportError: No module named util

If I change the import line in pack.py to from . import util as suggested, effectively the opposite occours. main.py runs successfully, however now pack.py fails, raising:

Traceback (most recent call last):
  File ".../src/pack/pack.py", line 1, in <module>
    from . import util
ValueError: Attempted relative import in non-package

I would hav开发者_JS百科e thought that imports are relative to the current location, and as such you ought to be able to construct a chain of imports like this. It seems very odd to me that module is supposed to import a sibling file differently depending on where the program starts.

Can someone explain why this error occurs in one way but not the other, and if there is some way to allow this file structure to run whether I want to run from main.py or pack.py?


You will have trouble making the import work in both cases. This is because in one case you are running pack.py as the main file and in another you run it as part of a package.

When you run it as a standalone script python pack.py, the "pack" directory gets added to the PYTHONPATH, meaning that you can import any module in it. Hence, import util will work.

When you run python main.py you add the src directory to your PYTHONPATH. This means that any module or package in src, for example the pack directory, now becomes importable. Hence from pack import pack. However, to access util.py you now need to do from pack import util. You can also do from . import util from within pack.py, as you noticed.

But you can't really do both at the same time. Either src/ is the main directory or src/pack is.

The obvious, but wrong solution, is to let the main.py add the src/pack directory to the PYTHONPATH. That will work, but it's not a good idea. The correct way to do this is to make up your mind. Is src/pack a module that should be imported via import pack or is it just a folder with a bunch of Python scripts? Decide! :-)

I think in this case its' obvious that src/pack should be a module. So then treat it like a module, and make sure it's available like a module. Then you can from pack import util even when running pack.py as a main script.

How do you do that? Well, basically you either install the pack module in your site-packages, or you add the src directory to the PYTHONPATH. That last one is what you want during development. You can either do it manually with export PYTHONPATH=<path> or you can let your testrunners do it for you. You don't have a testrunner? Well you should, but that's another question. :)

For installing it permanently once you don't do development anymore, take a look at Distribute. It includes a testrunner. ;)


Your link is to python 2.7 documentation, but it looks like you are using Python 3.x.

See here: http://docs.python.org/py3k/ for the correct docs.

Python 3 removes implicit relative import. That is, you cannot import packages within the same module in the same way anymore.

You need to use from . import util, this is an explicitly relative import and is allowed. import X no longer checks the current directory. Instead it only check the entries in sys.path. This includes the directory of the script was started and python's standard library.


You are missing __init__.py in your pack directory.

Create an empty file called __init__.py in your pack directory.


Nothing in the correct documentation supports your theory that this form of importing should work.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜