I'm having trouble importing modules from my pytest functions. I know there's a million questions on this, but I've read through a bunch, and I'm still having trouble understanding.
$ tree
.
└── code
├── eight_puzzle.py
├── missionaries_and_cannibals.py
├── node.py
├── search.py
└── test
├── test_eight_puzzle.py
└── test_search.py
2 directories, 6 files
$
$ grep import code/test/test_search.py
import sys
import pytest
import code.search
$
$ pytest
...
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
import code.search
E ModuleNotFoundError: No module named 'code.search'; 'code' is not a package
...
I expected that to work. 'code' is a package, right? A package in Python 3 is any directory with .py files in it.
I've also tried it with a relative import - from .. import search
- and I get the following.
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
from .. import search
E ImportError: attempted relative import with no known parent package
I've also tried modifying sys.path as shown here, specifying my PYTHONPATH, and adding init.py files in code and test.
Can I get this import to work without using something like setuptools? This is just for experimenting, so I'd rather not deal with the overhead.
It may also be important to note that I'm using conda, because it seems to work when I'm using the python 2 pip-installed version of pytest with init.py files.
pytest as a testing framework needs to import test modules and conftest.py files for execution.
Project StructureThe modules containing pytests should be named “test_*. py” or “*_test.py”. While the pytest discovery mechanism can find tests anywhere, pytests must be placed into separate directories from the product code packages. These directories may either be under the project root or under the Python package.
Pytest is a Python library for testing Python applications. It is an alternative to nose and unittest.
Some notes about directories without __init__.py
files first:
Although a directory without an __init__.py
is a valid import source in Python 3, it is not a regular package, rather being an implicit namespace package (see PEP 420 for the details). Among other properties, implicit namespace packages are second-class citizens when it comes to importing, meaning that when Python has two packages with the same name in sys.path
, one being a regular package and another being an implicit namespace package, the regular one will be preferred regardless what package comes first. Check it yourself:
$ mkdir -p implicit_namespace/mypkg
$ echo -e "def spam():\n print('spam from implicit namespace package')" > implicit_namespace/mypkg/mymod.py
$ mkdir -p regular/mypkg
$ touch regular/mypkg/__init__.py
$ echo -e "def spam():\n print('spam from regular package')" > regular/mypkg/mymod.py
$ PYTHONPATH=implicit_namespace:regular python3 -c "from mypkg.mymod import spam; spam()"
This will print spam from regular package
: although implicit_namespace
comes first in sys.path
, mypkg.mymod
from regular
is imported instead because regular/mypkg
is a regular package.
Now you know that since your package code
is an implicit namespace package, Python will prefer regular imports of code
to yours if it encounters one. Unfortunately for you, there is a module code
in the stdlib, so it's practically a "reverse name shadowing" problem: you have an import object with the same name as the one from stdlib, but instead of shadowing the stdlib import, it shadows yours.
You thus need to do two things in order to make your layout usable:
code
dir a unique name (let it be mycode
for this answer's example)sys.path
when running the tests from the project root dir because it's not in sys.path
per se. You have some possibilities:
conftest.py
file to the root dir (aside the mycode
dir). This will instruct pytest
to add the root dir to sys.path
(see here for an explanation). You can now just run pytest
as usual and the imports will be resolved;python -m pytest
- invoking interpreter directly adds the current dir to sys.path
;sys.path
via PYTHONPATH
env var, e.g. run PYTHONPATH=. pytest
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With