2

I created with virtualenv a Foo project and I've some problem with importing the File module in test_file.py

This is my project directory

Foo/
├── app
│   ├── __init__.py
│   ├── testfile.py
│   ├── tests
│   │   ├── __init__.py
│   │   └── test_gram.py
│   ├── util
│   │   ├── File.py
│   │   ├── Gram.py
│   │   ├── __init__.py
└── NOTES

test_gram.py:

from app.util.File import loadDataFromPickle

listofdict = loadDataFromPickle(".....")
i = 0
for item in listofdict[:50]:
    print(item)

If I run test_gram, I am getting a ImportError:

Traceback (most recent call last):
  File "test_gram.py", line 1, in <module>
    from app.util.File import loadDataFromPickle
ImportError: No module named 'app

What am I doing wrong? Do I need to change the virtualenv-path to Foo/app instead of Foo?

Eric
  • 59
  • 1
  • 8
  • 1
    Python imports are now absolute by default and your app directory is not in `sys.path`. Try `from ..app.util.File import loadDataFromPickle` – dhke Apr 22 '15 at 18:36
  • I think you should move the `test_gram.py` to Foo/ (not too sure) – Ronnie Apr 22 '15 at 18:37
  • If I put test_gram.py in app and i do from util.File import ............... It works, but it is cleaner to put tests in a tests directory. – Eric Apr 22 '15 at 18:51
  • @dhke What do you mean by from ..app.util.File import loadDataFromPickle – Eric Apr 22 '15 at 19:20

4 Answers4

3

If test_gram.py is in the tests folder, then the import line should be:

from ..util.File import loadDataFromPickle$

Another option is to use the imp module, which is usually suggested instead of appending the sys.path (source here, including Python 3 version)

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Community
  • 1
  • 1
philshem
  • 24,761
  • 8
  • 61
  • 127
2
from app.util.File import loadDataFromPickle

is an absolute import, meaning

  1. go to sys.path
  2. from any of the paths listed there
  3. find the first one that has an app module.
  4. Then import the util module from that app module.
  5. ...

The tricky bit is the sys.path, which is documented as

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

So if you run Foo/app/tests/test_gram.py, sys.path starts with .../Foo/app/tests/. There's no app module anywhere below that directory, so you cannot import app using an absolute import (unless there's an app somewhere else on some sys.path).

As suggested in the comments and other answers, it is good practice to use relative imports in such situations:

from ..util.File import loadDataFromPickle

Relative imports are relative to the current package/module, not to the directories in sys.path.

Edit:

However, relative imports will not work when running the script directly from the command line, as python will complain that the '' module has not been imported (Parent module '' not loaded, cannot perform relative importSystemError: Parent module ''). Well, that's because the parent modules (tests and '') are not loaded when running the script directly and the importer rightly assumes they should be.

One trick is to run the test script as a module:

python -m app.tests.test_gram.py

This will most likely require some changes to the test script, at least having

if __name__ == '__main__':
    [...]

inside the script. See also Relative imports in Python 3 for more details.

As a suggestion, you might want to convert the test script to using unittest, anyway.

Community
  • 1
  • 1
dhke
  • 15,008
  • 2
  • 39
  • 56
  • Thanks for info. The solution of appending the app dir to the sys.path suggested by a.j works in this particular, but it seems not useful if there are a lot of testfiles. So I tried to import module with the relative path like @dhke suggested. But I Get the following error: SystemError: Parent module '' not loaded, cannot perform relative importSystemError: Parent module '' not loaded, cannot perform relative import I'm just experimenting to look for a useful hierarchy – Eric Apr 23 '15 at 14:04
  • 1
    See here http://stackoverflow.com/questions/16981921/relative-imports-in-python-3 – dhke Apr 23 '15 at 14:06
  • 1
    Better than appending the sys.path is to use the `imp` module - see [here](http://stackoverflow.com/a/67692/2327328) – philshem Apr 24 '15 at 08:22
  • My intention is to start with ttd&unittest, but because python is pretty new for me, I wanted to get a good hierarchy first. Now I'm looking for the best way to test modules (importing: I've read that some people uses imp to add the 'modules' path. Others says you need to export the 'module dir' in PYTHONPATH – Eric Apr 24 '15 at 17:59
0

You are in test_gram.py which is in a child folder of app. Thus, when you try to import app, it looks for app in tests, which it isn't there:

import sys
sys.path.append('/Foo/app/util')

from File import loadDataFromPickle
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
  • Better than appending the sys.path is to use the `imp` module - see [here](http://stackoverflow.com/a/67692/2327328) – philshem Apr 24 '15 at 08:22
0

In this case I just needed to run the test (Foo/app/tests/test_gram.py) from toplevel directory and not from the test directory like I did.

Eric
  • 59
  • 1
  • 8