diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 6f34c1b93fd..9a59af5b9d5 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -585,11 +585,14 @@ def filtering(self, filter_: logging.Filter) -> Generator[None]: .. versionadded:: 7.5 """ - self.handler.addFilter(filter_) + filter_was_present = filter_ in self.handler.filters + if not filter_was_present: + self.handler.addFilter(filter_) try: yield finally: - self.handler.removeFilter(filter_) + if not filter_was_present: + self.handler.removeFilter(filter_) @fixture diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 5f94cb8508a..fbf2d45e6ae 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -206,6 +206,25 @@ def filter(self, record: logging.LogRecord) -> bool: assert unfiltered_tuple == ("test_fixture", 20, "handler call") +def test_with_statement_filtering_nested_same_filter( + caplog: pytest.LogCaptureFixture, +) -> None: + class NoCaptureFilter(logging.Filter): + def filter(self, _record: logging.LogRecord) -> bool: + return False + + filter_obj = NoCaptureFilter() + + with caplog.at_level(logging.INFO): + with caplog.filtering(filter_obj): + logger.info("Will not be captured") + with caplog.filtering(filter_obj): + logger.info("Will also not be captured") + logger.info("Will still not be captured") + + assert caplog.records == [] + + @pytest.mark.parametrize( "level_str,expected_disable_level", [