From 4a0a31df5c6ab79dd7dc8ee828379dca1d1f632f Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 11 Apr 2011 08:42:07 +0100 Subject: Added 'handlers' argument to logging.basicConfig. --- Doc/library/logging.rst | 17 ++++++++++++++++- Lib/logging/__init__.py | 42 +++++++++++++++++++++++++++++++++--------- Lib/test/test_logging.py | 20 ++++++++++++++++++++ Misc/NEWS | 4 ++++ 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 32f762d..eb2c718 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -983,12 +983,27 @@ functions. | ``stream`` | Use the specified stream to initialize the | | | StreamHandler. Note that this argument is | | | incompatible with 'filename' - if both are | - | | present, 'stream' is ignored. | + | | present, a ``ValueError`` is raised. | + +--------------+---------------------------------------------+ + | ``handlers`` | If specified, this should be an iterable of | + | | already created handlers to add to the root | + | | logger. Any handlers which don't already | + | | have a formatter set will be assigned the | + | | default formatter created in this function. | + | | Note that this argument is incompatible | + | | with 'filename' or 'stream' - if both are | + | | present, a ``ValueError`` is raised. | +--------------+---------------------------------------------+ .. versionchanged:: 3.2 The ``style`` argument was added. + .. versionchanged:: 3.3 + The ``handlers`` argument was added. Additional checks were added to + catch situations where incompatible arguments are specified (e.g. + ``handlers`` together with ``stream`` or ``filename``, or ``stream`` + together with ``filename``). + .. function:: shutdown() diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 7757a82..ef88d0a 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1650,6 +1650,10 @@ def basicConfig(**kwargs): stream Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with 'filename' - if both are present, 'stream' is ignored. + handlers If specified, this should be an iterable of already created + handlers, which will be added to the root handler. Any handler + in the list which does not have a formatter assigned will be + assigned the formatter created in this function. Note that you could specify a stream created using open(filename, mode) rather than passing the filename and mode in. However, it should be @@ -1657,27 +1661,47 @@ def basicConfig(**kwargs): using sys.stdout or sys.stderr), whereas FileHandler closes its stream when the handler is closed. - .. versionchanged: 3.2 + .. versionchanged:: 3.2 Added the ``style`` parameter. + + .. versionchanged:: 3.3 + Added the ``handlers`` parameter. A ``ValueError`` is now thrown for + incompatible arguments (e.g. ``handlers`` specified together with + ``filename``/``filemode``, or ``filename``/``filemode`` specified + together with ``stream``, or ``handlers`` specified together with + ``stream``. """ # Add thread safety in case someone mistakenly calls # basicConfig() from multiple threads _acquireLock() try: if len(root.handlers) == 0: - filename = kwargs.get("filename") - if filename: - mode = kwargs.get("filemode", 'a') - hdlr = FileHandler(filename, mode) + handlers = kwargs.get("handlers") + if handlers is None: + if "stream" in kwargs and "filename" in kwargs: + raise ValueError("'stream' and 'filename' should not be " + "specified together") else: - stream = kwargs.get("stream") - hdlr = StreamHandler(stream) + if "stream" in kwargs or "filename" in kwargs: + raise ValueError("'stream' or 'filename' should not be " + "specified together with 'handlers'") + if handlers is None: + filename = kwargs.get("filename") + if filename: + mode = kwargs.get("filemode", 'a') + h = FileHandler(filename, mode) + else: + stream = kwargs.get("stream") + h = StreamHandler(stream) + handlers = [h] fs = kwargs.get("format", BASIC_FORMAT) dfs = kwargs.get("datefmt", None) style = kwargs.get("style", '%') fmt = Formatter(fs, dfs, style) - hdlr.setFormatter(fmt) - root.addHandler(hdlr) + for h in handlers: + if h.formatter is None: + h.setFormatter(fmt) + root.addHandler(h) level = kwargs.get("level") if level is not None: root.setLevel(level) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 18222ea..88f0ebc 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2482,6 +2482,26 @@ class BasicConfigTest(unittest.TestCase): logging.basicConfig(level=57) self.assertEqual(logging.root.level, 57) + def test_incompatible(self): + assertRaises = self.assertRaises + handlers = [logging.StreamHandler()] + stream = sys.stderr + 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) + + def test_handlers(self): + handlers = [logging.StreamHandler(), logging.StreamHandler(sys.stdout)] + logging.basicConfig(handlers=handlers) + self.assertIs(handlers[0], logging.root.handlers[0]) + self.assertIs(handlers[1], logging.root.handlers[1]) + self.assertIsNotNone(handlers[0].formatter) + self.assertIsNotNone(handlers[1].formatter) + self.assertIs(handlers[0].formatter, handlers[1].formatter) + def _test_log(self, method, level=None): # logging.root has no handlers so basicConfig should be called called = [] diff --git a/Misc/NEWS b/Misc/NEWS index 8df6f4b..d02a22e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,10 @@ Core and Builtins Library ------- +- logging.basicConfig now supports an optional 'handlers' argument taking an + iterable of handlers to be added to the root logger. Additional parameter + checks were also added to basicConfig. + - Issue #11814: Fix likely typo in multiprocessing.Pool._terminate(). - Issue #8428: Fix a race condition in multiprocessing.Pool when terminating -- cgit v0.12