Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ croc100
Cal Leeming
Carl Friedrich Bolz
Carlos Jenkins
Cen Fangyu (Dmao233)
Ceridwen
Charles Cloud
Charles Machalow
Expand Down
1 change: 1 addition & 0 deletions changelog/14953.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The :confval:`max_warnings` configuration option now accepts integer values in TOML configuration files, while still accepting strings for backward compatibility.
4 changes: 2 additions & 2 deletions doc/en/reference/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1705,7 +1705,7 @@ passed multiple times. The expected format is ``name=value``. For example::


.. confval:: max_warnings
:type: ``int``
:type: ``int | str``

.. versionadded:: 9.1

Expand Down Expand Up @@ -3671,7 +3671,7 @@ All the command-line flags can also be obtained by running ``pytest --help``::
Each line specifies a pattern for
warnings.filterwarnings. Processed after
-W/--pythonwarnings.
max_warnings (string):
max_warnings (int | string):
Exit with error if all tests pass but the number of
warnings exceeds this threshold
norecursedirs (args): Directory patterns to avoid for recursion
Expand Down
4 changes: 4 additions & 0 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,13 @@ def pytest_addoption(parser: Parser) -> None:
"warnings.filterwarnings. "
"Processed after -W/--pythonwarnings.",
)
# ``int | str`` (not plain ``int``) for backward compatibility: INI files
# and ``-o`` overrides provide the value as a string.
parser.addini(
"max_warnings",
help="Exit with error if all tests pass but the number of warnings exceeds this threshold",
type=int | str,
default=None,
)

group = parser.getgroup("collect", "collection")
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1093,9 +1093,9 @@ def _get_max_warnings(self) -> int | None:
if value is not None:
return int(value)
ini_value = self.config.getini("max_warnings")
if ini_value:
return int(ini_value)
return None
if ini_value is None:
return None
return int(ini_value)

#
# Summaries for sessionfinish.
Expand Down
96 changes: 96 additions & 0 deletions testing/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,12 @@ def test_two():
warnings.warn(UserWarning("warning two"))
"""

ONE_WARNING_PYFILE = """
import warnings
def test_warning():
warnings.warn(UserWarning("example warning"))
"""

@pytest.mark.filterwarnings("default::UserWarning")
def test_max_warnings_not_set(self, pytester: Pytester) -> None:
"""Without --max-warnings, warnings don't affect exit code."""
Expand Down Expand Up @@ -1145,6 +1151,96 @@ def test_one():
result.assert_outcomes(passed=1, warnings=1)
assert result.ret == ExitCode.OK

@pytest.mark.filterwarnings("default::UserWarning")
@pytest.mark.parametrize(
("writer", "source"),
[
pytest.param(
"toml",
"""
[pytest]
max_warnings = 0
""",
id="pytest-toml-int",
),
pytest.param(
"toml",
"""
[pytest]
max_warnings = "0"
""",
id="pytest-toml-string",
),
pytest.param(
"pyproject",
"""
[tool.pytest]
max_warnings = 0
""",
id="pyproject-native-int",
),
pytest.param(
"pyproject",
"""
[tool.pytest]
max_warnings = "0"
""",
id="pyproject-native-string",
),
pytest.param(
"ini",
"""
[pytest]
max_warnings = 0
""",
id="pytest-ini",
),
],
)
def test_max_warnings_config_accepts_int_and_string(
self, pytester: Pytester, writer: str, source: str
) -> None:
"""Native TOML integers and quoted/INI strings are accepted (#14953).

An explicit zero must be distinguished from the unset default.
"""
if writer == "toml":
pytester.maketoml(source)
elif writer == "pyproject":
pytester.makepyprojecttoml(source)
else:
assert writer == "ini"
pytester.makeini(source)
pytester.makepyfile(self.ONE_WARNING_PYFILE)
result = pytester.runpytest()
assert result.ret == ExitCode.MAX_WARNINGS_ERROR
result.stdout.fnmatch_lines(
["*Tests pass, but maximum allowed warnings exceeded: 1 > 0*"]
)

@pytest.mark.filterwarnings("default::UserWarning")
def test_max_warnings_override_ini(self, pytester: Pytester) -> None:
"""-o max_warnings=0 is accepted and treated as an explicit zero (#14953)."""
pytester.makepyfile(self.ONE_WARNING_PYFILE)
result = pytester.runpytest("-o", "max_warnings=0")
assert result.ret == ExitCode.MAX_WARNINGS_ERROR
result.stdout.fnmatch_lines(
["*Tests pass, but maximum allowed warnings exceeded: 1 > 0*"]
)

@pytest.mark.filterwarnings("default::UserWarning")
def test_max_warnings_cli_overrides_ini(self, pytester: Pytester) -> None:
"""--max-warnings takes precedence over the configuration value."""
pytester.makeini(
"""
[pytest]
max_warnings = 10
"""
)
pytester.makepyfile(self.PYFILE)
result = pytester.runpytest("--max-warnings", "0")
assert result.ret == ExitCode.MAX_WARNINGS_ERROR


def test_pythonwarnings_not_duplicated(pytester: Pytester) -> None:
"""Regression test for #13484: -W values should not be duplicated in
Expand Down