Emit unmatched warnings from pytest.warns()
This commit is contained in:
parent
a50ea1b8e7
commit
9279ea2882
1
AUTHORS
1
AUTHORS
|
@ -311,6 +311,7 @@ Raphael Pierzina
|
||||||
Rafal Semik
|
Rafal Semik
|
||||||
Raquel Alegre
|
Raquel Alegre
|
||||||
Ravi Chandra
|
Ravi Chandra
|
||||||
|
Reagan Lee
|
||||||
Robert Holt
|
Robert Holt
|
||||||
Roberto Aldera
|
Roberto Aldera
|
||||||
Roberto Polli
|
Roberto Polli
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Set :func:`warns` to re-emit unmatched warnings when the context closes
|
|
@ -149,6 +149,10 @@ def warns( # noqa: F811
|
||||||
This could be achieved in the same way as with exceptions, see
|
This could be achieved in the same way as with exceptions, see
|
||||||
:ref:`parametrizing_conditional_raising` for an example.
|
:ref:`parametrizing_conditional_raising` for an example.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Unlike the stdlib :func:`warnings.catch_warnings` context manager,
|
||||||
|
unmatched warnings will be re-emitted when the context closes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
if not args:
|
if not args:
|
||||||
|
@ -290,6 +294,32 @@ class WarningsChecker(WarningsRecorder):
|
||||||
def found_str():
|
def found_str():
|
||||||
return pformat([record.message for record in self], indent=2)
|
return pformat([record.message for record in self], indent=2)
|
||||||
|
|
||||||
|
def re_emit() -> None:
|
||||||
|
for r in self:
|
||||||
|
if matches(r):
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert issubclass(r.message.__class__, Warning)
|
||||||
|
|
||||||
|
warnings.warn_explicit(
|
||||||
|
str(r.message),
|
||||||
|
r.message.__class__,
|
||||||
|
r.filename,
|
||||||
|
r.lineno,
|
||||||
|
module=r.__module__,
|
||||||
|
source=r.source,
|
||||||
|
)
|
||||||
|
|
||||||
|
def matches(warning) -> bool:
|
||||||
|
if self.expected_warning is not None:
|
||||||
|
if issubclass(warning.category, self.expected_warning):
|
||||||
|
if self.match_expr is not None:
|
||||||
|
if re.compile(self.match_expr).search(str(warning.message)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
# only check if we're not currently handling an exception
|
# only check if we're not currently handling an exception
|
||||||
if exc_type is None and exc_val is None and exc_tb is None:
|
if exc_type is None and exc_val is None and exc_tb is None:
|
||||||
if self.expected_warning is not None:
|
if self.expected_warning is not None:
|
||||||
|
@ -303,6 +333,7 @@ class WarningsChecker(WarningsRecorder):
|
||||||
for r in self:
|
for r in self:
|
||||||
if issubclass(r.category, self.expected_warning):
|
if issubclass(r.category, self.expected_warning):
|
||||||
if re.compile(self.match_expr).search(str(r.message)):
|
if re.compile(self.match_expr).search(str(r.message)):
|
||||||
|
re_emit()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
fail(
|
fail(
|
||||||
|
@ -311,3 +342,5 @@ DID NOT WARN. No warnings of type {self.expected_warning} matching the regex wer
|
||||||
Regex: {self.match_expr}
|
Regex: {self.match_expr}
|
||||||
Emitted warnings: {found_str()}"""
|
Emitted warnings: {found_str()}"""
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
re_emit()
|
||||||
|
|
|
@ -376,10 +376,12 @@ class TestWarns:
|
||||||
warnings.warn("value must be 42", UserWarning)
|
warnings.warn("value must be 42", UserWarning)
|
||||||
|
|
||||||
def test_one_from_multiple_warns(self) -> None:
|
def test_one_from_multiple_warns(self) -> None:
|
||||||
with pytest.warns(UserWarning, match=r"aaa"):
|
with pytest.raises(pytest.fail.Exception):
|
||||||
warnings.warn("cccccccccc", UserWarning)
|
with pytest.warns(UserWarning, match=r"aaa"):
|
||||||
warnings.warn("bbbbbbbbbb", UserWarning)
|
with pytest.warns(UserWarning, match=r"aaa"):
|
||||||
warnings.warn("aaaaaaaaaa", UserWarning)
|
warnings.warn("cccccccccc", UserWarning)
|
||||||
|
warnings.warn("bbbbbbbbbb", UserWarning)
|
||||||
|
warnings.warn("aaaaaaaaaa", UserWarning)
|
||||||
|
|
||||||
def test_none_of_multiple_warns(self) -> None:
|
def test_none_of_multiple_warns(self) -> None:
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(pytest.fail.Exception):
|
||||||
|
@ -403,3 +405,33 @@ class TestWarns:
|
||||||
with pytest.warns(UserWarning, foo="bar"): # type: ignore
|
with pytest.warns(UserWarning, foo="bar"): # type: ignore
|
||||||
pass
|
pass
|
||||||
assert "Unexpected keyword arguments" in str(excinfo.value)
|
assert "Unexpected keyword arguments" in str(excinfo.value)
|
||||||
|
|
||||||
|
def test_re_emit_single(self) -> None:
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
with pytest.warns(UserWarning):
|
||||||
|
warnings.warn("user warning", UserWarning)
|
||||||
|
warnings.warn("some deprecation warning", DeprecationWarning)
|
||||||
|
|
||||||
|
def test_re_emit_multiple(self) -> None:
|
||||||
|
with pytest.warns(UserWarning):
|
||||||
|
warnings.warn("first warning", UserWarning)
|
||||||
|
warnings.warn("second warning", UserWarning)
|
||||||
|
|
||||||
|
def test_re_emit_match_single(self) -> None:
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
with pytest.warns(UserWarning, match="user warning"):
|
||||||
|
warnings.warn("user warning", UserWarning)
|
||||||
|
warnings.warn("some deprecation warning", DeprecationWarning)
|
||||||
|
|
||||||
|
def test_re_emit_match_multiple(self) -> None:
|
||||||
|
# with pytest.warns(UserWarning):
|
||||||
|
with pytest.warns(UserWarning, match="user warning"):
|
||||||
|
warnings.warn("first user warning", UserWarning)
|
||||||
|
warnings.warn("second user warning", UserWarning)
|
||||||
|
|
||||||
|
def test_re_emit_non_match_single(self) -> None:
|
||||||
|
# with pytest.warns(UserWarning):
|
||||||
|
with pytest.warns(UserWarning, match="v2 warning"):
|
||||||
|
with pytest.warns(UserWarning, match="v1 warning"):
|
||||||
|
warnings.warn("v1 warning", UserWarning)
|
||||||
|
warnings.warn("non-matching v2 warning", UserWarning)
|
||||||
|
|
Loading…
Reference in New Issue