Skip to content

Commit 7031cbd

Browse files
committed
Allow using warnings.warn() with Warnings
`pytest.warns()` now allows using `warnings.warn()` with a `Warning` instance, as is required by Python, with one exception. If the warning used is a `UserWarning` that was created by passing it arguments and the first argument was not a `str` then `pytest.raises()` still considers that an error. This is because if an invalid type was used in `warnings.warn()` then Python creates a `UserWarning` anyways and it becomes impossible for `pytest` to figure out if that was done automatically or not.
1 parent 898f8f0 commit 7031cbd

File tree

4 files changed

+22
-4
lines changed

4 files changed

+22
-4
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ Edison Gustavo Muenz
127127
Edoardo Batini
128128
Edson Tadeu M. Manoel
129129
Eduardo Schettino
130+
Eero Vaher
130131
Eli Boyarski
131132
Elizaveta Shashkova
132133
Éloi Rivard

changelog/10865.improvement.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
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.
1+
:func:`pytest.warns` now validates that :func:`warnings.warn` was called with a
2+
`str` or a `Warning`.
3+
Currently in Python it is possible to use other types, however this causes an
4+
exception when :func:`warnings.filterwarnings` is used to filter those warnings
5+
(see `CPython #103577 <https://github.com/python/cpython/issues/103577>`__ for
6+
a discussion).
7+
While this can be considered a bug in CPython, we decided to put guards in
8+
pytest as the error message produced without this check in place is confusing.

src/_pytest/recwarn.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,18 @@ def found_str():
336336

337337
@staticmethod
338338
def _validate_message(wrn: Any) -> None:
339+
if isinstance(wrn.message, Warning) and type(wrn.message) is not UserWarning:
340+
# If the warning was of an incorrect type then Python creates a UserWarning.
341+
# Any other warning must have been specified explicitly.
342+
return
343+
if not wrn.message.args:
344+
# UserWarning() without arguments must have been specified explicitly.
345+
return
339346
if not isinstance(msg := wrn.message.args[0], str):
347+
# It is possible that UserWarning was explicitly specified, but its
348+
# first argument was not a string, but that case can't be
349+
# distinguished from an invalid type.
340350
raise TypeError(
341-
f"Warning message must be str, got {msg!r} (type {type(msg).__name__})"
351+
"Warning must be str or Warning, got "
352+
f"{msg!r} (type {type(msg).__name__})"
342353
)

testing/test_recwarn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ def test_catch_warning_within_raise(self) -> None:
483483
def test_raise_type_error_on_invalid_warning() -> None:
484484
"""Check pytest.warns validates warning messages are strings (#10865) or
485485
Warning instances (#11959)."""
486-
with pytest.raises(TypeError, match="Warning message must be str"):
486+
with pytest.raises(TypeError, match="Warning must be str or Warning"):
487487
with pytest.warns(UserWarning):
488488
warnings.warn(1) # type: ignore
489489

0 commit comments

Comments
 (0)