From ac6c02f1e2229d78609dede5ad96a0a72bdefdc7 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 17 May 2020 14:58:04 +0300 Subject: [PATCH] logging: use item's store for private attributes This makes things type-safe and properly private. --- changelog/7224.breaking.rst | 2 ++ src/_pytest/logging.py | 23 +++++++++++++---------- testing/logging/test_fixture.py | 3 ++- 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 changelog/7224.breaking.rst diff --git a/changelog/7224.breaking.rst b/changelog/7224.breaking.rst new file mode 100644 index 000000000..32ab2c073 --- /dev/null +++ b/changelog/7224.breaking.rst @@ -0,0 +1,2 @@ +The `item.catch_log_handler` and `item.catch_log_handlers` attributes, set by the +logging plugin and never meant to be public , are no longer available. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 8cb7b1841..4de5c1b2b 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -17,10 +17,14 @@ from _pytest.config import _strtobool from _pytest.config import Config from _pytest.config import create_terminal_writer from _pytest.pathlib import Path +from _pytest.store import StoreKey + DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" _ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +catch_log_handler_key = StoreKey["LogCaptureHandler"]() +catch_log_handlers_key = StoreKey[Dict[str, "LogCaptureHandler"]]() def _remove_ansi_escape_sequences(text): @@ -317,7 +321,7 @@ class LogCaptureHandler(logging.StreamHandler): class LogCaptureFixture: """Provides access and control of log capturing.""" - def __init__(self, item) -> None: + def __init__(self, item: nodes.Node) -> None: """Creates a new funcarg.""" self._item = item # dict of log name -> log level @@ -338,7 +342,7 @@ class LogCaptureFixture: """ :rtype: LogCaptureHandler """ - return self._item.catch_log_handler # type: ignore[no-any-return] + return self._item._store[catch_log_handler_key] def get_records(self, when: str) -> List[logging.LogRecord]: """ @@ -352,9 +356,9 @@ class LogCaptureFixture: .. versionadded:: 3.4 """ - handler = self._item.catch_log_handlers.get(when) + handler = self._item._store[catch_log_handlers_key].get(when) if handler: - return handler.records # type: ignore[no-any-return] + return handler.records else: return [] @@ -645,16 +649,15 @@ class LoggingPlugin: yield # run the test return - if not hasattr(item, "catch_log_handlers"): - item.catch_log_handlers = {} # type: ignore[attr-defined] - item.catch_log_handlers[when] = log_handler # type: ignore[attr-defined] - item.catch_log_handler = log_handler # type: ignore[attr-defined] + empty = {} # type: Dict[str, LogCaptureHandler] + item._store.setdefault(catch_log_handlers_key, empty)[when] = log_handler + item._store[catch_log_handler_key] = log_handler try: yield # run test finally: if when == "teardown": - del item.catch_log_handler # type: ignore[attr-defined] - del item.catch_log_handlers # type: ignore[attr-defined] + del item._store[catch_log_handlers_key] + del item._store[catch_log_handler_key] if self.print_logs: # Add a captured log section to the report. diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index c68866bef..a33b0e80e 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -1,6 +1,7 @@ import logging import pytest +from _pytest.logging import catch_log_handlers_key logger = logging.getLogger(__name__) sublogger = logging.getLogger(__name__ + ".baz") @@ -136,4 +137,4 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"] # This reaches into private API, don't use this type of thing in real tests! - assert set(caplog._item.catch_log_handlers.keys()) == {"setup", "call"} + assert set(caplog._item._store[catch_log_handlers_key]) == {"setup", "call"}