Skip to content
Latchkey

pytest "--import-mode=importlib" Changes Imports and Breaks Tests in CI

pytest’s --import-mode controls how test modules are imported. The default prepend mode inserts directories onto sys.path; importlib mode does not, so tests that relied on implicit sys.path entries fail to import the package under test.

What this error means

After setting --import-mode=importlib (or upgrading to a config that defaults to it), tests fail at collection with ModuleNotFoundError for the package under test or a conftest, even though the files exist. Switching back to prepend, or installing the package, fixes it.

pytest output
ERROR collecting tests/test_api.py
ModuleNotFoundError: No module named 'myapp'
# worked under --import-mode=prepend, fails under importlib

Diagnose it: what did pytest actually collect?

Most pytest failures that only happen in CI are collection or import-path problems rather than test failures. The runner has a different working directory, a different sys.path, and usually no editable install, so a test module that imports your package locally may not resolve at all.

Terminal
# what would run, without running it
pytest --collect-only -q | tail -20

# where pytest thinks the root is (drives conftest and import mode)
pytest --collect-only 2>&1 | grep -i rootdir

# is the package importable at all from the runner cwd?
python -c "import yourpackage, sys; print(yourpackage.__file__)"
python -c "import sys; print(sys.path)"

Common causes

importlib mode does not modify sys.path

Unlike prepend/append, importlib mode imports tests without inserting their directories onto sys.path. Code relying on that implicit path no longer resolves the package.

Package not installed, only on an implicit path

If the project was never installed (pip install -e .) and relied on prepend adding the rootdir, importlib mode has no path to it.

How to fix it

Install the project so imports resolve regardless of mode

An editable install puts the package on sys.path properly, which importlib mode (and a src-layout) expects.

Terminal
pip install -e .
pytest --import-mode=importlib

Set the import mode explicitly and consistently

Pin the mode in config so local and CI behave the same.

pyproject.toml
[tool.pytest.ini_options]
addopts = "--import-mode=importlib"
pythonpath = ["src"]   # if not installing the package

Make a zero-test run fail the build

Terminal
# fail explicitly when nothing is collected
pytest --strict-markers -q
test "${PIPESTATUS[0]}" -ne 5 || { echo "pytest collected no tests"; exit 1; }

How to prevent it

  • Install the project (editable is fine) rather than relying on implicit sys.path.
  • Pin --import-mode in config so it is consistent across environments.
  • Adopt a src-layout with importlib mode for clean import isolation.

Frequently asked questions

What causes pytest "--import-mode=importlib" changes imports and breaks tests in CI?
There are 2 common causes: importlib mode does not modify sys.path and package not installed, only on an implicit path. Unlike prepend/append, importlib mode imports tests without inserting their directories onto sys.path.
How do I fix pytest "--import-mode=importlib" changes imports and breaks tests in CI?
There are 2 fixes depending on which cause you have: install the project so imports resolve regardless of mode and set the import mode explicitly and consistently. Work through them in order, since the first is the most common.
What does pytest "--import-mode=importlib" changes imports and breaks tests in CI actually mean?
After setting --import-mode=importlib (or upgrading to a config that defaults to it), tests fail at collection with ModuleNotFoundError for the package under test or a conftest, even though the files exist.
How do I stop pytest "--import-mode=importlib" changes imports and breaks tests in CI happening again?
Install the project (editable is fine) rather than relying on implicit sys.path. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card