From 6cf4358aba91aeef6961846e851436449dfc724f Mon Sep 17 00:00:00 2001 From: ace2016 <23010364+ace2016@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:01:46 +0100 Subject: [PATCH 1/2] Warn when cmdline_main raises SystemExit --- changelog/8986.bugfix.rst | 2 ++ src/_pytest/config/__init__.py | 10 +++++++++- testing/test_config.py | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 changelog/8986.bugfix.rst diff --git a/changelog/8986.bugfix.rst b/changelog/8986.bugfix.rst new file mode 100644 index 00000000000..e1c6544ded1 --- /dev/null +++ b/changelog/8986.bugfix.rst @@ -0,0 +1,2 @@ +Warn when a plugin raises :class:`SystemExit` from the +:hook:`pytest_cmdline_main` hook. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index e0038e26ce6..06046f50f16 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -234,7 +234,15 @@ def _main( return ExitCode.USAGE_ERROR try: - ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) + try: + ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) + except SystemExit: + warnings.warn( + PytestConfigWarning( + "A plugin raised SystemExit from the pytest_cmdline_main hook" + ) + ) + raise try: return ExitCode(ret) except ValueError: diff --git a/testing/test_config.py b/testing/test_config.py index 1d2aceb0d99..4ad00aab32b 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -32,6 +32,7 @@ from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import absolutepath from _pytest.pytester import Pytester +from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import PytestDeprecationWarning import pytest @@ -852,6 +853,20 @@ def test_absolute_win32_path(self, pytester: Pytester) -> None: class TestConfigAPI: + @pytest.mark.parametrize("code", [0, 42]) + def test_cmdline_main_system_exit(self, code: int) -> None: + class Plugin: + def pytest_cmdline_main(self, config: Config) -> None: + raise SystemExit(code) + + with pytest.warns( + PytestConfigWarning, + match="plugin raised SystemExit from the pytest_cmdline_main hook", + ), pytest.raises(SystemExit) as exc_info: + pytest.main([], plugins=[Plugin()]) + + assert exc_info.value.code == code + def test_config_trace(self, pytester: Pytester) -> None: config = pytester.parseconfig() values: list[str] = [] From 28b598c4d98ef3fdb4ebef1fcd0c14bf86d4b683 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Aug 2026 18:19:10 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_config.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/testing/test_config.py b/testing/test_config.py index 4ad00aab32b..5645d2b5233 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -859,10 +859,13 @@ class Plugin: def pytest_cmdline_main(self, config: Config) -> None: raise SystemExit(code) - with pytest.warns( - PytestConfigWarning, - match="plugin raised SystemExit from the pytest_cmdline_main hook", - ), pytest.raises(SystemExit) as exc_info: + with ( + pytest.warns( + PytestConfigWarning, + match="plugin raised SystemExit from the pytest_cmdline_main hook", + ), + pytest.raises(SystemExit) as exc_info, + ): pytest.main([], plugins=[Plugin()]) assert exc_info.value.code == code