Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9182201

Browse filesBrowse files
gh-115233: Fix an example in the Logging Cookbook (GH-115325)
Also add more tests for LoggerAdapter. Also support stacklevel in LoggerAdapter._log().
1 parent 8144661 commit 9182201
Copy full SHA for 9182201

File tree

Expand file treeCollapse file tree

4 files changed

+90
-23
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+90
-23
lines changed

‎Doc/howto/logging-cookbook.rst

Copy file name to clipboardExpand all lines: Doc/howto/logging-cookbook.rst
+4-6Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,13 +1744,11 @@ to the above, as in the following example::
17441744
return self.fmt.format(*self.args)
17451745

17461746
class StyleAdapter(logging.LoggerAdapter):
1747-
def __init__(self, logger, extra=None):
1748-
super().__init__(logger, extra or {})
1749-
1750-
def log(self, level, msg, /, *args, **kwargs):
1747+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
17511748
if self.isEnabledFor(level):
17521749
msg, kwargs = self.process(msg, kwargs)
1753-
self.logger._log(level, Message(msg, args), (), **kwargs)
1750+
self.logger.log(level, Message(msg, args), **kwargs,
1751+
stacklevel=stacklevel+1)
17541752

17551753
logger = StyleAdapter(logging.getLogger(__name__))
17561754

@@ -1762,7 +1760,7 @@ to the above, as in the following example::
17621760
main()
17631761

17641762
The above script should log the message ``Hello, world!`` when run with
1765-
Python 3.2 or later.
1763+
Python 3.8 or later.
17661764

17671765

17681766
.. currentmodule:: logging

‎Lib/logging/__init__.py

