From f0829f826df56531c5fbded6f303c8350648ec82 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Thu, 25 Dec 2025 21:28:32 +0000 Subject: [PATCH 1/2] fix(logging): restore caplog handler level Refs #35 --- src/_pytest/logging.py | 2 ++ testing/logging/test_fixture.py | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 11031f2f229..efd58e19a35 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -673,6 +673,8 @@ def pytest_runtest_logreport(self) -> None: def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]: """Implements the internals of pytest_runtest_xxx() hook.""" + if self.log_level is None: + self.caplog_handler.setLevel(logging.NOTSET) with catching_logs( self.caplog_handler, level=self.log_level, ) as caplog_handler, catching_logs( diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index da5303302b6..6cae17a653f 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -236,6 +236,66 @@ def test_log_level_override(request, caplog): assert result.ret == 1 +def test_caplog_handler_level_restored_between_tests(testdir): + testdir.makepyfile( + """ + import logging + + def test_sets_custom_level(caplog): + caplog.set_level(42) + assert caplog.handler.level == 42 + + def test_handler_level_restored(caplog): + assert caplog.handler.level == logging.NOTSET + """ + ) + + result = testdir.runpytest() + result.assert_outcomes(passed=2) + + +def test_caplog_logger_specific_override_restores_handler_level(testdir): + testdir.makepyfile( + """ + import logging + + def test_override_specific_logger(caplog): + logger = logging.getLogger('example') + caplog.set_level(logging.WARNING, logger=logger.name) + logger.warning('message') + assert caplog.handler.level == logging.WARNING + assert [record.levelno for record in caplog.records] == [logging.WARNING] + + def test_handler_level_after_logger_override(caplog): + assert caplog.handler.level == logging.NOTSET + """ + ) + + result = testdir.runpytest() + result.assert_outcomes(passed=2) + + +def test_caplog_nested_set_level_restores_handler(testdir): + testdir.makepyfile( + """ + import logging + + def test_nested_set_level(caplog): + caplog.set_level(logging.INFO) + caplog.set_level(logging.DEBUG) + with caplog.at_level(logging.WARNING): + logging.getLogger().warning('inside') + assert caplog.handler.level == logging.DEBUG + + def test_handler_level_after_nested_set_level(caplog): + assert caplog.handler.level == logging.NOTSET + """ + ) + + result = testdir.runpytest() + result.assert_outcomes(passed=2) + + def test_log_report_captures_according_to_config_option_upon_failure(testdir): """ Test that upon failure: (1) `caplog` succeeded to capture the DEBUG message and assert on it => No `Exception` is raised From 81c21efe951a5177281b52a0bb10530400552184 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Thu, 25 Dec 2025 21:34:17 +0000 Subject: [PATCH 2/2] fix(logging): keep fixture caplog level active Refs #35 --- src/_pytest/logging.py | 2 +- testing/logging/test_fixture.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index efd58e19a35..50dcf4aa1a9 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -673,7 +673,7 @@ def pytest_runtest_logreport(self) -> None: def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]: """Implements the internals of pytest_runtest_xxx() hook.""" - if self.log_level is None: + if when == "setup" and self.log_level is None: self.caplog_handler.setLevel(logging.NOTSET) with catching_logs( self.caplog_handler, level=self.log_level, diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 6cae17a653f..bd66c85472b 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -296,6 +296,37 @@ def test_handler_level_after_nested_set_level(caplog): result.assert_outcomes(passed=2) +def test_caplog_fixture_setup_level_reaches_test_body(testdir): + testdir.makepyfile( + """ + import logging + import pytest + + + @pytest.fixture + def info_level(caplog): + caplog.set_level(logging.INFO) + + + def test_uses_fixture_level(caplog, info_level): + logger = logging.getLogger() + logger.debug('hidden debug message') + logger.info('visible info message') + assert caplog.handler.level == logging.INFO + assert [record.levelno for record in caplog.records] == [logging.INFO] + assert 'hidden debug message' not in caplog.text + assert 'visible info message' in caplog.text + + + def test_handler_level_restored_after_fixture(caplog): + assert caplog.handler.level == logging.NOTSET + """ + ) + + result = testdir.runpytest() + result.assert_outcomes(passed=2) + + def test_log_report_captures_according_to_config_option_upon_failure(testdir): """ Test that upon failure: (1) `caplog` succeeded to capture the DEBUG message and assert on it => No `Exception` is raised