From f4ba3d49b95587d13ba0f92b5320914e90794b1c Mon Sep 17 00:00:00 2001 From: harshitlarl Date: Sun, 22 Mar 2026 22:42:11 +0530 Subject: [PATCH 1/3] Enhancement: Align pytest.warns regex mismatch error with pytest.raises (#11225) --- src/_pytest/recwarn.py | 14 +++++++++++--- testing/test_recwarn.py | 14 ++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index e3db717bfe4..de1af014d3c 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -321,11 +321,19 @@ def found_str() -> str: f" Emitted warnings: {found_str()}." ) elif not any(self.matches(w) for w in self): - fail( - f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n" - f" Regex: {self.match_expr}\n" + match_pattern = ( + self.match_expr.pattern + if isinstance(self.match_expr, re.Pattern) + else self.match_expr + ) + msg = ( + f"Regex pattern did not match.\n" + f" Regex: {match_pattern!r}\n" f" Emitted warnings: {found_str()}." ) + if any(str(w.message) == match_pattern for w in self): + msg += "\n Did you mean to `re.escape()` the regex?" + fail(msg) finally: # Whether or not any warnings matched, we want to re-emit all unmatched warnings. for w in self: diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 384f2b66a15..0a907b90d80 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -243,7 +243,9 @@ def test_deprecated_call_supports_match(self) -> None: warnings.warn("value must be 42", DeprecationWarning) with pytest.deprecated_call(): - with pytest.raises(pytest.fail.Exception, match="DID NOT WARN"): + with pytest.raises( + pytest.fail.Exception, match="Regex pattern did not match" + ): with pytest.deprecated_call(match=r"must be \d+$"): warnings.warn("this is not here", DeprecationWarning) @@ -406,7 +408,9 @@ def test_match_regex(self) -> None: def test_one_from_multiple_warns(self) -> None: with pytest.warns(): - with pytest.raises(pytest.fail.Exception, match="DID NOT WARN"): + with pytest.raises( + pytest.fail.Exception, match="Regex pattern did not match" + ): with pytest.warns(UserWarning, match=r"aaa"): with pytest.warns(UserWarning, match=r"aaa"): warnings.warn("cccccccccc", UserWarning) @@ -415,7 +419,9 @@ def test_one_from_multiple_warns(self) -> None: def test_none_of_multiple_warns(self) -> None: with pytest.warns(): - with pytest.raises(pytest.fail.Exception, match="DID NOT WARN"): + with pytest.raises( + pytest.fail.Exception, match="Regex pattern did not match" + ): with pytest.warns(UserWarning, match=r"aaa"): warnings.warn("bbbbbbbbbb", UserWarning) warnings.warn("cccccccccc", UserWarning) @@ -589,7 +595,7 @@ def __init__(self, a, b): pass with pytest.warns(CustomWarning): - with pytest.raises(pytest.fail.Exception, match="DID NOT WARN"): + with pytest.raises(pytest.fail.Exception, match="Regex pattern did not match"): with pytest.warns(CustomWarning, match="not gonna match"): a, b = 1, 2 warnings.warn(CustomWarning(a, b)) From 7799f09107c2982eccb529d9d345767905f18204 Mon Sep 17 00:00:00 2001 From: harshitlarl Date: Sun, 22 Mar 2026 22:47:18 +0530 Subject: [PATCH 2/3] Fix doctest and add changelog entry --- changelog/11225.improvement.rst | 2 ++ src/_pytest/recwarn.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog/11225.improvement.rst diff --git a/changelog/11225.improvement.rst b/changelog/11225.improvement.rst new file mode 100644 index 00000000000..23ea0c82b3d --- /dev/null +++ b/changelog/11225.improvement.rst @@ -0,0 +1,2 @@ +Improved the error message for unmatching regex patterns in ``pytest.warns()`` to align with the one used for ``pytest.raises()``. +Also added a hint suggesting ``re.escape()`` if the text exactly matches but the regex compilation fails or lacks escape characters. diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index de1af014d3c..30cc170c957 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -139,7 +139,7 @@ def warns( ... warnings.warn("this is not here", UserWarning) Traceback (most recent call last): ... - Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted... + Failed: Regex pattern did not match... **Using with** ``pytest.mark.parametrize`` From daa067c2f178e37c4841f964b56c0ecfa9b4ada9 Mon Sep 17 00:00:00 2001 From: harshitlarl Date: Mon, 23 Mar 2026 01:09:58 +0530 Subject: [PATCH 3/3] Add tests for recwarn regex mismatch hints to improve codecov --- testing/test_recwarn.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 0a907b90d80..e7bf263c5b5 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -406,6 +406,25 @@ def test_match_regex(self) -> None: with pytest.warns(FutureWarning, match=r"must be \d+$"): warnings.warn("value must be 42", UserWarning) + def test_warns_regex_escape_hint(self) -> None: + with pytest.warns(): + with pytest.raises( + pytest.fail.Exception, + match=r"Did you mean to `re\.escape\(\)` the regex\?", + ): + with pytest.warns(UserWarning, match=r"foo(bar)"): + warnings.warn("foo(bar)", UserWarning) + + def test_warns_compiled_regex_mismatch(self) -> None: + import re + + with pytest.warns(): + with pytest.raises( + pytest.fail.Exception, match="Regex pattern did not match" + ): + with pytest.warns(UserWarning, match=re.compile(r"not gonna match")): + warnings.warn("something else", UserWarning) + def test_one_from_multiple_warns(self) -> None: with pytest.warns(): with pytest.raises(