diff --git a/changelog/9396.bugfix.rst b/changelog/9396.bugfix.rst new file mode 100644 index 00000000000..dcb83bbc14f --- /dev/null +++ b/changelog/9396.bugfix.rst @@ -0,0 +1 @@ +Ensure :attr:`pytest.Config.inifile` is available during the :func:`pytest_cmdline_main <_pytest.hookspec.pytest_cmdline_main>` hook (regression during ``7.0.0rc1``). diff --git a/src/_pytest/legacypath.py b/src/_pytest/legacypath.py index 743c06d55c4..e4edec845cb 100644 --- a/src/_pytest/legacypath.py +++ b/src/_pytest/legacypath.py @@ -400,27 +400,11 @@ def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: self.path = Path(value) -@pytest.hookimpl -def pytest_configure(config: pytest.Config) -> None: +@pytest.hookimpl(tryfirst=True) +def pytest_load_initial_conftests(early_config: pytest.Config) -> None: + """Monkeypatch legacy path attributes in several classes, as early as possible.""" mp = pytest.MonkeyPatch() - config.add_cleanup(mp.undo) - - if config.pluginmanager.has_plugin("tmpdir"): - # Create TmpdirFactory and attach it to the config object. - # - # This is to comply with existing plugins which expect the handler to be - # available at pytest_configure time, but ideally should be moved entirely - # to the tmpdir_factory session fixture. - try: - tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] - except AttributeError: - # tmpdir plugin is blocked. - pass - else: - _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) - mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) - - config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + early_config.add_cleanup(mp.undo) # Add Cache.makedir(). mp.setattr(pytest.Cache, "makedir", Cache_makedir, raising=False) @@ -452,6 +436,29 @@ def pytest_configure(config: pytest.Config) -> None: mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) +@pytest.hookimpl +def pytest_configure(config: pytest.Config) -> None: + """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" + if config.pluginmanager.has_plugin("tmpdir"): + mp = pytest.MonkeyPatch() + config.add_cleanup(mp.undo) + # Create TmpdirFactory and attach it to the config object. + # + # This is to comply with existing plugins which expect the handler to be + # available at pytest_configure time, but ideally should be moved entirely + # to the tmpdir_factory session fixture. + try: + tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] + except AttributeError: + # tmpdir plugin is blocked. + pass + else: + _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) + mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) + + config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + + @pytest.hookimpl def pytest_plugin_registered( plugin: object, manager: pytest.PytestPluginManager diff --git a/testing/test_config.py b/testing/test_config.py index 4435591164f..75babdd1924 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1268,6 +1268,7 @@ def pytest_load_initial_conftests(self): expected = [ "_pytest.config", m.__module__, + "_pytest.legacypath", "_pytest.pythonpath", "_pytest.capture", "_pytest.warnings", diff --git a/testing/test_legacypath.py b/testing/test_legacypath.py index 9ab139df467..8acafe98e7c 100644 --- a/testing/test_legacypath.py +++ b/testing/test_legacypath.py @@ -161,3 +161,20 @@ def test_overriden(pytestconfig): ) result = pytester.runpytest("--override-ini", "paths=foo/bar1.py foo/bar2.py", "-s") result.stdout.fnmatch_lines(["user_path:bar1.py", "user_path:bar2.py"]) + + +def test_inifile_from_cmdline_main_hook(pytester: pytest.Pytester) -> None: + """Ensure Config.inifile is available during pytest_cmdline_main (#9396).""" + p = pytester.makeini( + """ + [pytest] + """ + ) + pytester.makeconftest( + """ + def pytest_cmdline_main(config): + print("pytest_cmdline_main inifile =", config.inifile) + """ + ) + result = pytester.runpytest_subprocess("-s") + result.stdout.fnmatch_lines(f"*pytest_cmdline_main inifile = {p}")