From 4038752bf38be9dcb724e028b5d604ac05f64dd5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Dec 2021 11:01:10 -0300 Subject: [PATCH 1/2] Ensure Config.inifile is available during pytest_cmdline_main Fix #9396 --- changelog/9396.bugfix.rst | 1 + src/_pytest/legacypath.py | 69 +++++++++++++++++++------------------- testing/test_config.py | 1 + testing/test_legacypath.py | 17 ++++++++++ 4 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 changelog/9396.bugfix.rst diff --git a/changelog/9396.bugfix.rst b/changelog/9396.bugfix.rst new file mode 100644 index 000000000..dcb83bbc1 --- /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 4c2db9412..37e8c2422 100644 --- a/src/_pytest/legacypath.py +++ b/src/_pytest/legacypath.py @@ -403,14 +403,44 @@ def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: self.path = Path(value) +@hookimpl(tryfirst=True) +def pytest_load_initial_conftests(early_config: Config) -> None: + """Monkeypatch legacy path attributes in several classes, as early as possible.""" + mp = MonkeyPatch() + early_config.add_cleanup(mp.undo) + + # Add Cache.makedir(). + mp.setattr(Cache, "makedir", Cache_makedir, raising=False) + + # Add FixtureRequest.fspath property. + mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) + + # Add TerminalReporter.startdir property. + mp.setattr( + TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False + ) + + # Add Config.{invocation_dir,rootdir,inifile} properties. + mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) + mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) + mp.setattr(Config, "inifile", property(Config_inifile), raising=False) + + # Add Session.startdir property. + mp.setattr(Session, "startdir", property(Session_stardir), raising=False) + + # Add pathlist configuration type. + mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) + + # Add Node.fspath property. + mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) + + @hookimpl def pytest_configure(config: Config) -> None: - import pytest - - mp = pytest.MonkeyPatch() - config.add_cleanup(mp.undo) - + """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" if config.pluginmanager.has_plugin("tmpdir"): + mp = 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 @@ -427,35 +457,6 @@ def pytest_configure(config: Config) -> None: config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") - # Add Cache.makedir(). - mp.setattr(pytest.Cache, "makedir", Cache_makedir, raising=False) - - # Add FixtureRequest.fspath property. - mp.setattr( - pytest.FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False - ) - - # Add TerminalReporter.startdir property. - mp.setattr( - TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False - ) - - # Add Config.{invocation_dir,rootdir,inifile} properties. - mp.setattr( - pytest.Config, "invocation_dir", property(Config_invocation_dir), raising=False - ) - mp.setattr(pytest.Config, "rootdir", property(Config_rootdir), raising=False) - mp.setattr(pytest.Config, "inifile", property(Config_inifile), raising=False) - - # Add Session.startdir property. - mp.setattr(pytest.Session, "startdir", property(Session_stardir), raising=False) - - # Add pathlist configuration type. - mp.setattr(pytest.Config, "_getini_unknown_type", Config__getini_unknown_type) - - # Add Node.fspath property. - mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) - @hookimpl def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: diff --git a/testing/test_config.py b/testing/test_config.py index 9a57b919d..f691d3ed5 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1268,6 +1268,7 @@ def test_load_initial_conftest_last_ordering(_config_for_test): 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 1d3fdb4bd..fbfd88b73 100644 --- a/testing/test_legacypath.py +++ b/testing/test_legacypath.py @@ -161,3 +161,20 @@ def test_override_ini_paths(pytester: pytest.Pytester) -> None: ) 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}") From 696f955ff8c3345de5090e0aada6e3223f67969f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Dec 2021 11:49:04 -0300 Subject: [PATCH 2/2] Split wrappers and non-wrappers in test_load_initial_conftest_last_ordering Seems better to test which one is which explicitly. --- testing/test_config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/test_config.py b/testing/test_config.py index f691d3ed5..c8e21a847 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1264,16 +1264,16 @@ def test_load_initial_conftest_last_ordering(_config_for_test): m = My() pm.register(m) hc = pm.hook.pytest_load_initial_conftests - values = hc._nonwrappers + hc._wrappers - expected = [ + assert [x.function.__module__ for x in hc._nonwrappers] == [ "_pytest.config", m.__module__, "_pytest.legacypath", "_pytest.pythonpath", + ] + assert [x.function.__module__ for x in hc._wrappers] == [ "_pytest.capture", "_pytest.warnings", ] - assert [x.function.__module__ for x in values] == expected def test_get_plugin_specs_as_list() -> None: