https://github.com/python/cpython/commit/327dbbedffa3f2c95e70129a11974b83e27864f9
commit: 327dbbedffa3f2c95e70129a11974b83e27864f9
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-10-30T12:52:02+02:00
summary:
gh-138162: Fix logging.LoggerAdapter with merge_extra=True and without the
extra argument (GH-140511)
files:
A Misc/NEWS.d/next/Library/2025-10-23-19-39-16.gh-issue-138162.Znw5DN.rst
M Doc/library/logging.rst
M Lib/logging/__init__.py
M Lib/test/test_logging.py
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 425025931d9835..0cf5b1c0d9bc3e 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -1082,12 +1082,13 @@ LoggerAdapter Objects
information into logging calls. For a usage example, see the section on
:ref:`adding contextual information to your logging output <context-info>`.
-.. class:: LoggerAdapter(logger, extra, merge_extra=False)
+.. class:: LoggerAdapter(logger, extra=None, merge_extra=False)
Returns an instance of :class:`LoggerAdapter` initialized with an
- underlying :class:`Logger` instance, a dict-like object (*extra*), and a
- boolean (*merge_extra*) indicating whether or not the *extra* argument of
- individual log calls should be merged with the :class:`LoggerAdapter` extra.
+ underlying :class:`Logger` instance, an optional dict-like object (*extra*),
+ and an optional boolean (*merge_extra*) indicating whether or not
+ the *extra* argument of individual log calls should be merged with
+ the :class:`LoggerAdapter` extra.
The default behavior is to ignore the *extra* argument of individual log
calls and only use the one of the :class:`LoggerAdapter` instance
@@ -1127,9 +1128,13 @@ information into logging calls. For a usage example, see
the section on
Attribute :attr:`!manager` and method :meth:`!_log` were added, which
delegate to the underlying logger and allow adapters to be nested.
+ .. versionchanged:: 3.10
+
+ The *extra* argument is now optional.
+
.. versionchanged:: 3.13
- The *merge_extra* argument was added.
+ The *merge_extra* parameter was added.
Thread Safety
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 431ff41b352048..39689a57e6ecd6 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1849,9 +1849,9 @@ class LoggerAdapter(object):
def __init__(self, logger, extra=None, merge_extra=False):
"""
- Initialize the adapter with a logger and a dict-like object which
- provides contextual information. This constructor signature allows
- easy stacking of LoggerAdapters, if so desired.
+ Initialize the adapter with a logger and an optional dict-like object
+ which provides contextual information. This constructor signature
+ allows easy stacking of LoggerAdapters, if so desired.
You can effectively pass keyword arguments as shown in the
following example:
@@ -1882,8 +1882,9 @@ def process(self, msg, kwargs):
Normally, you'll only need to override this one method in a
LoggerAdapter subclass for your specific needs.
"""
- if self.merge_extra and "extra" in kwargs:
- kwargs["extra"] = {**self.extra, **kwargs["extra"]}
+ if self.merge_extra and kwargs.get("extra") is not None:
+ if self.extra is not None:
+ kwargs["extra"] = {**self.extra, **kwargs["extra"]}
else:
kwargs["extra"] = self.extra
return msg, kwargs
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 1f7a4d9e197f9c..8815426fc99c39 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -5826,7 +5826,7 @@ def cleanup():
self.addCleanup(cleanup)
self.addCleanup(logging.shutdown)
- self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
+ self.adapter = logging.LoggerAdapter(logger=self.logger)
def test_exception(self):
msg = 'testing exception: %r'
@@ -5997,6 +5997,18 @@ def test_extra_merged(self):
self.assertEqual(record.foo, '1')
self.assertEqual(record.bar, '2')
+ self.adapter.critical('no extra') # should not fail
+ self.assertEqual(len(self.recording.records), 2)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+ self.assertNotHasAttr(record, 'bar')
+
+ self.adapter.critical('none extra', extra=None) # should not fail
+ self.assertEqual(len(self.recording.records), 3)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+ self.assertNotHasAttr(record, 'bar')
+
def test_extra_merged_log_call_has_precedence(self):
self.adapter = logging.LoggerAdapter(logger=self.logger,
extra={'foo': '1'},
@@ -6008,6 +6020,25 @@ def test_extra_merged_log_call_has_precedence(self):
self.assertHasAttr(record, 'foo')
self.assertEqual(record.foo, '2')
+ def test_extra_merged_without_extra(self):
+ self.adapter = logging.LoggerAdapter(logger=self.logger,
+ merge_extra=True)
+
+ self.adapter.critical('foo should be here', extra={'foo': '1'})
+ self.assertEqual(len(self.recording.records), 1)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+
+ self.adapter.critical('no extra') # should not fail
+ self.assertEqual(len(self.recording.records), 2)
+ record = self.recording.records[-1]
+ self.assertNotHasAttr(record, 'foo')
+
+ self.adapter.critical('none extra', extra=None) # should not fail
+ self.assertEqual(len(self.recording.records), 3)
+ record = self.recording.records[-1]
+ self.assertNotHasAttr(record, 'foo')
+
class PrefixAdapter(logging.LoggerAdapter):
prefix = 'Adapter'
diff --git
a/Misc/NEWS.d/next/Library/2025-10-23-19-39-16.gh-issue-138162.Znw5DN.rst
b/Misc/NEWS.d/next/Library/2025-10-23-19-39-16.gh-issue-138162.Znw5DN.rst
new file mode 100644
index 00000000000000..ef7a90bc37e650
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-10-23-19-39-16.gh-issue-138162.Znw5DN.rst
@@ -0,0 +1,2 @@
+Fix :class:`logging.LoggerAdapter` with ``merge_extra=True`` and without the
+*extra* argument.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]