recwarn: let base exceptions propagate through `pytest.warns` again

Fix #11907.
This commit is contained in:
Ran Benita 2024-02-04 22:25:49 +02:00
parent 5d7a5a9343
commit 288201b8f5
3 changed files with 81 additions and 2 deletions

View File

@ -0,0 +1 @@
Fix a regression in pytest 8.0.0 whereby calling :func:`pytest.skip` and similar control-flow exceptions within a :func:`pytest.warns()` block would get suppressed instead of propagating.

View File

@ -20,6 +20,7 @@ import warnings
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.outcomes import Exit
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -302,6 +303,17 @@ class WarningsChecker(WarningsRecorder):
__tracebackhide__ = True __tracebackhide__ = True
# BaseExceptions like pytest.{skip,fail,xfail,exit} or Ctrl-C within
# pytest.warns should *not* trigger "DID NOT WARN" and get suppressed
# when the warning doesn't happen. Control-flow exceptions should always
# propagate.
if exc_val is not None and (
not isinstance(exc_val, Exception)
# Exit is an Exception, not a BaseException, for some reason.
or isinstance(exc_val, Exit)
):
return
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)

View File

@ -5,9 +5,10 @@ from typing import Optional
from typing import Type from typing import Type
import warnings import warnings
from _pytest.pytester import Pytester
from _pytest.recwarn import WarningsRecorder
import pytest import pytest
from pytest import ExitCode
from pytest import Pytester
from pytest import WarningsRecorder
def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None: def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None:
@ -479,6 +480,71 @@ class TestWarns:
warnings.warn("some warning", category=FutureWarning) warnings.warn("some warning", category=FutureWarning)
raise ValueError("some exception") raise ValueError("some exception")
def test_skip_within_warns(self, pytester: Pytester) -> None:
"""Regression test for #11907."""
pytester.makepyfile(
"""
import pytest
def test_it():
with pytest.warns(Warning):
pytest.skip("this is OK")
""",
)
result = pytester.runpytest()
assert result.ret == ExitCode.OK
result.assert_outcomes(skipped=1)
def test_fail_within_warns(self, pytester: Pytester) -> None:
"""Regression test for #11907."""
pytester.makepyfile(
"""
import pytest
def test_it():
with pytest.warns(Warning):
pytest.fail("BOOM")
""",
)
result = pytester.runpytest()
assert result.ret == ExitCode.TESTS_FAILED
result.assert_outcomes(failed=1)
assert "DID NOT WARN" not in str(result.stdout)
def test_exit_within_warns(self, pytester: Pytester) -> None:
"""Regression test for #11907."""
pytester.makepyfile(
"""
import pytest
def test_it():
with pytest.warns(Warning):
pytest.exit()
""",
)
result = pytester.runpytest()
assert result.ret == ExitCode.INTERRUPTED
result.assert_outcomes()
def test_keyboard_interrupt_within_warns(self, pytester: Pytester) -> None:
"""Regression test for #11907."""
pytester.makepyfile(
"""
import pytest
def test_it():
with pytest.warns(Warning):
raise KeyboardInterrupt()
""",
)
result = pytester.runpytest_subprocess()
assert result.ret == ExitCode.INTERRUPTED
result.assert_outcomes()
def test_raise_type_error_on_non_string_warning() -> None: def test_raise_type_error_on_non_string_warning() -> None:
"""Check pytest.warns validates warning messages are strings (#10865).""" """Check pytest.warns validates warning messages are strings (#10865)."""