diff --git a/changelog/14189.bugfix.rst b/changelog/14189.bugfix.rst new file mode 100644 index 00000000000..cdb0680e35a --- /dev/null +++ b/changelog/14189.bugfix.rst @@ -0,0 +1 @@ +Nested usage of :meth:`caplog.filtering ` no longer removes filters early if they were already present. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 3204d43ee01..5b7092375b0 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -600,11 +600,15 @@ def filtering(self, filter_: logging.Filter) -> Generator[None]: .. versionadded:: 7.5 """ - self.handler.addFilter(filter_) - try: + already_present = filter_ in self.handler.filters + if already_present: yield - finally: - self.handler.removeFilter(filter_) + else: + try: + self.handler.addFilter(filter_) + yield + finally: + self.handler.removeFilter(filter_) @fixture diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index c98b7d84258..95c0f44b7f5 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -206,6 +206,40 @@ def filter(self, record: logging.LogRecord) -> bool: assert unfiltered_tuple == ("test_fixture", 20, "handler call") +class DropAllFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + return False + + +def test_with_statement_nested_filtering(caplog: pytest.LogCaptureFixture) -> None: + drop_all = DropAllFilter() + + with caplog.filtering(drop_all): + logger.warning("Will not be captured") + with caplog.filtering(drop_all): + logger.warning("Will also not be captured") + logger.warning("Should not be captured either") + + assert caplog.records == [] + + +def test_with_statement_filtering_already_present( + caplog: pytest.LogCaptureFixture, +) -> None: + drop_all = DropAllFilter() + + caplog.handler.addFilter(drop_all) + try: + with caplog.filtering(drop_all): + logger.warning("Should not be captured") + + # After context manager, filter should STILL be present because it was already there + logger.warning("Should still not be captured") + assert caplog.records == [] + finally: + caplog.handler.removeFilter(drop_all) + + @pytest.mark.parametrize( "level_str,expected_disable_level", [