Skip to content

Commit 0877d57

Browse files
authored
Merge pull request #13830 from bluetech/simplify-override-ini
config: apply `--override-ini` eagerly
2 parents e76b054 + 15c2e19 commit 0877d57

File tree

4 files changed

+53
-30
lines changed

4 files changed

+53
-30
lines changed

changelog/13830.misc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Configuration overrides (``-o``/``--override-ini``) are now processed during startup rather than during :func:`config.getini() <pytest.Config.getini>`.

src/_pytest/config/__init__.py

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,6 @@ def __init__(
10831083
self.trace = self.pluginmanager.trace.root.get("config")
10841084
self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment]
10851085
self._inicache: dict[str, Any] = {}
1086-
self._override_ini: Sequence[str] = ()
10871086
self._opt2dest: dict[str, str] = {}
10881087
self._cleanup_stack = contextlib.ExitStack()
10891088
self.pluginmanager.register(self, "pytestconfig")
@@ -1251,6 +1250,7 @@ def _initini(self, args: Sequence[str]) -> None:
12511250
)
12521251
rootpath, inipath, inicfg, ignored_config_files = determine_setup(
12531252
inifile=ns.inifilename,
1253+
override_ini=ns.override_ini,
12541254
args=ns.file_or_dir + unknown_args,
12551255
rootdir_cmd_arg=ns.rootdir or None,
12561256
invocation_dir=self.invocation_params.dir,
@@ -1272,7 +1272,6 @@ def _initini(self, args: Sequence[str]) -> None:
12721272
type="args",
12731273
default=[],
12741274
)
1275-
self._override_ini = ns.override_ini or ()
12761275

12771276
def _consider_importhook(self, args: Sequence[str]) -> None:
12781277
"""Install the PEP 302 import hook if using assertion rewriting.
@@ -1641,14 +1640,10 @@ def _getini(self, name: str):
16411640
_description, type, default = self._parser._inidict[name]
16421641
except KeyError as e:
16431642
raise ValueError(f"unknown configuration value: {name!r}") from e
1644-
override_value = self._get_override_ini_value(name)
1645-
if override_value is None:
1646-
try:
1647-
value = self.inicfg[name]
1648-
except KeyError:
1649-
return default
1650-
else:
1651-
value = override_value
1643+
try:
1644+
value = self.inicfg[name]
1645+
except KeyError:
1646+
return default
16521647
# Coerce the values based on types.
16531648
#
16541649
# Note: some coercions are only required if we are reading from .ini files, because
@@ -1719,23 +1714,6 @@ def _getconftest_pathlist(
17191714
values.append(relroot)
17201715
return values
17211716

1722-
def _get_override_ini_value(self, name: str) -> str | None:
1723-
value = None
1724-
# override_ini is a list of "ini=value" options.
1725-
# Always use the last item if multiple values are set for same ini-name,
1726-
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2.
1727-
for ini_config in self._override_ini:
1728-
try:
1729-
key, user_ini_value = ini_config.split("=", 1)
1730-
except ValueError as e:
1731-
raise UsageError(
1732-
f"-o/--override-ini expects option=value style (got: {ini_config!r})."
1733-
) from e
1734-
else:
1735-
if key == name:
1736-
value = user_ini_value
1737-
return value
1738-
17391717
def getoption(self, name: str, default: Any = notset, skip: bool = False):
17401718
"""Return command line option value.
17411719

src/_pytest/config/findpaths.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,35 @@ def get_dir_from_path(path: Path) -> Path:
181181
return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)]
182182

183183

184+
def parse_override_ini(override_ini: Sequence[str] | None) -> dict[str, str]:
185+
"""Parse the -o/--override-ini command line arguments and return the overrides.
186+
187+
:raises UsageError:
188+
If one of the values is malformed.
189+
"""
190+
overrides = {}
191+
# override_ini is a list of "ini=value" options.
192+
# Always use the last item if multiple values are set for same ini-name,
193+
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2.
194+
for ini_config in override_ini or ():
195+
try:
196+
key, user_ini_value = ini_config.split("=", 1)
197+
except ValueError as e:
198+
raise UsageError(
199+
f"-o/--override-ini expects option=value style (got: {ini_config!r})."
200+
) from e
201+
else:
202+
overrides[key] = user_ini_value
203+
return overrides
204+
205+
184206
CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead."
185207

