diff --git a/changelog/7295.trivial.rst b/changelog/7295.trivial.rst new file mode 100644 index 000000000..113a7ee60 --- /dev/null +++ b/changelog/7295.trivial.rst @@ -0,0 +1 @@ +``src/_pytest/config/__init__.py`` now uses the ``warnings`` module to report warnings instead of ``sys.stderr.write``. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 31b73a2c9..45e0c05ea 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -992,7 +992,7 @@ class Config: mode = "plain" else: self._mark_plugins_for_rewrite(hook) - _warn_about_missing_assertion(mode) + self._warn_about_missing_assertion(mode) def _mark_plugins_for_rewrite(self, hook) -> None: """ @@ -1136,7 +1136,12 @@ class Config: def _warn_or_fail_if_strict(self, message: str) -> None: if self.known_args_namespace.strict_config: fail(message, pytrace=False) - sys.stderr.write("WARNING: {}".format(message)) + + from _pytest.warnings import _issue_warning_captured + + _issue_warning_captured( + PytestConfigWarning(message), self.hook, stacklevel=2, + ) def _get_unknown_ini_keys(self) -> List[str]: parser_inicfg = self._parser._inidict @@ -1303,6 +1308,27 @@ class Config: """ (deprecated, use getoption(skip=True)) """ return self.getoption(name, skip=True) + def _warn_about_missing_assertion(self, mode: str) -> None: + if not _assertion_supported(): + from _pytest.warnings import _issue_warning_captured + + warning_text = ( + "assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n" + ) + if mode == "plain": + warning_text = ( + "ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?" + ) + _issue_warning_captured( + PytestConfigWarning(warning_text), self.hook, stacklevel=2, + ) + def _assertion_supported(): try: @@ -1313,24 +1339,6 @@ def _assertion_supported(): return False -def _warn_about_missing_assertion(mode): - if not _assertion_supported(): - if mode == "plain": - sys.stderr.write( - "WARNING: ASSERTIONS ARE NOT EXECUTED" - " and FAILING TESTS WILL PASS. Are you" - " using python -O?" - ) - else: - sys.stderr.write( - "WARNING: assertions not in test modules or" - " plugins will be ignored" - " because assert statements are not executed " - "by the underlying Python interpreter " - "(are you using python -O?)\n" - ) - - def create_terminal_writer(config: Config, *args, **kwargs) -> TerminalWriter: """Create a TerminalWriter instance configured according to the options in the config object. Every code which requires a TerminalWriter object diff --git a/testing/test_assertion.py b/testing/test_assertion.py index ae5e75dbf..5dbae96a0 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1385,11 +1385,21 @@ def test_exception_handling_no_traceback(testdir): @pytest.mark.skipif("'__pypy__' in sys.builtin_module_names") def test_warn_missing(testdir): + testdir.makepyfile("") + + warning_output = [ + "warning :*PytestConfigWarning:*assert statements are not executed*" + ] result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h") - result.stderr.fnmatch_lines(["*WARNING*assert statements are not executed*"]) + result.stdout.fnmatch_lines(warning_output) + + warning_output = [ + "=*= warnings summary =*=", + "*PytestConfigWarning:*assert statements are not executed*", + ] result = testdir.run(sys.executable, "-OO", "-m", "pytest") - result.stderr.fnmatch_lines(["*WARNING*assert statements are not executed*"]) + result.stdout.fnmatch_lines(warning_output) def test_recursion_source_decode(testdir): diff --git a/testing/test_config.py b/testing/test_config.py index c9eea7a16..930366232 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -163,7 +163,7 @@ class TestParseIni: assert result.ret == 0 @pytest.mark.parametrize( - "ini_file_text, invalid_keys, stderr_output, exception_text", + "ini_file_text, invalid_keys, warning_output, exception_text", [ ( """ @@ -173,8 +173,9 @@ class TestParseIni: """, ["unknown_ini", "another_unknown_ini"], [ - "WARNING: Unknown config ini key: another_unknown_ini", - "WARNING: Unknown config ini key: unknown_ini", + "=*= warnings summary =*=", + "*PytestConfigWarning:*Unknown config ini key: another_unknown_ini", + "*PytestConfigWarning:*Unknown config ini key: unknown_ini", ], "Unknown config ini key: another_unknown_ini", ), @@ -185,7 +186,10 @@ class TestParseIni: minversion = 5.0.0 """, ["unknown_ini"], - ["WARNING: Unknown config ini key: unknown_ini"], + [ + "=*= warnings summary =*=", + "*PytestConfigWarning:*Unknown config ini key: unknown_ini", + ], "Unknown config ini key: unknown_ini", ), ( @@ -220,7 +224,7 @@ class TestParseIni: ], ) def test_invalid_ini_keys( - self, testdir, ini_file_text, invalid_keys, stderr_output, exception_text + self, testdir, ini_file_text, invalid_keys, warning_output, exception_text ): testdir.makeconftest( """ @@ -234,7 +238,7 @@ class TestParseIni: assert sorted(config._get_unknown_ini_keys()) == sorted(invalid_keys) result = testdir.runpytest() - result.stderr.fnmatch_lines(stderr_output) + result.stdout.fnmatch_lines(warning_output) if exception_text: with pytest.raises(pytest.fail.Exception, match=exception_text):