diff --git a/changelog/14436.bugfix.rst b/changelog/14436.bugfix.rst new file mode 100644 index 00000000000..fe9d5715035 --- /dev/null +++ b/changelog/14436.bugfix.rst @@ -0,0 +1,2 @@ +Fixed ``KeyError`` from the ``caplog`` fixture when a plugin accesses +``caplog.text`` during teardown report creation. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 982e12cd8c2..5cce6aab3e3 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -422,6 +422,8 @@ class LogCaptureFixture: def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: check_ispytest(_ispytest) self._item = item + self._handler = item.stash[caplog_handler_key] + self._records = item.stash[caplog_records_key] self._initial_handler_level: int | None = None # Dict of log name -> log level. self._initial_logger_levels: dict[str | None, int] = {} @@ -446,7 +448,7 @@ def _finalize(self) -> None: @property def handler(self) -> LogCaptureHandler: """Get the logging handler used by the fixture.""" - return self._item.stash[caplog_handler_key] + return self._handler def get_records( self, when: Literal["setup", "call", "teardown"] @@ -461,7 +463,7 @@ def get_records( .. versionadded:: 3.4 """ - return self._item.stash[caplog_records_key].get(when, []) + return self._records.get(when, []) @property def text(self) -> str: diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index c98b7d84258..fb8165b4e1e 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -508,3 +508,31 @@ def test_that_fails(request, caplog): ["*Print message*", "*INFO log message*", "*WARNING log message*"] ) assert result.ret == 1 + + +def test_caplog_text_access_from_teardown_makereport(pytester: Pytester) -> None: + """Regression test for #14436.""" + pytester.makeconftest( + """ + def pytest_runtest_makereport(item, call): + if call.when == "teardown" and "caplog" in item.funcargs: + caplog = item.funcargs["caplog"] + caplog.text + assert [r.getMessage() for r in caplog.get_records("call")] == [ + "call log" + ] + """ + ) + pytester.makepyfile( + """ + import logging + + def test_uses_caplog(caplog): + caplog.set_level(logging.INFO) + logging.info("call log") + """ + ) + + result = pytester.runpytest() + result.assert_outcomes(passed=1) + result.stdout.no_fnmatch_line("*KeyError*")