https://github.com/python/cpython/commit/2eb49d278e081b0cde057c1ffc2e8cd24ae39225
commit: 2eb49d278e081b0cde057c1ffc2e8cd24ae39225
branch: main
author: Sebastian Pipping <sebast...@pipping.org>
committer: vsajip <vinay_sa...@yahoo.co.uk>
date: 2025-05-14T07:45:00+01:00
summary:

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

files:
A Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.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 72190e97240514..4509da5891685f 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -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
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 283a1055182b63..f2d1a02629d92b 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -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)
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 1e5adcc8db13f6..fa5b1e438168bc 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -61,7 +61,7 @@
 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 @@ def test_incompatible(self):
         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 @@ def dummy_handle_error(record):
             # 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():
diff --git 
a/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst 
b/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst
new file mode 100644
index 00000000000000..9d056983439e4b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst
@@ -0,0 +1 @@
+Add parameter ``formatter`` to :func:`logging.basicConfig`.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to