Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relative Import Confusion in Python

I am having some trouble figuring out how to do relative imports in Python. I am currently working on my first major project so I want to do it right using unit tests. However, I am having trouble with my file structure and relative imports.

Here is my current structure:

App/
  __init__.py
  src/
    __init__.py
    person.py
  tests/
    __init__.py
    person_tests.py

What I want to do is be able to import person.py into person_tests.py for the unit tests. I have attempted the following:

from . import person
from .. import person
from .App.src import person
from ..App.src import person
from ..src.person import *
from ..src import person
from .src import person

Every one of the above throws either a syntax error or

ValueError: Attempted relative import in non-package

Can someone please clarify this for me?

Edit: Python version is 2.7. Edit: I would like to be able to use this with say unittest or nose.

like image 925
firstofth300 Avatar asked Aug 05 '13 22:08

firstofth300


2 Answers

My guess (and I'll delete this if I'm wrong) is that you're trying to use person_tests.py as a top-level script, rather than as a module inside a package, by doing something like this:

$ cd App/tests
$ python person_tests.py

In that case, person_tests does not end up as App.tests.person_tests, but as just __main__ (or, with minor variations, as the top-level person_tests, which has the same basic issues). So, .. does not refer to App, and therefore there is no way to get to person as a relative import.

More generally, nothing on PYTHONPATH, including ., should ever be in the middle of any package directory, or things will get broken.

The right answer is to not do that. Do something like this:

$ python -m App.tests.person_tests

Or write a top-level (outside the package) script that imports the module, and run that top-level script.

like image 56
abarnert Avatar answered Sep 28 '22 21:09

abarnert


Just as abarnert says, you can not excute the script as main for the relative import based on the current name of the script. Quoted from the doc:

Note that both explicit and implicit relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application should always use absolute imports.

. represents the current package and .. represents the parent package. So some of your import statement is wrong.

from . import person # wrong for no person module in package tests
from .. import person # wrong for no person module in package app
from .App.src import person # wrong for no app package in package tests
from ..App.src import person # wrong for no app package in package app
from ..src.person import * # right
from ..src import person # right
from .src import person # wrong

You can refer PEP328 for the standard of relative import.

like image 25
zhangyangyu Avatar answered Sep 28 '22 20:09

zhangyangyu