Summary:
I’m trying to set up a custom pytest option with the pytest_addoption
feature.
But when trying to configure my project with a project.toml
file while using the said custom option, I’m getting the following error:
$ pytest --foo foo
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --foo
inifile: /home/vmonteco/Code/MREs/pytest__addoption__pyproject_toml/01_adding_pyproject.toml/pyproject.toml
rootdir: /home/vmonteco/Code/MREs/pytest__addoption__pyproject_toml/01_adding_pyproject.toml
Why is this problem occurring despite the configured test path and how could I solve it?
Used versions are:
- Python 3.10.13
- Pytest 8.1.1
How to reproduce:
Step 1 – before organizing the project, it works:
I start with a very simple test in a single directory.
$ tree
.
├── conftest.py
└── test_foo.py
1 directory, 2 files
$
conftest.py
:
import pytest
def pytest_addoption(parser):
parser.addoption("--foo", action="store")
@pytest.fixture
def my_val(request):
return request.config.getoption("--foo")
test_foo.py
:
def test_foo(my_val):
assert my_val == "foo"
$ pytest --foo bar
=============================== test session starts ===============================
platform linux -- Python 3.10.13, pytest-8.1.1, pluggy-1.5.0
rootdir: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/00_simplest_case
collected 1 item
test_foo.py F [100%]
==================================== FAILURES =====================================
____________________________________ test_foo _____________________________________
my_val = 'bar'
def test_foo(my_val):
> assert my_val == "foo"
E AssertionError: assert 'bar' == 'foo'
E
E - foo
E + bar
test_foo.py:2: AssertionError
============================= short test summary info =============================
FAILED test_foo.py::test_foo - AssertionError: assert 'bar' == 'foo'
================================ 1 failed in 0.01s ================================
$
Step 2 – When adding an empty pyproject.toml
and reorganizing the project, it fails:
$ tree
.
├── my_project
│ └── my_tests
│ ├── conftest.py
│ ├── __init__.py
│ └── test_foo.py
└── pyproject.toml
3 directories, 4 files
$ pytest --foo bar
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --foo
inifile: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/01_adding_pyproject_toml/pyproject.toml
rootdir: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/01_adding_pyproject_toml
$
Notes:
- In doubt, I also added an
__init__.py
file. - The
pyproject.toml
seems to be recognized despite not having the required[tool.pytest.ini_options]
table, thus apparently contradicting the documentation.
However, there’s a simple workaround that seems to work in this specific case:
Just manually passing the test path to my tests as command line argument seems enough to make things work correctly again:
$ pytest --foo bar my_project/my_tests
=============================== test session starts ===============================
platform linux -- Python 3.10.13, pytest-8.1.1, pluggy-1.5.0
rootdir: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/01_adding_pyproject_toml
configfile: pyproject.toml
collected 1 item
my_project/my_tests/test_foo.py F [100%]
==================================== FAILURES =====================================
____________________________________ test_foo _____________________________________
my_val = 'bar'
def test_foo(my_val):
> assert my_val == "foo"
E AssertionError: assert 'bar' == 'foo'
E
E - foo
E + bar
my_project/my_tests/test_foo.py:2: AssertionError
============================= short test summary info =============================
FAILED my_project/my_tests/test_foo.py::test_foo - AssertionError: assert 'bar' == 'foo'
================================ 1 failed in 0.02s ================================
$
But I’d like to avoid that and I’d rather have my project correctly configured.
Step 3 – Unsuccessfully trying to configure testpaths
in the pyproject.toml
.
The best explanation I found so far lies relies on the following points in the documentation:
-
The necessity to put pytest_addoption in an “initial”
conftest.py
:This hook is only called for initial conftests.
pytest_addoption documentation
-
What is actually an “initial
conftest.py
“:
Initial conftest are for each test path the files whose path match<test_path>/conftest.py
or<test_test>/test*/conftest.py
.- by loading all “initial “conftest.py files:
- determine the test paths: specified on the command line, otherwise in testpaths if defined and running from the rootdir, otherwise the current dir
- for each test path, load conftest.py and test*/conftest.py relative to the directory part of the test path, if exist. Before a conftest.py file is loaded, load conftest.py files in all of its parent directories. After a conftest.py file is loaded, recursively load all plugins specified in its pytest_plugins variable if present.
Plugin discovery order at tool startup
So, if I understand well:
-
my error seems to occurs because if I don’t provide an explicit test path, the current directory is used. But in that case, my
conftest.py
is too deep into the arborescence to be used as an initial conftest. -
With my workaround, explicitly passing a deeper test path solves this by making the conftest “initial” again.
From this, it would seem appropriate to try to translate my command line argument path into a bit of configuration (testpaths
) as shown in the relevant documentation.
But when trying to run my command again, I still get the same error:
$ cat pyproject.toml
[tool.pytest.ini_options]
testpaths = [
"my_project/my_tests",
]
$ tree
.
├── my_project
│ └── my_tests
│ ├── conftest.py
│ ├── __init__.py
│ └── test_foo.py
└── pyproject.toml
3 directories, 4 files
$ pytest --foo bar
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --foo
inifile: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/02_attempt_to_solve/pyproject.toml
rootdir: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/02_attempt_to_solve
$
I also tried to use a different kind of configuration file:
$ cat pytest.ini
[pytest]
testpaths = my_project/my_tests
$ pytest --foo bar
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --foo
inifile: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/03_with_pytest_ini/pytest.ini
rootdir: /home/vmonteco/code/MREs/pytest__addoption__pyproject_toml/03_with_pytest_ini
$
But it still doesn’t solve the problem despite the equivalent as command line argument seems to work. Why?