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/14454.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a regression where :option:`-c` pointing to a config file in a sub-directory would set :confval:`rootdir` to the config file's parent directory instead of the invocation directory, breaking conftest discovery. Patch by :user:`EternalRights`.
12 changes: 12 additions & 0 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import importlib
import importlib.metadata
import inspect
import logging
import os
import pathlib
import re
Expand Down Expand Up @@ -78,6 +79,9 @@
from _pytest.warning_types import warn_explicit_for


log = logging.getLogger(__name__)


if TYPE_CHECKING:
from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.cacheprovider import Cache
Expand Down Expand Up @@ -1501,6 +1505,14 @@ def parse(self, args: list[str], addopts: bool = True) -> None:
self._parser.extra_info["rootdir"] = str(self.rootpath)
self._parser.extra_info["inifile"] = str(self.inipath)

if ns.inifilename and not ns.rootdir:
if inipath is not None and inipath.parent != self.invocation_params.dir:
log.warning(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest doesnt use logging for ux we might want to find a differnt place to output this

@nicoddemus @bluetech any opinion on this - personally i lean towards the pre collection summary header that shows pytest/plugin versions + config paths already but something about that feels icky as well

"rootdir was set to %s because -c was given without --rootdir. "
"Use --rootdir to explicitly disambiguate.",
self.invocation_params.dir,
)

self._parser.addini("addopts", "Extra command line options", "args")
self._parser.addini("minversion", "Minimally required pytest version")
self._parser.addini(
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def determine_setup(
inipath: Path | None = inipath_
inicfg = load_config_dict_from_file(inipath_) or {}
if rootdir_cmd_arg is None:
rootdir = inipath_.parent
rootdir = invocation_dir
else:
ancestor = get_common_ancestor(invocation_dir, dirs)
rootdir, inipath, inicfg, ignored_config_files = locate_config(
Expand Down
63 changes: 62 additions & 1 deletion testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2028,12 +2028,73 @@ def test_with_specific_inifile(
override_ini=None,
args=[str(tmp_path)],
rootdir_cmd_arg=None,
invocation_dir=Path.cwd(),
invocation_dir=tmp_path,
)
assert rootpath == tmp_path
assert inipath == p
assert ini_config["x"] == ConfigValue("10", origin="file", mode="ini")

def test_config_in_subdir_does_not_change_rootdir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Config file in a subdir should not move rootdir to that subdir (#13246)."""
config_dir = tmp_path / "config"
config_dir.mkdir()
inipath = config_dir / "pytest.ini"
inipath.touch()
monkeypatch.chdir(tmp_path)

rootpath, found_inipath, *_ = determine_setup(
inifile=str(inipath),
override_ini=None,
args=[],
rootdir_cmd_arg=None,
invocation_dir=Path.cwd(),
)
assert rootpath == tmp_path, (
f"rootdir should be invocation_dir ({tmp_path}), got {rootpath}"
)
assert found_inipath == inipath

def test_rootdir_warning_when_config_in_subdir(
self, tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
"""When -c points to a subdir, a warning should be logged (#13246)."""
import logging

config_dir = tmp_path / "config"
config_dir.mkdir()
inipath = config_dir / "pytest.ini"
inipath.touch()

caplog.set_level(logging.WARNING)
Config.fromdictargs(
{"inifilename": str(inipath)}, # -c config/pytest.ini
[],
)

assert len(caplog.records) >= 1
assert "rootdir was set to" in caplog.records[0].message
assert "--rootdir" in caplog.records[0].message

def test_no_warning_when_config_in_rootdir(
self, tmp_path: Path, monkeypatch: MonkeyPatch, caplog: pytest.LogCaptureFixture
) -> None:
"""When -c points to the invocation dir itself, no warning needed (#13246)."""
import logging

inipath = tmp_path / "pytest.ini"
inipath.touch()
monkeypatch.chdir(tmp_path)

caplog.set_level(logging.WARNING)
Config.fromdictargs(
{"inifilename": str(inipath)},
[],
)

assert len(caplog.records) == 0

def test_explicit_config_file_sets_rootdir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down
Loading