From 8765bd70e6dadb83433c85e5d445732c8bd04a96 Mon Sep 17 00:00:00 2001 From: Gabriele Date: Thu, 15 Oct 2026 22:42:15 -0300 Subject: [PATCH 1/4] deprecate PytestPluginManager.getplugin --- src/_pytest/capture.py | 12 ++++++------ src/_pytest/config/__init__.py | 5 +++-- src/_pytest/debugging.py | 6 +++--- src/_pytest/doctest.py | 2 +- src/_pytest/pastebin.py | 4 ++-- src/_pytest/setuponly.py | 2 +- src/_pytest/subtests.py | 4 ++-- testing/logging/test_fixture.py | 8 ++++---- testing/logging/test_reporting.py | 20 ++++++++++---------- testing/python/collect.py | 2 +- testing/test_capture.py | 2 +- testing/test_config.py | 14 +++++++++++++- testing/test_debugging.py | 2 +- testing/test_pastebin.py | 4 ++-- testing/test_terminal.py | 2 +- 15 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index b914bc2831c..85905dd6893 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -999,7 +999,7 @@ def _is_started(self) -> bool: @contextlib.contextmanager def disabled(self) -> Generator[None]: """Temporarily disable capturing while inside the ``with`` block.""" - capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( + capmanager: CaptureManager = self.request.config.pluginmanager.get_plugin( "capturemanager" ) with capmanager.global_and_fixture_disabled(): @@ -1028,7 +1028,7 @@ def test_output(capsys): captured = capsys.readouterr() assert captured.out == "hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.get_plugin("capturemanager") capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1061,7 +1061,7 @@ def test_output(capteesys): captured = capteesys.readouterr() assert captured.out == "hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.get_plugin("capturemanager") capture_fixture = CaptureFixture( SysCapture, request, config=dict(tee=True), _ispytest=True ) @@ -1091,7 +1091,7 @@ def test_output(capsysbinary): captured = capsysbinary.readouterr() assert captured.out == b"hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.get_plugin("capturemanager") capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1119,7 +1119,7 @@ def test_system_echo(capfd): captured = capfd.readouterr() assert captured.out == "hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.get_plugin("capturemanager") capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1148,7 +1148,7 @@ def test_system_echo(capfdbinary): assert captured.out == b"hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.get_plugin("capturemanager") capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 60ba5f54a89..a1a8df2b442 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -84,7 +84,6 @@ from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import warn_explicit_for - if TYPE_CHECKING: from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.cacheprovider import Cache @@ -615,8 +614,10 @@ def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None self.consider_module(plugin) return plugin_name + @deprecated( + "PytestPluginManager.getplugin() is deprecated, use get_plugin() instead." + ) def getplugin(self, name: str): - # Support deprecated naming because plugins (xdist e.g.) use it. plugin: _PluggyPlugin | None = self.get_plugin(name) return plugin diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index b256f83c8bf..7c32003d44e 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -244,7 +244,7 @@ def _init_pdb(cls, method, *args, **kwargs): if cls._pluginmanager is None: capman: CaptureManager | None = None else: - capman = cls._pluginmanager.getplugin("capturemanager") + capman = cls._pluginmanager.get_plugin("capturemanager") if capman: capman.suspend(in_=True) @@ -287,7 +287,7 @@ class PdbInvoke: def pytest_exception_interact( self, node: Node, call: CallInfo[Any], report: BaseReport ) -> None: - capman = node.config.pluginmanager.getplugin("capturemanager") + capman = node.config.pluginmanager.get_plugin("capturemanager") if capman: capman.suspend_global_capture(in_=True) out, err = capman.read_global_capture() @@ -340,7 +340,7 @@ def _enter_pdb( # XXX we reuse the TerminalReporter's terminalwriter # because this seems to avoid some encoding related troubles # for not completely clear reasons. - tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw = node.config.pluginmanager.get_plugin("terminalreporter")._tw tw.line() showcapture = node.config.option.showcapture diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 04d707113cc..5985ae4e888 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -312,7 +312,7 @@ def _disable_output_capturing_for_darwin(self) -> None: """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" if platform.system() != "Darwin": return - capman = self.config.pluginmanager.getplugin("capturemanager") + capman = self.config.pluginmanager.get_plugin("capturemanager") if capman: capman.suspend_global_capture(in_=True) out, err = capman.read_global_capture() diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 5e1c06ca9e5..d3bbb44eb23 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -38,7 +38,7 @@ def pytest_configure(config: Config) -> None: config.issue_config_time_warning(PASTEBIN.format(), 2) if config.option.pastebin == "all": - tr = config.pluginmanager.getplugin("terminalreporter") + tr = config.pluginmanager.get_plugin("terminalreporter") # If no terminal reporter plugin is present, nothing we can do here; # this can happen when this function executes in a worker node # when using pytest-xdist, for example. @@ -65,7 +65,7 @@ def pytest_unconfigure(config: Config) -> None: pastebinfile.close() del config.stash[pastebinfile_key] # Undo our patching in the terminal reporter. - tr = config.pluginmanager.getplugin("terminalreporter") + tr = config.pluginmanager.get_plugin("terminalreporter") del tr._tw.__dict__["write"] # Write summary. tr.write_sep("=", "Sending information to Paste Service") diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 7e6b46bcdb4..79865b8840f 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -64,7 +64,7 @@ def pytest_fixture_post_finalizer( def _show_fixture_action( fixturedef: FixtureDef[object], config: Config, msg: str ) -> None: - capman = config.pluginmanager.getplugin("capturemanager") + capman = config.pluginmanager.get_plugin("capturemanager") if capman: capman.suspend_global_capture() diff --git a/src/_pytest/subtests.py b/src/_pytest/subtests.py index 0c66e289a4d..74e585ccb97 100644 --- a/src/_pytest/subtests.py +++ b/src/_pytest/subtests.py @@ -289,7 +289,7 @@ def __exit__( def capturing_output(request: SubRequest) -> Iterator[Captured]: option = request.config.getoption("capture", None) - capman = request.config.pluginmanager.getplugin("capturemanager") + capman = request.config.pluginmanager.get_plugin("capturemanager") if getattr(capman, "_capture_fixture", None): # capsys or capfd are active, subtest should not capture. fixture = None @@ -318,7 +318,7 @@ def capturing_output(request: SubRequest) -> Iterator[Captured]: def capturing_logs( request: SubRequest, ) -> Iterator[CapturedLogs | None]: - logging_plugin: LoggingPlugin | None = request.config.pluginmanager.getplugin( + logging_plugin: LoggingPlugin | None = request.config.pluginmanager.get_plugin( "logging-plugin" ) if logging_plugin is None: diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 95c0f44b7f5..18fe7152329 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -385,7 +385,7 @@ def test_ini_controls_global_log_level(pytester: Pytester) -> None: import pytest import logging def test_log_level_override(request, caplog): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_level == logging.ERROR logger = logging.getLogger('catchlog') logger.warning("WARNING message won't be shown") @@ -413,7 +413,7 @@ def test_can_override_global_log_level(pytester: Pytester) -> None: import logging def test_log_level_override(request, caplog): logger = logging.getLogger('catchlog') - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_level == logging.WARNING logger.info("INFO message won't be shown") @@ -479,7 +479,7 @@ def test_captures_despite_exception(pytester: Pytester) -> None: import logging def test_log_level_override(request, caplog): logger = logging.getLogger('catchlog') - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_level == logging.WARNING logger.error("ERROR message " + "will be shown") @@ -522,7 +522,7 @@ def function_that_logs(): print('Print ' + 'message') def test_that_fails(request, caplog): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_level == logging.INFO with caplog.at_level(logging.DEBUG): diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 165d774435d..bb799055e8f 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -260,7 +260,7 @@ def test_log_cli_default_level(pytester: Pytester) -> None: import pytest import logging def test_log_cli(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_cli_handler.level == logging.NOTSET logging.getLogger('catchlog').info("INFO message won't be shown") logging.getLogger('catchlog').warning("WARNING message will be shown") @@ -551,7 +551,7 @@ def test_log_cli_level(pytester: Pytester) -> None: import pytest import logging def test_log_cli(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_cli_handler.level == logging.INFO logging.getLogger('catchlog').debug("This log message won't be shown") logging.getLogger('catchlog').info("This log message will be shown") @@ -607,7 +607,7 @@ def test_log_cli_ini_level(pytester: Pytester) -> None: import pytest import logging def test_log_cli(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_cli_handler.level == logging.INFO logging.getLogger('catchlog').debug("This log message won't be shown") logging.getLogger('catchlog').info("This log message will be shown") @@ -685,7 +685,7 @@ def test_log_file_cli(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.WARNING logging.getLogger('catchlog').info("This log message won't be shown") logging.getLogger('catchlog').warning("This log message will be shown") @@ -718,7 +718,7 @@ def test_log_file_mode_cli(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.WARNING logging.getLogger('catchlog').info("This log message won't be shown") logging.getLogger('catchlog').warning("This log message will be shown") @@ -758,7 +758,7 @@ def test_log_file_mode_cli_invalid(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.WARNING logging.getLogger('catchlog').info("This log message won't be shown") logging.getLogger('catchlog').warning("This log message will be shown") @@ -785,7 +785,7 @@ def test_log_file_cli_level(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.INFO logging.getLogger('catchlog').debug("This log message won't be shown") logging.getLogger('catchlog').info("This log message will be shown") @@ -836,7 +836,7 @@ def test_log_file_ini(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.WARNING logging.getLogger('catchlog').info("This log message won't be shown") logging.getLogger('catchlog').warning("This log message will be shown") @@ -874,7 +874,7 @@ def test_log_file_mode_ini(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.WARNING logging.getLogger('catchlog').info("This log message won't be shown") logging.getLogger('catchlog').warning("This log message will be shown") @@ -914,7 +914,7 @@ def test_log_file_ini_level(pytester: Pytester) -> None: import pytest import logging def test_log_file(request): - plugin = request.config.pluginmanager.getplugin('logging-plugin') + plugin = request.config.pluginmanager.get_plugin('logging-plugin') assert plugin.log_file_handler.level == logging.INFO logging.getLogger('catchlog').debug("This log message won't be shown") logging.getLogger('catchlog').info("This log message will be shown") diff --git a/testing/python/collect.py b/testing/python/collect.py index dddd15b8f67..940d635f798 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1290,7 +1290,7 @@ def pytest_pycollect_makeitem(collector, name, obj): """ ) item = pytester.getitem("def test_func(): pass") - item.config.pluginmanager.getplugin("runner") + item.config.pluginmanager.get_plugin("runner") assert item.location == ("ABCDE", 42, "custom") def test_func_reportinfo(self, pytester: Pytester) -> None: diff --git a/testing/test_capture.py b/testing/test_capture.py index a0a4f044d62..9a0c6f38814 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1749,7 +1749,7 @@ def test_libedit_workaround(pytester: Pytester) -> None: def pytest_terminal_summary(config): - capture = config.pluginmanager.getplugin("capturemanager") + capture = config.pluginmanager.get_plugin("capturemanager") capture.suspend_global_capture(in_=True) print("Enter 'hi'") diff --git a/testing/test_config.py b/testing/test_config.py index 282e66409f7..8dd33f0537b 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1917,7 +1917,7 @@ def my_dists(): ) monkeypatch.setenv("PYTEST_PLUGINS", "mytestplugin") config = pytester.parseconfig() - plugin = config.pluginmanager.getplugin("mytestplugin") + plugin = config.pluginmanager.get_plugin("mytestplugin") assert plugin.x == 42 @@ -2319,6 +2319,18 @@ class pytest_something: assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None +def test_getplugin_is_deprecated() -> None: + from _pytest.config import PytestPluginManager + + pluginmanager = PytestPluginManager() + + with pytest.warns( + DeprecationWarning, + match=r"PytestPluginManager\.getplugin\(\) is deprecated", + ): + pluginmanager.getplugin("missing_plugin") # type: ignore[deprecated] + + class TestRootdir: def test_simple_noini(self, tmp_path: Path, monkeypatch: MonkeyPatch) -> None: assert get_common_ancestor(Path.cwd(), [tmp_path]) == tmp_path diff --git a/testing/test_debugging.py b/testing/test_debugging.py index 3a67cfa8cd6..85ecb4d1ab1 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -114,7 +114,7 @@ def pdblist(self, request): def mypdb(*args): pdblist.append(args) - plugin = request.config.pluginmanager.getplugin("debugging") + plugin = request.config.pluginmanager.get_plugin("debugging") monkeypatch.setattr(plugin, "post_mortem", mypdb) return pdblist diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index 0cf05c475b5..aaadc7b3be9 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -15,7 +15,7 @@ class TestPasteCapture: @pytest.fixture def pastebinlist(self, monkeypatch, request) -> list[str | bytes]: pastebinlist: list[str | bytes] = [] - plugin = request.config.pluginmanager.getplugin("pastebin") + plugin = request.config.pluginmanager.get_plugin("pastebin") monkeypatch.setattr(plugin, "create_new_paste", pastebinlist.append) return pastebinlist @@ -106,7 +106,7 @@ def test_deprecated(self, pytester: Pytester, pastebinlist) -> None: class TestPaste: @pytest.fixture def pastebin(self, request): - return request.config.pluginmanager.getplugin("pastebin") + return request.config.pluginmanager.get_plugin("pastebin") @pytest.fixture def mocked_urlopen_invalid(self, monkeypatch: MonkeyPatch): diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 21b0557a470..b37651f36a8 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1625,7 +1625,7 @@ def test_terminalreporter_reportopt_addopts(pytester: Pytester) -> None: @pytest.fixture def tr(request): - tr = request.config.pluginmanager.getplugin("terminalreporter") + tr = request.config.pluginmanager.get_plugin("terminalreporter") return tr def test_opt(tr): assert tr.hasopt('skipped') From a76947f268d6ff0f35cff44f73c008311c20c977 Mon Sep 17 00:00:00 2001 From: Gabriele Date: Thu, 15 Oct 2026 22:55:40 -0300 Subject: [PATCH 2/4] remove PytestPluginManager.getplugin --- src/_pytest/config/__init__.py | 7 ------- testing/test_config.py | 12 ------------ 2 files changed, 19 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index a1a8df2b442..e96d9ccf5a4 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -614,13 +614,6 @@ def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None self.consider_module(plugin) return plugin_name - @deprecated( - "PytestPluginManager.getplugin() is deprecated, use get_plugin() instead." - ) - def getplugin(self, name: str): - plugin: _PluggyPlugin | None = self.get_plugin(name) - return plugin - def hasplugin(self, name: str) -> bool: """Return whether a plugin with the given name is registered.""" return bool(self.get_plugin(name)) diff --git a/testing/test_config.py b/testing/test_config.py index 8dd33f0537b..7c40d06b77c 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -2319,18 +2319,6 @@ class pytest_something: assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None -def test_getplugin_is_deprecated() -> None: - from _pytest.config import PytestPluginManager - - pluginmanager = PytestPluginManager() - - with pytest.warns( - DeprecationWarning, - match=r"PytestPluginManager\.getplugin\(\) is deprecated", - ): - pluginmanager.getplugin("missing_plugin") # type: ignore[deprecated] - - class TestRootdir: def test_simple_noini(self, tmp_path: Path, monkeypatch: MonkeyPatch) -> None: assert get_common_ancestor(Path.cwd(), [tmp_path]) == tmp_path From 8f8f2c57bc72acafa7bfa41a23d66b9516fed3f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:04:33 +0000 Subject: [PATCH 3/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/config/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index e96d9ccf5a4..237b7d42ad1 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -84,6 +84,7 @@ from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import warn_explicit_for + if TYPE_CHECKING: from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.cacheprovider import Cache From c6f0a5064df369cd2a19b0d7b025be3624fa765e Mon Sep 17 00:00:00 2001 From: Gabriele Date: Thu, 15 Oct 2026 23:09:17 -0300 Subject: [PATCH 4/4] add changelog for getplugin removal --- changelog/5842.deprecation.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/5842.deprecation.rst diff --git a/changelog/5842.deprecation.rst b/changelog/5842.deprecation.rst new file mode 100644 index 00000000000..8364526a4fc --- /dev/null +++ b/changelog/5842.deprecation.rst @@ -0,0 +1 @@ +The ``PytestPluginManager.getplugin()`` method has been removed. Use ``PytestPluginManager.get_plugin()`` instead.