Copy file name to clipboardExpand all lines: Lib/logging/__init__.py
+2-9Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,18 +1949,11 @@ def hasHandlers(self):
19491949
"""
19501950
return self.logger.hasHandlers()
19511951

1952-
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
1952+
def _log(self, level, msg, args, **kwargs):
19531953
"""
19541954
Low-level log implementation, proxied to allow nested logger adapters.
19551955
"""
1956-
return self.logger._log(
1957-
level,
1958-
msg,
1959-
args,
1960-
exc_info=exc_info,
1961-
extra=extra,
1962-
stack_info=stack_info,
1963-
)
1956+
return self.logger._log(level, msg, args, **kwargs)
19641957

19651958
@property
19661959
def manager(self):

‎Lib/test/test_logging.py

Copy file name to clipboardExpand all lines: Lib/test/test_logging.py
+83-8Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5478,6 +5478,7 @@ def test_critical(self):
54785478
self.assertEqual(record.levelno, logging.CRITICAL)
54795479
self.assertEqual(record.msg, msg)
54805480
self.assertEqual(record.args, (self.recording,))
5481+
self.assertEqual(record.funcName, 'test_critical')
54815482

54825483
def test_is_enabled_for(self):
54835484
old_disable = self.adapter.logger.manager.disable
@@ -5496,15 +5497,9 @@ def test_has_handlers(self):
54965497
self.assertFalse(self.adapter.hasHandlers())
54975498

54985499
def test_nested(self):
5499-
class Adapter(logging.LoggerAdapter):
5500-
prefix = 'Adapter'
5501-
5502-
def process(self, msg, kwargs):
5503-
return f"{self.prefix} {msg}", kwargs
5504-
55055500
msg = 'Adapters can be nested, yo.'
5506-
adapter = Adapter(logger=self.logger, extra=None)
5507-
adapter_adapter = Adapter(logger=adapter, extra=None)
5501+
adapter = PrefixAdapter(logger=self.logger, extra=None)
5502+
adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
55085503
adapter_adapter.prefix = 'AdapterAdapter'
55095504
self.assertEqual(repr(adapter), repr(adapter_adapter))
55105505
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
@@ -5513,6 +5508,7 @@ def process(self, msg, kwargs):
55135508
self.assertEqual(record.levelno, logging.CRITICAL)
55145509
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
55155510
self.assertEqual(record.args, (self.recording,))
5511+
self.assertEqual(record.funcName, 'test_nested')
55165512
orig_manager = adapter_adapter.manager
55175513
self.assertIs(adapter.manager, orig_manager)
55185514
self.assertIs(self.logger.manager, orig_manager)
@@ -5528,6 +5524,61 @@ def process(self, msg, kwargs):
55285524
self.assertIs(adapter.manager, orig_manager)
55295525
self.assertIs(self.logger.manager, orig_manager)
55305526

5527+
def test_styled_adapter(self):
5528+
# Test an example from the Cookbook.
5529+
records = self.recording.records
5530+
adapter = StyleAdapter(self.logger)
5531+
adapter.warning('Hello, {}!', 'world')
5532+
self.assertEqual(str(records[-1].msg), 'Hello, world!')
5533+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5534+
adapter.log(logging.WARNING, 'Goodbye {}.', 'world')
5535+
self.assertEqual(str(records[-1].msg), 'Goodbye world.')
5536+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5537+
5538+
def test_nested_styled_adapter(self):
5539+
records = self.recording.records
5540+
adapter = PrefixAdapter(self.logger)
5541+
adapter.prefix = '{}'
5542+
adapter2 = StyleAdapter(adapter)
5543+
adapter2.warning('Hello, {}!', 'world')
5544+
self.assertEqual(str(records[-1].msg), '{} Hello, world!')
5545+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5546+
adapter2.log(logging.WARNING, 'Goodbye {}.', 'world')
5547+
self.assertEqual(str(records[-1].msg), '{} Goodbye world.')
5548+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5549+
5550+
def test_find_caller_with_stacklevel(self):
5551+
the_level = 1
5552+
trigger = self.adapter.warning
5553+
5554+
def innermost():
5555+
trigger('test', stacklevel=the_level)
5556+
5557+
def inner():
5558+
innermost()
5559+
5560+
def outer():
5561+
inner()
5562+
5563+
records = self.recording.records
5564+
outer()
5565+
self.assertEqual(records[-1].funcName, 'innermost')
5566+
lineno = records[-1].lineno
5567+
the_level += 1
5568+
outer()
5569+
self.assertEqual(records[-1].funcName, 'inner')
5570+
self.assertGreater(records[-1].lineno, lineno)
5571+
lineno = records[-1].lineno
5572+
the_level += 1
5573+
outer()
5574+
self.assertEqual(records[-1].funcName, 'outer')
5575+
self.assertGreater(records[-1].lineno, lineno)
5576+
lineno = records[-1].lineno
5577+
the_level += 1
5578+
outer()
5579+
self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5580+
self.assertGreater(records[-1].lineno, lineno)
5581+
55315582
def test_extra_in_records(self):
55325583
self.adapter = logging.LoggerAdapter(logger=self.logger,
55335584
extra={'foo': '1'})
@@ -5569,6 +5620,30 @@ def test_extra_merged_log_call_has_precedence(self):
55695620
self.assertEqual(record.foo, '2')
55705621

55715622

5623+
class PrefixAdapter(logging.LoggerAdapter):
5624+
prefix = 'Adapter'
5625+
5626+
def process(self, msg, kwargs):
5627+
return f"{self.prefix} {msg}", kwargs
5628+
5629+
5630+
class Message:
5631+
def __init__(self, fmt, args):
5632+
self.fmt = fmt
5633+
self.args = args
5634+
5635+
def __str__(self):
5636+
return self.fmt.format(*self.args)
5637+
5638+
5639+
class StyleAdapter(logging.LoggerAdapter):
5640+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
5641+
if self.isEnabledFor(level):
5642+
msg, kwargs = self.process(msg, kwargs)
5643+
self.logger.log(level, Message(msg, args), **kwargs,
5644+
stacklevel=stacklevel+1)
5645+
5646+
55725647
class LoggerTest(BaseTest, AssertErrorMessage):
55735648

55745649
def setUp(self):
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix an example for :class:`~logging.LoggerAdapter` in the Logging Cookbook.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.