Layout and importing for pytest in python3

可紊 提交于 2019-12-05 03:55:52

Some notes about directories without __init__.py files first:

Implicit namespace packages

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:

  1. give the code dir a unique name (let it be mycode for this answer's example)
  2. after that, you still need to fix the sys.path when running the tests from the project root dir because it's not in sys.path per se. You have some possibilities:
    • add an empty 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;
    • run the tests via python -m pytest - invoking interpreter directly adds the current dir to sys.path;
    • add the current dir to sys.path via PYTHONPATH env var, e.g. run PYTHONPATH=. pytest.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!