I clone this toy repository that demonstrates how namespace packages work:

C:\workspace>git clone https://github.com/pypa/sample-namespace-packages.git

Specifically, I'll use its pkg_resources directory, which has the structure

pkg_a/
  setup.py
  example_pkg/
    __init__.py
    a/
      __init__.py
pkg_b/
  setup.py
  example_pkg/
    __init__.py
    b/
      __init__.py

The example_pkg package is a pkg_resources-style namespace package (explained here).

I set up my python environment:

C:\workspace>\Python35\python.exe -m venv localpython

C:\workspace>localpython\Scripts\activate.bat
(localpython) C:\workspace>python -m pip install --upgrade pip setuptools

I install pkg_a from the toy repository:

(localpython) C:\workspace>python -m pip install c:\workspace\sample-namespace-packages\pkg_resources\pkg_a

I put pkg_b from the toy repository on my PYTHONPATH:

(localpython) C:\workspace>set PYTHONPATH=c:\workspace\sample-namespace-packages\pkg_resources\pkg_b

I write a test suite for pkg_b, consisting of one line:

(localpython) C:\workspace>echo import example_pkg.b > test_b.py

Now, if I run that test suite in pytest 4.5 or less, it succeeds:

(localpython) C:\workspace>python -m pip install pytest==4.5.0
Collecting pytest==4.5.0
...

(localpython) C:\workspace>pytest test_b.py
================================================= test session starts =================================================
platform win32 -- Python 3.5.2, pytest-4.5.0, py-1.8.0, pluggy-0.12.0
rootdir: C:\workspace
collected 0 items

============================================ no tests ran in 0.02 seconds =============================================

But if I run it in pytest 4.6 or greater, it errors:

(localpython) C:\workspace>python -m pip install pytest==4.6.0
Collecting pytest==4.6.0
...

(localpython) C:\workspace>pytest test_b.py
================================================= test session starts =================================================
platform win32 -- Python 3.5.2, pytest-4.6.0, py-1.8.0, pluggy-0.12.0
rootdir: C:\workspace
collected 0 items / 1 errors

======================================================= ERRORS ========================================================
_____________________________________________ ERROR collecting test_b.py ______________________________________________
ImportError while importing test module 'C:\workspace\test_b.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_b.py:1: in <module>
    import example_pkg.b
E   ImportError: No module named 'example_pkg.b'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=============================================== 1 error in 0.08 seconds ===============================================

The import error is understandable: It's presumably looking in the example_pkg in the site-packages and therefore not finding the b package, which is under the example_pkg on the PYTHONPATH.

Yet pytest 4.5 or earlier manages to find example_pkg.b.

Edit: According to a comment, the relevant difference is that pytest 4.5 and earlier imported pkg_resources. Indeed, if I add the line

import pkg_resources

at the top of my test file, test_b.py, then the test succeeds even in pytest 4.6 or greater. Furthermore, if I have multiple test files that try to import example_pkg.b, then it's necessary and sufficient to import pkg_resources in whichever of them pytest happens to run first.

That means that some side effect of importing pkg_resources makes example_pkg.b importable. What exactly does pkg_resources do that achieves that effect, and can I do it directly instead of getting it as a side effect of a seemingly unused import? And does my needing that side effect mean the setup is invalid?

1

There are 1 best solutions below

0
IQbrod On

Import your packages through sys as written in this project (using unittest not pytest)

import sys
sys.path.insert(0,"pkg_a")
sys.path.insert(0,"pkg_b")

#Test goes here

My project load test/testSpecie.py which tests files in src/...