Pytest: conftest.py in root directory should not add root directory to sys.path

Created on 21 Feb 2017  路  17Comments  路  Source: pytest-dev/pytest

I started putting conftest.py in the root directory of my packages, instead of the tests/ directory, because I wanted to modify my doctest namespace.

However, it turns out that putting conftest.py in the root directory causes py.test to use the local rather than the installed version of my package to do the tests, which breaks some of my tests which depend on compiled modules.

Is this the intended behaviour? Some people seem to use this as a feature (e.g. see this stackoverflow answer). However, in my case this was a bug that was relatively difficult to track down.

I fixed this by putting the following lines in the root conftest.py file:

import os.path as op
import sys
sys.path.remove(op.dirname(op.abspath(__file__)))

Anaconda python running on CentOS 6

$ py.test --version
This is pytest version 3.0.6, imported from /opt/conda/lib/python3.5/site-packages/pytest.py
setuptools registered plugins:
  pytest-logging-2015.11.4 at /opt/conda/lib/python3.5/site-packages/pytest_logging/plugin.py
  pytest-cov-2.4.0 at /opt/conda/lib/python3.5/site-packages/pytest_cov/plugin.py
  hypothesis-3.6.1 at /opt/conda/lib/python3.5/site-packages/hypothesis/extra/pytestplugin.py
collection

Most helpful comment

Indeed, good catch, thanks!

--import-mode=importlib will prevent pytest from messing with sys.path at all. 馃憤

(This will be released in 6.0.0, but 6.0.0rc1 can be used to test this already).

All 17 comments

this is expected behaviour with conftests, and indeed tricky to gauge
a common solution is to switch from the fragile root layout to the more robust src layout

in the root layout you have your python packages in the root folder directly, thus easily causing trouble with sys.path behaviour

in the src layout you have your python packages inside a src folder separating them from common importability

FWIW I would not be against trying to fix this (and I wrote that SO answer...), it is rather bad that pytest modifies sys.path so much just because of loading conftest files.

This would probably cause major breakage though so may be trick to introduce and need a major version etc.

a pytest.ini option wrt early conftest processing as well as expected sys.path changes can be done as a non-intrusive opt-in

if we give people a way to specify the expectation, it safes us and them a lot of pain and the need for a major release

From a glance at the code it seems pytest adds the conftest's directory to sys.path and leaves it there. Would improve things if we removed the contest's path after importing it?

It would break the world in various cases, like deferred imports

Hmm thanks. But adding an option like you propose help in this case?

I mean, adding an option that removes from sys.path

@nicoddemus on bit we need to control is leading of conftests only in pythonpath (so disallow adding them to sys.path)
the other bit we need to control is if and which early conftests to load

I'm actually interested in the opposite - I would like to include the top-level directory (current directory when running pytest) in the Python path. ("src", I know - not gonna happen)

This seems like something a couple of simple command line options could fix:

  • An option to never modify path
  • The default to work as it does today
  • An option to add current directory

I get there is a "right" way that people want to force everyone to use, but there are a lot of projects out there that don't use src. Auto-discovery really loses its shine when it doesn't actually work. Using a blank conftest.py to accomplish the same thing seems like real hack.

Hmmm adding an option indeed might be a good idea here in this case (although I don't think An option to never modify path is possible given we have to do that to import conftest.py files).

It might be better even as a pytest.ini option, which would make it "official" how to run pytest for a given project.

What do others think?

I've ran into this today:

root _
     |_ a
         |_ tests 
                |_ conftest.py

(root) $ cd a
(a)    $ pytest  tests 

So this is very confusing behavior here:

  • note that when my current working directory is a (not root)
  • a contains my tests
  • tests contain conftest.py

the root will be added to the sys.path, in a persistent way. One could argue that okay, the tests folder needs to be on path to import conftest.py, but why root?

A manifestation of this is that coverage report breaks, as imports no longer use the site package, but the local files.

Hi @gaborbernat!

That's strange, I can't reproduce your problem:

============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.3.0, py-1.5.2, pluggy-0.6.0
rootdir: X:\temp\conftest-issue\root\a, inifile:
collected 1 item

tests\test_a.py F                                                        [100%]

================================== FAILURES ===================================
____________________________________ test _____________________________________

    def test():
        for x in sys.path: print(x)
>       assert 0
E       assert 0

tests\test_a.py:5: AssertionError
---------------------------- Captured stdout call -----------------------------
X:\temp\conftest-issue\root\a\tests
X:\temp\conftest-issue\.env36\Scripts\pytest.exe
x:\temp\conftest-issue\.env36\scripts\python36.zip
C:\Users\Bruno\AppData\Local\Programs\Python\Python36\DLLs
C:\Users\Bruno\AppData\Local\Programs\Python\Python36\lib
C:\Users\Bruno\AppData\Local\Programs\Python\Python36
x:\temp\conftest-issue\.env36
x:\temp\conftest-issue\.env36\lib\site-packages
========================== 1 failed in 0.03 seconds ===========================

As can be seen, only root\a\tests is added to sys.path. Do you have perhaps another conftest.py somewhere? Do you have a pytest.ini?

https://github.com/pytest-dev/pytest/pull/5352 was a step in this direction, but was closed without explanation (?).

5352 was a step in this direction, but was closed without explanation (?).

Maybe @blueyed can explain why?

Because you did not want to take it as a hidden option only mostly - at least that was my impression.

@nicoddemus I think --import-mode=importlib fixes this, right?

Indeed, good catch, thanks!

--import-mode=importlib will prevent pytest from messing with sys.path at all. 馃憤

(This will be released in 6.0.0, but 6.0.0rc1 can be used to test this already).

Was this page helpful?
0 / 5 - 0 ratings