logging: use item's store for private attributes

This makes things type-safe and properly private.
This commit is contained in:
Ran Benita 2020-05-17 14:58:04 +03:00
parent d4dfe863c9
commit ac6c02f1e2
3 changed files with 17 additions and 11 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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"}