Skip to content

Commit 618337d

Browse files
committed
determine_setup: rootdir should be invocation_dir when -c is given
Found this while testing a project with pytest -c config/pytest.ini. rootdir ended up pointing to config/ instead of the project root, which broke conftest discovery since conftests are resolved relative to rootdir. Turned out `rootdir = inipath_.parent` was the culprit - it makes rootdir jump to wherever the config file lives, ignoring invocation_dir. Simple fix: use invocation_dir instead.
1 parent b106128 commit 618337d

4 files changed

Lines changed: 77 additions & 2 deletions

File tree

changelog/14454.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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`.

src/_pytest/config/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@
7777
from _pytest.warning_types import PytestConfigWarning
7878
from _pytest.warning_types import warn_explicit_for
7979

80+
import logging
81+
82+
log = logging.getLogger(__name__)
83+
8084

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

1508+
if ns.inifilename and not ns.rootdir:
1509+
if inipath is not None and inipath.parent != self.invocation_params.dir:
1510+
log.warning(
1511+
"rootdir was set to %s because -c was given without --rootdir. "
1512+
"Use --rootdir to explicitly disambiguate.",
1513+
self.invocation_params.dir,
1514+
)
1515+
15041516
self._parser.addini("addopts", "Extra command line options", "args")
15051517
self._parser.addini("minversion", "Minimally required pytest version")
15061518
self._parser.addini(

src/_pytest/config/findpaths.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def determine_setup(
308308
inipath: Path | None = inipath_
309309
inicfg = load_config_dict_from_file(inipath_) or {}
310310
if rootdir_cmd_arg is None:
311-
rootdir = inipath_.parent
311+
rootdir = invocation_dir
312312
else:
313313
ancestor = get_common_ancestor(invocation_dir, dirs)
314314
rootdir, inipath, inicfg, ignored_config_files = locate_config(

testing/test_config.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2028,12 +2028,74 @@ def test_with_specific_inifile(
20282028
override_ini=None,
20292029
args=[str(tmp_path)],
20302030
rootdir_cmd_arg=None,
2031-
invocation_dir=Path.cwd(),
2031+
invocation_dir=tmp_path,
20322032
)
20332033
assert rootpath == tmp_path
20342034
assert inipath == p
20352035
assert ini_config["x"] == ConfigValue("10", origin="file", mode="ini")
20362036

2037+
def test_config_in_subdir_does_not_change_rootdir(
2038+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
2039+
) -> None:
2040+
"""Config file in a subdir should not move rootdir to that subdir (#13246)."""
2041+
config_dir = tmp_path / "config"
2042+
config_dir.mkdir()
2043+
inipath = config_dir / "pytest.ini"
2044+
inipath.touch()
2045+
monkeypatch.chdir(tmp_path)
2046+
2047+
rootpath, found_inipath, *_ = determine_setup(
2048+
inifile=str(inipath),
2049+
override_ini=None,
2050+
args=[],
2051+
rootdir_cmd_arg=None,
2052+
invocation_dir=Path.cwd(),
2053+
)
2054+
assert rootpath == tmp_path, (
2055+
f"rootdir should be invocation_dir ({tmp_path}), "
2056+
f"got {rootpath}"
2057+
)
2058+
assert found_inipath == inipath
2059+
2060+
def test_rootdir_warning_when_config_in_subdir(
2061+
self, tmp_path: Path, caplog: pytest.LogCaptureFixture
2062+
) -> None:
2063+
"""When -c points to a subdir, a warning should be logged (#13246)."""
2064+
import logging
2065+
2066+
config_dir = tmp_path / "config"
2067+
config_dir.mkdir()
2068+
inipath = config_dir / "pytest.ini"
2069+
inipath.touch()
2070+
2071+
caplog.set_level(logging.WARNING)
2072+
Config.fromdictargs(
2073+
{"inifilename": str(inipath)}, # -c config/pytest.ini
2074+
[],
2075+
)
2076+
2077+
assert len(caplog.records) >= 1
2078+
assert "rootdir was set to" in caplog.records[0].message
2079+
assert "--rootdir" in caplog.records[0].message
2080+
2081+
def test_no_warning_when_config_in_rootdir(
2082+
self, tmp_path: Path, monkeypatch: MonkeyPatch, caplog: pytest.LogCaptureFixture
2083+
) -> None:
2084+
"""When -c points to the invocation dir itself, no warning needed (#13246)."""
2085+
import logging
2086+
2087+
inipath = tmp_path / "pytest.ini"
2088+
inipath.touch()
2089+
monkeypatch.chdir(tmp_path)
2090+
2091+
caplog.set_level(logging.WARNING)
2092+
Config.fromdictargs(
2093+
{"inifilename": str(inipath)},
2094+
[],
2095+
)
2096+
2097+
assert len(caplog.records) == 0
2098+
20372099
def test_explicit_config_file_sets_rootdir(
20382100
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
20392101
) -> None:

0 commit comments

Comments
 (0)