Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/13913.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a regression where a conftest.py that registers command line options was sometimes not loaded as an initial conftest. This happened when a command line option's value was the path of an existing file (e.g. ``pytest --write-idents idents.txt``): due to intermixed argument parsing the value ended up among the positional arguments during pre-parsing and was treated as a test path, which suppressed the conftest discovery and made the option report as "unrecognized".
11 changes: 9 additions & 2 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,17 @@ def _set_initial_conftests(
if not safe_exists(anchor):
continue

# With intermixed argument parsing, the path *value* of a command
# line option (e.g. ``--write-idents idents.txt``) can end up among
# the positional arguments. Such a file anchor must not prevent the
# conftest discovery for its directory (and its ``test*``
# sub-directories), otherwise conftests that register the option are
# not loaded as initial conftests and the option becomes
# "unrecognized" (#13913). Resolve file anchors to their directory.
anchor = self._get_directory(anchor)
anchors.append(anchor)
# Let's also consider test* subdirs.
if anchor.is_dir():
anchors.extend(x for x in anchor.glob("test*") if x.is_dir())
anchors.extend(x for x in anchor.glob("test*") if x.is_dir())
if not anchors:
anchors.append(invocation_dir)
anchors.extend(x for x in invocation_dir.glob("test*") if x.is_dir())
Expand Down
30 changes: 30 additions & 0 deletions testing/test_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,36 @@ def test_it(request):
result.assert_outcomes(passed=1)


def test_initial_conftest_loaded_when_option_value_is_existing_file(
pytester: Pytester,
) -> None:
"""An initial conftest registering a command line option is still loaded
when the option's value happens to be the path of an existing file.

With intermixed argument parsing, such a value ends up among the
positional arguments during pre-parsing and used to be treated as a test
path anchor, which suppressed the ``test*`` conftest discovery and left
the option "unrecognized". Regression test for #13913.
"""
pytester.makepyfile(
**{
"tests/conftest.py": """
def pytest_addoption(parser):
parser.addoption("--write-idents", action="store", default=None)
""",
"test_it.py": """
def test_it(request):
assert request.config.getoption("--write-idents") == "idents.txt"
""",
}
)
# The option value is a path to an existing file on disk.
pytester.makefile(".txt", idents="")
result = pytester.runpytest("--write-idents", "idents.txt")
assert result.ret == ExitCode.OK
result.assert_outcomes(passed=1)


def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
ct1 = pytester.makeconftest("")
sub = pytester.mkdir("sub")
Expand Down
Loading