186208

187209
def determine_setup(
188210
*,
189211
inifile: str | None,
212+
override_ini: Sequence[str] | None,
190213
args: Sequence[str],
191214
rootdir_cmd_arg: str | None,
192215
invocation_dir: Path,
@@ -196,12 +219,16 @@ def determine_setup(
196219
197220
:param inifile:
198221
The `--inifile` command line argument, if given.
222+
:param override_ini:
223+
The -o/--override-ini command line arguments, if given.
199224
:param args:
200225
The free command line arguments.
201226
:param rootdir_cmd_arg:
202227
The `--rootdir` command line argument, if given.
203228
:param invocation_dir:
204229
The working directory when pytest was invoked.
230+
231+
:raises UsageError:
205232
"""
206233
rootdir = None
207234
dirs = get_dirs_from_args(args)
@@ -238,8 +265,12 @@ def determine_setup(
238265
raise UsageError(
239266
f"Directory '{rootdir}' not found. Check your '--rootdir' option."
240267
)
268+
269+
ini_overrides = parse_override_ini(override_ini)
270+
inicfg.update(ini_overrides)
271+
241272
assert rootdir is not None
242-
return rootdir, inipath, inicfg or {}, ignored_config_files
273+
return rootdir, inipath, inicfg, ignored_config_files
243274

244275

245276
def is_fs_root(p: Path) -> bool:

testing/test_config.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,7 @@ def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None:
16311631
for args in ([str(tmp_path)], [str(a)], [str(b)]):
16321632
rootpath, parsed_inipath, *_ = determine_setup(
16331633
inifile=None,
1634+
override_ini=None,
16341635
args=args,
16351636
rootdir_cmd_arg=None,
16361637
invocation_dir=Path.cwd(),
@@ -1639,6 +1640,7 @@ def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None:
16391640
assert parsed_inipath == inipath
16401641
rootpath, parsed_inipath, ini_config, _ = determine_setup(
16411642
inifile=None,
1643+
override_ini=None,
16421644
args=[str(b), str(a)],
16431645
rootdir_cmd_arg=None,
16441646
invocation_dir=Path.cwd(),
@@ -1656,6 +1658,7 @@ def test_pytestini_overrides_empty_other(self, tmp_path: Path, name: str) -> Non
16561658
(a / name).touch()
16571659
rootpath, parsed_inipath, *_ = determine_setup(
16581660
inifile=None,
1661+
override_ini=None,
16591662
args=[str(a)],
16601663
rootdir_cmd_arg=None,
16611664
invocation_dir=Path.cwd(),
@@ -1670,6 +1673,7 @@ def test_setuppy_fallback(self, tmp_path: Path) -> None:
16701673
(tmp_path / "setup.py").touch()
16711674
rootpath, inipath, inicfg, _ = determine_setup(
16721675
inifile=None,
1676+
override_ini=None,
16731677
args=[str(a)],
16741678
rootdir_cmd_arg=None,
16751679
invocation_dir=Path.cwd(),
@@ -1682,6 +1686,7 @@ def test_nothing(self, tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
16821686
monkeypatch.chdir(tmp_path)
16831687
rootpath, inipath, inicfg, _ = determine_setup(
16841688
inifile=None,
1689+
override_ini=None,
16851690
args=[str(tmp_path)],
16861691
rootdir_cmd_arg=None,
16871692
invocation_dir=Path.cwd(),
@@ -1709,6 +1714,7 @@ def test_with_specific_inifile(
17091714
p.write_text(contents, encoding="utf-8")
17101715
rootpath, inipath, ini_config, _ = determine_setup(
17111716
inifile=str(p),
1717+
override_ini=None,
17121718
args=[str(tmp_path)],
17131719
rootdir_cmd_arg=None,
17141720
invocation_dir=Path.cwd(),
@@ -1728,6 +1734,7 @@ def test_explicit_config_file_sets_rootdir(
17281734
# No config file is explicitly given: rootdir is determined to be cwd.
17291735
rootpath, found_inipath, *_ = determine_setup(
17301736
inifile=None,
1737+
override_ini=None,
17311738
args=[str(tests_dir)],
17321739
rootdir_cmd_arg=None,
17331740
invocation_dir=Path.cwd(),
@@ -1740,6 +1747,7 @@ def test_explicit_config_file_sets_rootdir(
17401747
inipath.touch()
17411748
rootpath, found_inipath, *_ = determine_setup(
17421749
inifile=str(inipath),
1750+
override_ini=None,
17431751
args=[str(tests_dir)],
17441752
rootdir_cmd_arg=None,
17451753
invocation_dir=Path.cwd(),
@@ -1757,6 +1765,7 @@ def test_with_arg_outside_cwd_without_inifile(
17571765
b.mkdir()
17581766
rootpath, inifile, *_ = determine_setup(
17591767
inifile=None,
1768+
override_ini=None,
17601769
args=[str(a), str(b)],
17611770
rootdir_cmd_arg=None,
17621771
invocation_dir=Path.cwd(),
@@ -1773,6 +1782,7 @@ def test_with_arg_outside_cwd_with_inifile(self, tmp_path: Path) -> None:
17731782
inipath.touch()
17741783
rootpath, parsed_inipath, *_ = determine_setup(
17751784
inifile=None,
1785+
override_ini=None,
17761786
args=[str(a), str(b)],
17771787
rootdir_cmd_arg=None,
17781788
invocation_dir=Path.cwd(),
@@ -1787,6 +1797,7 @@ def test_with_non_dir_arg(
17871797
monkeypatch.chdir(tmp_path)
17881798
rootpath, inipath, *_ = determine_setup(
17891799
inifile=None,
1800+
override_ini=None,
17901801
args=dirs,
17911802
rootdir_cmd_arg=None,
17921803
invocation_dir=Path.cwd(),
@@ -1803,6 +1814,7 @@ def test_with_existing_file_in_subdir(
18031814
monkeypatch.chdir(tmp_path)
18041815
rootpath, inipath, *_ = determine_setup(
18051816
inifile=None,
1817+
override_ini=None,
18061818
args=["a/exist"],
18071819
rootdir_cmd_arg=None,
18081820
invocation_dir=Path.cwd(),
@@ -1822,6 +1834,7 @@ def test_with_config_also_in_parent_directory(
18221834

18231835
rootpath, inipath, *_ = determine_setup(
18241836
inifile=None,
1837+
override_ini=None,
18251838
args=["tests/"],
18261839
rootdir_cmd_arg=None,
18271840
invocation_dir=Path.cwd(),
@@ -1978,7 +1991,7 @@ def test_addopts_before_initini(
19781991
monkeypatch.setenv("PYTEST_ADDOPTS", f"-o cache_dir={cache_dir}")
19791992
config = _config_for_test
19801993
config._preparse([], addopts=True)
1981-
assert config._override_ini == [f"cache_dir={cache_dir}"]
1994+
assert config.inicfg.get("cache_dir") == cache_dir
19821995

19831996
def test_addopts_from_env_not_concatenated(
19841997
self, monkeypatch: MonkeyPatch, _config_for_test
@@ -2016,7 +2029,7 @@ def test_override_ini_does_not_contain_paths(
20162029
"""Check that -o no longer swallows all options after it (#3103)"""
20172030
config = _config_for_test
20182031
config._preparse(["-o", "cache_dir=/cache", "/some/test/path"])
2019-
assert config._override_ini == ["cache_dir=/cache"]
2032+
assert config.inicfg.get("cache_dir") == "/cache"
20202033

20212034
def test_multiple_override_ini_options(self, pytester: Pytester) -> None:
20222035
"""Ensure a file path following a '-o' option does not generate an error (#3103)"""

0 commit comments

Comments
 (0)