forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpo-40275: Avoid importing logging in test.support (pythonGH-19601)
Import logging lazily in assertLogs() in unittest. Move TestHandler from test.support to logging_helper.
- Loading branch information
1 parent
1699491
commit 515fce4
Showing
7 changed files
with
105 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import logging.handlers | ||
|
||
class TestHandler(logging.handlers.BufferingHandler): | ||
def __init__(self, matcher): | ||
# BufferingHandler takes a "capacity" argument | ||
# so as to know when to flush. As we're overriding | ||
# shouldFlush anyway, we can set a capacity of zero. | ||
# You can call flush() manually to clear out the | ||
# buffer. | ||
logging.handlers.BufferingHandler.__init__(self, 0) | ||
self.matcher = matcher | ||
|
||
def shouldFlush(self): | ||
return False | ||
|
||
def emit(self, record): | ||
self.format(record) | ||
self.buffer.append(record.__dict__) | ||
|
||
def matches(self, **kwargs): | ||
""" | ||
Look for a saved dict whose keys/values match the supplied arguments. | ||
""" | ||
result = False | ||
for d in self.buffer: | ||
if self.matcher.matches(d, **kwargs): | ||
result = True | ||
break | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import logging | ||
import collections | ||
|
||
from .case import _BaseTestCaseContext | ||
|
||
|
||
_LoggingWatcher = collections.namedtuple("_LoggingWatcher", | ||
["records", "output"]) | ||
|
||
class _CapturingHandler(logging.Handler): | ||
""" | ||
A logging handler capturing all (raw and formatted) logging output. | ||
""" | ||
|
||
def __init__(self): | ||
logging.Handler.__init__(self) | ||
self.watcher = _LoggingWatcher([], []) | ||
|
||
def flush(self): | ||
pass | ||
|
||
def emit(self, record): | ||
self.watcher.records.append(record) | ||
msg = self.format(record) | ||
self.watcher.output.append(msg) | ||
|
||
|
||
class _AssertLogsContext(_BaseTestCaseContext): | ||
"""A context manager used to implement TestCase.assertLogs().""" | ||
|
||
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s" | ||
|
||
def __init__(self, test_case, logger_name, level): | ||
_BaseTestCaseContext.__init__(self, test_case) | ||
self.logger_name = logger_name | ||
if level: | ||
self.level = logging._nameToLevel.get(level, level) | ||
else: | ||
self.level = logging.INFO | ||
self.msg = None | ||
|
||
def __enter__(self): | ||
if isinstance(self.logger_name, logging.Logger): | ||
logger = self.logger = self.logger_name | ||
else: | ||
logger = self.logger = logging.getLogger(self.logger_name) | ||
formatter = logging.Formatter(self.LOGGING_FORMAT) | ||
handler = _CapturingHandler() | ||
handler.setFormatter(formatter) | ||
self.watcher = handler.watcher | ||
self.old_handlers = logger.handlers[:] | ||
self.old_level = logger.level | ||
self.old_propagate = logger.propagate | ||
logger.handlers = [handler] | ||
logger.setLevel(self.level) | ||
logger.propagate = False | ||
return handler.watcher | ||
|
||
def __exit__(self, exc_type, exc_value, tb): | ||
self.logger.handlers = self.old_handlers | ||
self.logger.propagate = self.old_propagate | ||
self.logger.setLevel(self.old_level) | ||
if exc_type is not None: | ||
# let unexpected exceptions pass through | ||
return False | ||
if len(self.watcher.records) == 0: | ||
self._raiseFailure( | ||
"no logs of level {} or higher triggered on {}" | ||
.format(logging.getLevelName(self.level), self.logger.name)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
Misc/NEWS.d/next/Library/2020-04-20-19-06-55.bpo-40275.9UcN2g.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
The :mod:`logging` package is now imported lazily in :mod:`unittest` only | ||
when the :meth:`~unittest.TestCase.assertLogs` assertion is used. |