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 @@ -209,6 +209,7 @@ Israël Hallé
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
JaeHyuck Sa
Jake VanderPlas
Jakob van Santen
Jakub Mitoraj
Expand Down
1 change: 1 addition & 0 deletions changelog/11225.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`pytest.warns` showed "Regex pattern did not match" instead of "DID NOT WARN" when warnings were emitted but the ``match`` pattern did not match.
4 changes: 3 additions & 1 deletion doc/en/how-to/capture-warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,9 @@ Some examples:
...
Traceback (most recent call last):
...
Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted...
Failed: Regex pattern did not match.
Regex: ...
Emitted warnings: ...UserWarning...

>>> with warns(UserWarning, match=re.escape("issue with foo() func")):
... warnings.warn("issue with foo() func")
Expand Down
17 changes: 13 additions & 4 deletions src/_pytest/recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ 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.
Regex: ...
Emitted warnings: ...UserWarning...

**Using with** ``pytest.mark.parametrize``

Expand Down Expand Up @@ -321,10 +323,17 @@ def found_str() -> str:
f" Emitted warnings: {found_str()}."
)
elif not any(self.matches(w) for w in self):
escape_hint = ""
if isinstance(self.match_expr, str) and any(
self.match_expr == str(w.message)
for w in self
if issubclass(w.category, self.expected_warning)
):
escape_hint = "\n Did you mean to `re.escape()` the regex?"
fail(
f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n"
f" Regex: {self.match_expr}\n"
f" Emitted warnings: {found_str()}."
"Regex pattern did not match.\n"
f" Regex: {self.match_expr!r}\n"
f" Emitted warnings: {found_str()}.{escape_hint}"
)
finally:
# Whether or not any warnings matched, we want to re-emit all unmatched warnings.
Expand Down
39 changes: 35 additions & 4 deletions testing/test_recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -415,11 +419,38 @@ 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)

def test_warns_match_failure_message_detail(self) -> None:
with pytest.warns():
with pytest.raises(pytest.fail.Exception) as excinfo:
with pytest.warns(UserWarning, match=r"must be \d+$"):
warnings.warn("this is not here", UserWarning)
msg = str(excinfo.value)
assert "Regex pattern did not match" in msg
assert "this is not here" in msg
assert "DID NOT WARN" not in msg

def test_warns_match_re_escape_hint(self) -> None:
with pytest.warns():
with pytest.raises(pytest.fail.Exception) as excinfo:
with pytest.warns(UserWarning, match="foo (bar)"):
warnings.warn("foo (bar)", UserWarning)
assert "re.escape()" in str(excinfo.value)

def test_warns_match_re_escape_hint_no_false_positive(self) -> None:
with pytest.warns():
with pytest.raises(pytest.fail.Exception) as excinfo:
with pytest.warns(DeprecationWarning, match="foo (bar)"):
warnings.warn("some deprecation msg", DeprecationWarning)
warnings.warn("foo (bar)", UserWarning)
assert "re.escape()" not in str(excinfo.value)

@pytest.mark.filterwarnings("ignore")
def test_can_capture_previously_warned(self) -> None:
def f() -> int:
Expand Down Expand Up @@ -589,7 +620,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))
Loading