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 21edde1

Browse filesBrowse files
[3.11] gh-115233: Fix an example in the Logging Cookbook (GH-115325) (GH-115355) (GH-115357)
Also add more tests for LoggerAdapter. Also support stacklevel in LoggerAdapter._log(). (cherry picked from commit 225856e) (cherry picked from commit 9182201) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent d732134 commit 21edde1
Copy full SHA for 21edde1

File tree

Expand file treeCollapse file tree

4 files changed

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

4 files changed

+106
-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
@@ -1910,18 +1910,11 @@ def hasHandlers(self):
19101910
"""
19111911
return self.logger.hasHandlers()
19121912

1913-
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
1913+
def _log(self, level, msg, args, **kwargs):
19141914
"""
19151915
Low-level log implementation, proxied to allow nested logger adapters.
19161916
"""
1917-
return self.logger._log(
1918-
level,
1919-
msg,
1920-
args,
1921-
exc_info=exc_info,
1922-
extra=extra,
1923-
stack_info=stack_info,
1924-
)
1917+
return self.logger._log(level, msg, args, **kwargs)
19251918

19261919
@property
19271920
def manager(self):

‎Lib/test/test_logging.py

Copy file name to clipboardExpand all lines: Lib/test/test_logging.py
+99-8Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5075,6 +5075,7 @@ def test_critical(self):
50755075
self.assertEqual(record.levelno, logging.CRITICAL)
50765076
self.assertEqual(record.msg, msg)
50775077
self.assertEqual(record.args, (self.recording,))
5078+
self.assertEqual(record.funcName, 'test_critical')
50785079

50795080
def test_is_enabled_for(self):
50805081
old_disable = self.adapter.logger.manager.disable
@@ -5093,15 +5094,9 @@ def test_has_handlers(self):
50935094
self.assertFalse(self.adapter.hasHandlers())
50945095

50955096
def test_nested(self):
5096-
class Adapter(logging.LoggerAdapter):
5097-
prefix = 'Adapter'
5098-
5099-
def process(self, msg, kwargs):
5100-
return f"{self.prefix} {msg}", kwargs
5101-
51025097
msg = 'Adapters can be nested, yo.'
5103-
adapter = Adapter(logger=self.logger, extra=None)
5104-
adapter_adapter = Adapter(logger=adapter, extra=None)
5098+
adapter = PrefixAdapter(logger=self.logger, extra=None)
5099+
adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
51055100
adapter_adapter.prefix = 'AdapterAdapter'
51065101
self.assertEqual(repr(adapter), repr(adapter_adapter))
51075102
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
@@ -5110,6 +5105,7 @@ def process(self, msg, kwargs):
51105105
self.assertEqual(record.levelno, logging.CRITICAL)
51115106
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
51125107
self.assertEqual(record.args, (self.recording,))
5108+
self.assertEqual(record.funcName, 'test_nested')
51135109
orig_manager = adapter_adapter.manager
51145110
self.assertIs(adapter.manager, orig_manager)
51155111
self.assertIs(self.logger.manager, orig_manager)
@@ -5125,6 +5121,101 @@ def process(self, msg, kwargs):
51255121
self.assertIs(adapter.manager, orig_manager)
51265122
self.assertIs(self.logger.manager, orig_manager)
51275123

5124+
def test_styled_adapter(self):
5125+
# Test an example from the Cookbook.
5126+
records = self.recording.records
5127+
adapter = StyleAdapter(self.logger)
5128+
adapter.warning('Hello, {}!', 'world')
5129+
self.assertEqual(str(records[-1].msg), 'Hello, world!')
5130+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5131+
adapter.log(logging.WARNING, 'Goodbye {}.', 'world')
5132+
self.assertEqual(str(records[-1].msg), 'Goodbye world.')
5133+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5134+
5135+
def test_nested_styled_adapter(self):
5136+
records = self.recording.records
5137+
adapter = PrefixAdapter(self.logger)
5138+
adapter.prefix = '{}'
5139+
adapter2 = StyleAdapter(adapter)
5140+
adapter2.warning('Hello, {}!', 'world')
5141+
self.assertEqual(str(records[-1].msg), '{} Hello, world!')
5142+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5143+
adapter2.log(logging.WARNING, 'Goodbye {}.', 'world')
5144+
self.assertEqual(str(records[-1].msg), '{} Goodbye world.')
5145+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5146+
5147+
def test_find_caller_with_stacklevel(self):
5148+
the_level = 1
5149+
trigger = self.adapter.warning
5150+
5151+
def innermost():
5152+
trigger('test', stacklevel=the_level)
5153+
5154+
def inner():
5155+
innermost()
5156+
5157+
def outer():
5158+
inner()
5159+
5160+
records = self.recording.records
5161+
outer()
5162+
self.assertEqual(records[-1].funcName, 'innermost')
5163+
lineno = records[-1].lineno
5164+
the_level += 1
5165+
outer()
5166+
self.assertEqual(records[-1].funcName, 'inner')
5167+
self.assertGreater(records[-1].lineno, lineno)
5168+
lineno = records[-1].lineno
5169+
the_level += 1
5170+
outer()
5171+
self.assertEqual(records[-1].funcName, 'outer')
5172+
self.assertGreater(records[-1].lineno, lineno)
5173+
lineno = records[-1].lineno
5174+
the_level += 1
5175+
outer()
5176+
self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5177+
self.assertGreater(records[-1].lineno, lineno)
5178+
5179+
def test_extra_in_records(self):
5180+
self.adapter = logging.LoggerAdapter(logger=self.logger,
5181+
extra={'foo': '1'})
5182+
5183+
self.adapter.critical('foo should be here')
5184+
self.assertEqual(len(self.recording.records), 1)
5185+
record = self.recording.records[0]
5186+
self.assertTrue(hasattr(record, 'foo'))
5187+
self.assertEqual(record.foo, '1')
5188+
5189+
def test_extra_not_merged_by_default(self):
5190+
self.adapter.critical('foo should NOT be here', extra={'foo': 'nope'})
5191+
self.assertEqual(len(self.recording.records), 1)
5192+
record = self.recording.records[0]
5193+
self.assertFalse(hasattr(record, 'foo'))
5194+
5195+
5196+
class PrefixAdapter(logging.LoggerAdapter):
5197+
prefix = 'Adapter'
5198+
5199+
def process(self, msg, kwargs):
5200+
return f"{self.prefix} {msg}", kwargs
5201+
5202+
5203+
class Message:
5204+
def __init__(self, fmt, args):
5205+
self.fmt = fmt
5206+
self.args = args
5207+
5208+
def __str__(self):
5209+
return self.fmt.format(*self.args)
5210+
5211+
5212+
class StyleAdapter(logging.LoggerAdapter):
5213+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
5214+
if self.isEnabledFor(level):
5215+
msg, kwargs = self.process(msg, kwargs)
5216+
self.logger.log(level, Message(msg, args), **kwargs,
5217+
stacklevel=stacklevel+1)
5218+
51285219

51295220
class LoggerTest(BaseTest, AssertErrorMessage):
51305221

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