[Python-checkins] bpo-41906: Accept built filters in dictConfig (GH-30756)

miss-islington webhook-mailer at python.org
Mon Jan 24 07:40:02 EST 2022


https://github.com/python/cpython/commit/d7c68639795a576ff58b6479c8bb34c113df3618
commit: d7c68639795a576ff58b6479c8bb34c113df3618
branch: main
author: Mario Corchero <mcorcherojim at bloomberg.net>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2022-01-24T04:39:50-08:00
summary:

bpo-41906: Accept built filters in dictConfig (GH-30756)



When configuring the logging stack, accept already built filters (or
just callables) in the filters array of loggers and handlers.
This facilitates passing quick callables as filters.

Automerge-Triggered-By: GH:vsajip

files:
A Misc/NEWS.d/next/Library/2022-01-21-18-19-45.bpo-41906.YBaquj.rst
M Doc/library/logging.config.rst
M Lib/logging/config.py
M Lib/test/test_logging.py

diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst
index a1b8dc755ba6b..c979961a221c9 100644
--- a/Doc/library/logging.config.rst
+++ b/Doc/library/logging.config.rst
@@ -288,6 +288,9 @@ otherwise, the context is used to determine what to instantiate.
   * ``filters`` (optional).  A list of ids of the filters for this
     handler.
 
+    .. versionchanged:: 3.11
+       ``filters`` can take filter instances in addition to ids.
+
   All *other* keys are passed through as keyword arguments to the
   handler's constructor.  For example, given the snippet:
 
@@ -326,6 +329,9 @@ otherwise, the context is used to determine what to instantiate.
   * ``filters`` (optional).  A list of ids of the filters for this
     logger.
 
+    .. versionchanged:: 3.11
+       ``filters`` can take filter instances in addition to ids.
+
   * ``handlers`` (optional).  A list of ids of the handlers for this
     logger.
 
@@ -524,6 +530,10 @@ valid keyword parameter name, and so will not clash with the names of
 the keyword arguments used in the call.  The ``'()'`` also serves as a
 mnemonic that the corresponding value is a callable.
 
+    .. versionchanged:: 3.11
+       The ``filters`` member of ``handlers`` and ``loggers`` can take
+       filter instances in addition to ids.
+
 
 .. _logging-config-dict-externalobj:
 
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 9bc07eddd76b4..86a1e4eaf4cbc 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -694,7 +694,11 @@ def add_filters(self, filterer, filters):
         """Add filters to a filterer from a list of names."""
         for f in filters:
             try:
-                filterer.addFilter(self.config['filters'][f])
+                if callable(f) or callable(getattr(f, 'filter', None)):
+                    filter_ = f
+                else:
+                    filter_ = self.config['filters'][f]
+                filterer.addFilter(filter_)
             except Exception as e:
                 raise ValueError('Unable to add filter %r' % f) from e
 
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 7c38676012bab..4f3315161cf20 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3447,6 +3447,44 @@ def emit(self, record):
             logging.info('some log')
         self.assertEqual(stderr.getvalue(), 'some log my_type\n')
 
+    def test_config_callable_filter_works(self):
+        def filter_(_):
+            return 1
+        self.apply_config({
+            "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
+        })
+        assert logging.getLogger().filters[0] is filter_
+        logging.getLogger().filters = []
+
+    def test_config_filter_works(self):
+        filter_ = logging.Filter("spam.eggs")
+        self.apply_config({
+            "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
+        })
+        assert logging.getLogger().filters[0] is filter_
+        logging.getLogger().filters = []
+
+    def test_config_filter_method_works(self):
+        class FakeFilter:
+            def filter(self, _):
+                return 1
+        filter_ = FakeFilter()
+        self.apply_config({
+            "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
+        })
+        assert logging.getLogger().filters[0] is filter_
+        logging.getLogger().filters = []
+
+    def test_invalid_type_raises(self):
+        class NotAFilter: pass
+        for filter_ in [None, 1, NotAFilter()]:
+            self.assertRaises(
+                ValueError,
+                self.apply_config,
+                {"version": 1, "root": {"level": "DEBUG", "filters": [filter_]}}
+            )
+
+
 class ManagerTest(BaseTest):
     def test_manager_loggerclass(self):
         logged = []
diff --git a/Misc/NEWS.d/next/Library/2022-01-21-18-19-45.bpo-41906.YBaquj.rst b/Misc/NEWS.d/next/Library/2022-01-21-18-19-45.bpo-41906.YBaquj.rst
new file mode 100644
index 0000000000000..be707130875f2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-21-18-19-45.bpo-41906.YBaquj.rst
@@ -0,0 +1,2 @@
+Support passing filter instances in the ``filters`` values of ``handlers`` and
+``loggers`` in the dictionary passed to :func:`logging.config.dictConfig`.



More information about the Python-checkins mailing list