Skip to content

Commit 9454fc3

Browse files
authored
closes: 10865 Fix muted exception (#11804)
* feat: 10865 * feat: 10865 refactor code and tests * feat: 10865 add test skip for pypy * feat: 10865 add test with valid warning * feat: 10865 fix v2 for codecov * feat: 10865 fix conflict
1 parent 101328a commit 9454fc3

File tree

4 files changed

+41
-0
lines changed

4 files changed

+41
-0
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ Vivaan Verma
416416
Vlad Dragos
417417
Vlad Radziuk
418418
Vladyslav Rachek
419+
Volodymyr Kochetkov
419420
Volodymyr Piskun
420421
Wei Lin
421422
Wil Cooley

changelog/10865.improvement.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`pytest.warns` now validates that warning object's ``message`` is of type `str` -- currently in Python it is possible to pass other types than `str` when creating `Warning` instances, however this causes an exception when :func:`warnings.filterwarnings` is used to filter those warnings. See `CPython #103577 <https://github.com/python/cpython/issues/103577>`__ for a discussion.
2+
While this can be considered a bug in CPython, we decided to put guards in pytest as the error message produced without this check in place is confusing.

src/_pytest/recwarn.py

+11
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,14 @@ def found_str():
329329
module=w.__module__,
330330
source=w.source,
331331
)
332+
# Check warnings has valid argument type (#10865).
333+
wrn: warnings.WarningMessage
334+
for wrn in self:
335+
self._validate_message(wrn)
336+
337+
@staticmethod
338+
def _validate_message(wrn: Any) -> None:
339+
if not isinstance(msg := wrn.message.args[0], str):
340+
raise TypeError(
341+
f"Warning message must be str, got {msg!r} (type {type(msg).__name__})"
342+
)

testing/test_recwarn.py

+27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# mypy: allow-untyped-defs
2+
import sys
23
from typing import List
34
from typing import Optional
45
from typing import Type
@@ -477,3 +478,29 @@ def test_catch_warning_within_raise(self) -> None:
477478
with pytest.raises(ValueError, match="some exception"):
478479
warnings.warn("some warning", category=FutureWarning)
479480
raise ValueError("some exception")
481+
482+
483+
def test_raise_type_error_on_non_string_warning() -> None:
484+
"""Check pytest.warns validates warning messages are strings (#10865)."""
485+
with pytest.raises(TypeError, match="Warning message must be str"):
486+
with pytest.warns(UserWarning):
487+
warnings.warn(1) # type: ignore
488+
489+
490+
def test_no_raise_type_error_on_string_warning() -> None:
491+
"""Check pytest.warns validates warning messages are strings (#10865)."""
492+
with pytest.warns(UserWarning):
493+
warnings.warn("Warning")
494+
495+
496+
@pytest.mark.skipif(
497+
hasattr(sys, "pypy_version_info"),
498+
reason="Not for pypy",
499+
)
500+
def test_raise_type_error_on_non_string_warning_cpython() -> None:
501+
# Check that we get the same behavior with the stdlib, at least if filtering
502+
# (see https://github.com/python/cpython/issues/103577 for details)
503+
with pytest.raises(TypeError):
504+
with warnings.catch_warnings():
505+
warnings.filterwarnings("ignore", "test")
506+
warnings.warn(1) # type: ignore

0 commit comments

Comments
 (0)