Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2eb49d2

Browse files
authoredMay 14, 2025··
gh-133577: Add parameter formatter to logging.basicConfig (GH-133578)
1 parent 9ad0c7b commit 2eb49d2

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed
 

‎Doc/library/logging.rst

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,8 +1342,9 @@ functions.
13421342

13431343
.. function:: basicConfig(**kwargs)
13441344

1345-
Does basic configuration for the logging system by creating a
1346-
:class:`StreamHandler` with a default :class:`Formatter` and adding it to the
1345+
Does basic configuration for the logging system by either creating a
1346+
:class:`StreamHandler` with a default :class:`Formatter`
1347+
or using the given *formatter* instance, and adding it to the
13471348
root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
13481349
:func:`error` and :func:`critical` will call :func:`basicConfig` automatically
13491350
if no handlers are defined for the root logger.
@@ -1428,6 +1429,19 @@ functions.
14281429
| | which means that it will be treated the |
14291430
| | same as passing 'errors'. |
14301431
+--------------+---------------------------------------------+
1432+
| *formatter* | If specified, set this formatter instance |
1433+
| | (see :ref:`formatter-objects`) |
1434+
| | for all involved handlers. |
1435+
| | If not specified, the default is to create |
1436+
| | and use an instance of |
1437+
| | :class:`logging.Formatter` based on |
1438+
| | arguments *format*, *datefmt* and *style*. |
1439+
| | When *formatter* is specified together with |
1440+
| | any of the three arguments *format*, |
1441+
| | *datefmt* and *style*, a ``ValueError`` is |
1442+
| | raised to signal that these arguments would |
1443+
| | lose meaning otherwise. |
1444+
+--------------+---------------------------------------------+
14311445

14321446
.. versionchanged:: 3.2
14331447
The *style* argument was added.
@@ -1444,6 +1458,9 @@ functions.
14441458
.. versionchanged:: 3.9
14451459
The *encoding* and *errors* arguments were added.
14461460

1461+
.. versionchanged:: 3.15
1462+
The *formatter* argument was added.
1463+
14471464
.. function:: shutdown()
14481465

14491466
Informs the logging system to perform an orderly shutdown by flushing and

‎Lib/logging/__init__.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,15 @@ def basicConfig(**kwargs):
20572057
created FileHandler, causing it to be used when the file is
20582058
opened in text mode. If not specified, the default value is
20592059
`backslashreplace`.
2060+
formatter If specified, set this formatter instance for all involved
2061+
handlers.
2062+
If not specified, the default is to create and use an instance of
2063+
`logging.Formatter` based on arguments 'format', 'datefmt' and
2064+
'style'.
2065+
When 'formatter' is specified together with any of the three
2066+
arguments 'format', 'datefmt' and 'style', a `ValueError`
2067+
is raised to signal that these arguments would lose meaning
2068+
otherwise.
20602069
20612070
Note that you could specify a stream created using open(filename, mode)
20622071
rather than passing the filename and mode in. However, it should be
@@ -2079,6 +2088,9 @@ def basicConfig(**kwargs):
20792088
20802089
.. versionchanged:: 3.9
20812090
Added the ``encoding`` and ``errors`` parameters.
2091+
2092+
.. versionchanged:: 3.15
2093+
Added the ``formatter`` parameter.
20822094
"""
20832095
# Add thread safety in case someone mistakenly calls
20842096
# basicConfig() from multiple threads
@@ -2114,13 +2126,19 @@ def basicConfig(**kwargs):
21142126
stream = kwargs.pop("stream", None)
21152127
h = StreamHandler(stream)
21162128
handlers = [h]
2117-
dfs = kwargs.pop("datefmt", None)
2118-
style = kwargs.pop("style", '%')
2119-
if style not in _STYLES:
2120-
raise ValueError('Style must be one of: %s' % ','.join(
2121-
_STYLES.keys()))
2122-
fs = kwargs.pop("format", _STYLES[style][1])
2123-
fmt = Formatter(fs, dfs, style)
2129+
fmt = kwargs.pop("formatter", None)
2130+
if fmt is None:
2131+
dfs = kwargs.pop("datefmt", None)
2132+
style = kwargs.pop("style", '%')
2133+
if style not in _STYLES:
2134+
raise ValueError('Style must be one of: %s' % ','.join(
2135+
_STYLES.keys()))
2136+
fs = kwargs.pop("format", _STYLES[style][1])
2137+
fmt = Formatter(fs, dfs, style)
2138+
else:
2139+
for forbidden_key in ("datefmt", "format", "style"):
2140+
if forbidden_key in kwargs:
2141+
raise ValueError(f"{forbidden_key!r} should not be specified together with 'formatter'")
21242142
for h in handlers:
21252143
if h.formatter is None:
21262144
h.setFormatter(fmt)

‎Lib/test/test_logging.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
import weakref
6262

6363
from http.server import HTTPServer, BaseHTTPRequestHandler
64-
from unittest.mock import patch
64+
from unittest.mock import call, Mock, patch
6565
from urllib.parse import urlparse, parse_qs
6666
from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
6767
ThreadingTCPServer, StreamRequestHandler)
@@ -5655,12 +5655,19 @@ def test_incompatible(self):
56555655
assertRaises = self.assertRaises
56565656
handlers = [logging.StreamHandler()]
56575657
stream = sys.stderr
5658+
formatter = logging.Formatter()
56585659
assertRaises(ValueError, logging.basicConfig, filename='test.log',
56595660
stream=stream)
56605661
assertRaises(ValueError, logging.basicConfig, filename='test.log',
56615662
handlers=handlers)
56625663
assertRaises(ValueError, logging.basicConfig, stream=stream,
56635664
handlers=handlers)
5665+
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
5666+
format='%(message)s')
5667+
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
5668+
datefmt='%H:%M:%S')
5669+
assertRaises(ValueError, logging.basicConfig, formatter=formatter,
5670+
style='%')
56645671
# Issue 23207: test for invalid kwargs
56655672
assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
56665673
# Should pop both filename and filemode even if filename is None
@@ -5795,6 +5802,20 @@ def dummy_handle_error(record):
57955802
# didn't write anything due to the encoding error
57965803
self.assertEqual(data, r'')
57975804

5805+
def test_formatter_given(self):
5806+
mock_formatter = Mock()
5807+
mock_handler = Mock(formatter=None)
5808+
with patch("logging.Formatter") as mock_formatter_init:
5809+
logging.basicConfig(formatter=mock_formatter, handlers=[mock_handler])
5810+
self.assertEqual(mock_handler.setFormatter.call_args_list, [call(mock_formatter)])
5811+
self.assertEqual(mock_formatter_init.call_count, 0)
5812+
5813+
def test_formatter_not_given(self):
5814+
mock_handler = Mock(formatter=None)
5815+
with patch("logging.Formatter") as mock_formatter_init:
5816+
logging.basicConfig(handlers=[mock_handler])
5817+
self.assertEqual(mock_formatter_init.call_count, 1)
5818+
57985819
@support.requires_working_socket()
57995820
def test_log_taskName(self):
58005821
async def log_record():
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add parameter ``formatter`` to :func:`logging.basicConfig`.

0 commit comments

Comments
 (0)
Please sign in to comment.

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant