gh-133577: Add parameter formatter to logging.basicConfig (GH-133578)

This commit is contained in:
Sebastian Pipping 2025-05-14 08:45:00 +02:00 committed by GitHub
parent 9ad0c7b0f1
commit 2eb49d278e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 10 deletions

View file

@ -1342,8 +1342,9 @@ functions.
.. function:: basicConfig(**kwargs)
Does basic configuration for the logging system by creating a
:class:`StreamHandler` with a default :class:`Formatter` and adding it to the
Does basic configuration for the logging system by either creating a
:class:`StreamHandler` with a default :class:`Formatter`
or using the given *formatter* instance, and adding it to the
root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
:func:`error` and :func:`critical` will call :func:`basicConfig` automatically
if no handlers are defined for the root logger.
@ -1428,6 +1429,19 @@ functions.
| | which means that it will be treated the |
| | same as passing 'errors'. |
+--------------+---------------------------------------------+
| *formatter* | If specified, set this formatter instance |
| | (see :ref:`formatter-objects`) |
| | for all involved handlers. |
| | If not specified, the default is to create |
| | and use an instance of |
| | :class:`logging.Formatter` based on |
| | arguments *format*, *datefmt* and *style*. |
| | When *formatter* is specified together with |
| | any of the three arguments *format*, |
| | *datefmt* and *style*, a ``ValueError`` is |
| | raised to signal that these arguments would |
| | lose meaning otherwise. |
+--------------+---------------------------------------------+
.. versionchanged:: 3.2
The *style* argument was added.
@ -1444,6 +1458,9 @@ functions.
.. versionchanged:: 3.9
The *encoding* and *errors* arguments were added.
.. versionchanged:: 3.15
The *formatter* argument was added.
.. function:: shutdown()
Informs the logging system to perform an orderly shutdown by flushing and

View file

@ -2057,6 +2057,15 @@ def basicConfig(**kwargs):
created FileHandler, causing it to be used when the file is
opened in text mode. If not specified, the default value is
`backslashreplace`.
formatter If specified, set this formatter instance for all involved
handlers.
If not specified, the default is to create and use an instance of
`logging.Formatter` based on arguments 'format', 'datefmt' and
'style'.
When 'formatter' is specified together with any of the three
arguments 'format', 'datefmt' and 'style', a `ValueError`
is raised to signal that these arguments would lose meaning
otherwise.
Note that you could specify a stream created using open(filename, mode)
rather than passing the filename and mode in. However, it should be
@ -2079,6 +2088,9 @@ def basicConfig(**kwargs):
.. versionchanged:: 3.9
Added the ``encoding`` and ``errors`` parameters.
.. versionchanged:: 3.15
Added the ``formatter`` parameter.
"""
# Add thread safety in case someone mistakenly calls
# basicConfig() from multiple threads
@ -2114,13 +2126,19 @@ def basicConfig(**kwargs):
stream = kwargs.pop("stream", None)
h = StreamHandler(stream)
handlers = [h]
dfs = kwargs.pop("datefmt", None)
style = kwargs.pop("style", '%')
if style not in _STYLES:
raise ValueError('Style must be one of: %s' % ','.join(
_STYLES.keys()))
fs = kwargs.pop("format", _STYLES[style][1])
fmt = Formatter(fs, dfs, style)
fmt = kwargs.pop("formatter", None)
if fmt is None:
dfs = kwargs.pop("datefmt", None)
style = kwargs.pop("style", '%')
if style not in _STYLES:
raise ValueError('Style must be one of: %s' % ','.join(
_STYLES.keys()))
fs = kwargs.pop("format", _STYLES[style][1])
fmt = Formatter(fs, dfs, style)
else:
for forbidden_key in ("datefmt", "format", "style"):
if forbidden_key in kwargs:
raise ValueError(f"{forbidden_key!r} should not be specified together with 'formatter'")
for h in handlers:
if h.formatter is None:
h.setFormatter(fmt)

View file

@ -61,7 +61,7 @@ import warnings
import weakref
from http.server import HTTPServer, BaseHTTPRequestHandler
from unittest.mock import patch
from unittest.mock import call, Mock, patch
from urllib.parse import urlparse, parse_qs
from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
ThreadingTCPServer, StreamRequestHandler)
@ -5655,12 +5655,19 @@ class BasicConfigTest(unittest.TestCase):
assertRaises = self.assertRaises
handlers = [logging.StreamHandler()]
stream = sys.stderr
formatter = logging.Formatter()
assertRaises(ValueError, logging.basicConfig, filename='test.log',
stream=stream)
assertRaises(ValueError, logging.basicConfig, filename='test.log',
handlers=handlers)
assertRaises(ValueError, logging.basicConfig, stream=stream,
handlers=handlers)
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
format='%(message)s')
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
datefmt='%H:%M:%S')
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
style='%')
# Issue 23207: test for invalid kwargs
assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
# Should pop both filename and filemode even if filename is None
@ -5795,6 +5802,20 @@ class BasicConfigTest(unittest.TestCase):
# didn't write anything due to the encoding error
self.assertEqual(data, r'')
def test_formatter_given(self):
mock_formatter = Mock()
mock_handler = Mock(formatter=None)
with patch("logging.Formatter") as mock_formatter_init:
logging.basicConfig(formatter=mock_formatter, handlers=[mock_handler])
self.assertEqual(mock_handler.setFormatter.call_args_list, [call(mock_formatter)])
self.assertEqual(mock_formatter_init.call_count, 0)
def test_formatter_not_given(self):
mock_handler = Mock(formatter=None)
with patch("logging.Formatter") as mock_formatter_init:
logging.basicConfig(handlers=[mock_handler])
self.assertEqual(mock_formatter_init.call_count, 1)
@support.requires_working_socket()
def test_log_taskName(self):
async def log_record():

View file

@ -0,0 +1 @@
Add parameter ``formatter`` to :func:`logging.basicConfig`.