From 3f885b543256df8acabc39fa9c28f45dfa6e4979 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 22 Mar 2013 15:19:54 +0000 Subject: [PATCH] Issue #17508: Handled out-of-order handler configuration correctly. --- Lib/logging/config.py | 28 ++++++++++++++++++++++++---- Lib/test/test_logging.py | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 373da2b0764..38b21e6b1ee 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -1,4 +1,4 @@ -# Copyright 2001-2010 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -19,7 +19,7 @@ Configuration functions for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python, and influenced by Apache's log4j system. -Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -564,7 +564,21 @@ class DictConfigurator(BaseConfigurator): # As handlers can refer to other handlers, sort the keys # to allow a deterministic order of configuration handlers = config.get('handlers', EMPTY_DICT) + deferred = [] for name in sorted(handlers): + try: + handler = self.configure_handler(handlers[name]) + handler.name = name + handlers[name] = handler + except Exception as e: + if 'target not configured yet' in str(e): + deferred.append(name) + else: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + + # Now do any that were deferred + for name in deferred: try: handler = self.configure_handler(handlers[name]) handler.name = name @@ -572,6 +586,7 @@ class DictConfigurator(BaseConfigurator): except Exception as e: raise ValueError('Unable to configure handler ' '%r: %s' % (name, e)) + # Next, do loggers - they refer to handlers and filters #we don't want to lose the existing loggers, @@ -694,12 +709,17 @@ class DictConfigurator(BaseConfigurator): c = self.resolve(c) factory = c else: - klass = self.resolve(config.pop('class')) + cname = config.pop('class') + klass = self.resolve(cname) #Special case for handler which refers to another handler if issubclass(klass, logging.handlers.MemoryHandler) and\ 'target' in config: try: - config['target'] = self.config['handlers'][config['target']] + th = self.config['handlers'][config['target']] + if not isinstance(th, logging.Handler): + config['class'] = cname # restore for deferred configuration + raise TypeError('target not configured yet') + config['target'] = th except Exception as e: raise ValueError('Unable to set target handler ' '%r: %s' % (config['target'], e)) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 2a3c780d2c4..bbd485267ad 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ """Test harness for the logging module. Run all tests. -Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. """ import logging @@ -1696,6 +1696,36 @@ class ConfigDictTest(BaseTest): }, } + out_of_order = { + "version": 1, + "formatters": { + "mySimpleFormatter": { + "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s" + } + }, + "handlers": { + "fileGlobal": { + "class": "logging.StreamHandler", + "level": "DEBUG", + "formatter": "mySimpleFormatter" + }, + "bufferGlobal": { + "class": "logging.handlers.MemoryHandler", + "capacity": 5, + "formatter": "mySimpleFormatter", + "target": "fileGlobal", + "level": "DEBUG" + } + }, + "loggers": { + "mymodule": { + "level": "DEBUG", + "handlers": ["bufferGlobal"], + "propagate": "true" + } + } + } + def apply_config(self, conf): logging.config.dictConfig(conf) @@ -1994,6 +2024,10 @@ class ConfigDictTest(BaseTest): # Original logger output is empty. self.assert_log_lines([]) + def test_out_of_order(self): + self.apply_config(self.out_of_order) + handler = logging.getLogger('mymodule').handlers[0] + self.assertIsInstance(handler.target, logging.Handler) class ManagerTest(BaseTest): def test_manager_loggerclass(self):