Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / util / dictconfig.py @ 5c285c17

History | View | Annotate | Download (22.4 kB)

1 9e98ba3c Giorgos Verigakis
# This is a copy of the Python logging.config.dictconfig module.
2 9e98ba3c Giorgos Verigakis
# It is provided here for backwards compatibility for Python versions
3 9e98ba3c Giorgos Verigakis
# prior to 2.7.
4 9e98ba3c Giorgos Verigakis
#
5 9e98ba3c Giorgos Verigakis
# Copyright 2009-2010 by Vinay Sajip. All Rights Reserved.
6 9e98ba3c Giorgos Verigakis
#
7 9e98ba3c Giorgos Verigakis
# Permission to use, copy, modify, and distribute this software and its
8 9e98ba3c Giorgos Verigakis
# documentation for any purpose and without fee is hereby granted,
9 9e98ba3c Giorgos Verigakis
# provided that the above copyright notice appear in all copies and that
10 9e98ba3c Giorgos Verigakis
# both that copyright notice and this permission notice appear in
11 9e98ba3c Giorgos Verigakis
# supporting documentation, and that the name of Vinay Sajip
12 9e98ba3c Giorgos Verigakis
# not be used in advertising or publicity pertaining to distribution
13 9e98ba3c Giorgos Verigakis
# of the software without specific, written prior permission.
14 9e98ba3c Giorgos Verigakis
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 9e98ba3c Giorgos Verigakis
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
16 9e98ba3c Giorgos Verigakis
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 9e98ba3c Giorgos Verigakis
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
18 9e98ba3c Giorgos Verigakis
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 9e98ba3c Giorgos Verigakis
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 9e98ba3c Giorgos Verigakis
21 9e98ba3c Giorgos Verigakis
import logging.handlers
22 9e98ba3c Giorgos Verigakis
import re
23 9e98ba3c Giorgos Verigakis
import sys
24 9e98ba3c Giorgos Verigakis
import types
25 9e98ba3c Giorgos Verigakis
26 9e98ba3c Giorgos Verigakis
IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
27 9e98ba3c Giorgos Verigakis
28 9e98ba3c Giorgos Verigakis
def valid_ident(s):
29 9e98ba3c Giorgos Verigakis
    m = IDENTIFIER.match(s)
30 9e98ba3c Giorgos Verigakis
    if not m:
31 9e98ba3c Giorgos Verigakis
        raise ValueError('Not a valid Python identifier: %r' % s)
32 9e98ba3c Giorgos Verigakis
    return True
33 9e98ba3c Giorgos Verigakis
34 9e98ba3c Giorgos Verigakis
#
35 9e98ba3c Giorgos Verigakis
# This function is defined in logging only in recent versions of Python
36 9e98ba3c Giorgos Verigakis
#
37 9e98ba3c Giorgos Verigakis
try:
38 9e98ba3c Giorgos Verigakis
    from logging import _checkLevel
39 9e98ba3c Giorgos Verigakis
except ImportError:
40 9e98ba3c Giorgos Verigakis
    def _checkLevel(level):
41 9e98ba3c Giorgos Verigakis
        if isinstance(level, int):
42 9e98ba3c Giorgos Verigakis
            rv = level
43 9e98ba3c Giorgos Verigakis
        elif str(level) == level:
44 9e98ba3c Giorgos Verigakis
            if level not in logging._levelNames:
45 9e98ba3c Giorgos Verigakis
                raise ValueError('Unknown level: %r' % level)
46 9e98ba3c Giorgos Verigakis
            rv = logging._levelNames[level]
47 9e98ba3c Giorgos Verigakis
        else:
48 9e98ba3c Giorgos Verigakis
            raise TypeError('Level not an integer or a '
49 9e98ba3c Giorgos Verigakis
                            'valid string: %r' % level)
50 9e98ba3c Giorgos Verigakis
        return rv
51 9e98ba3c Giorgos Verigakis
52 9e98ba3c Giorgos Verigakis
# The ConvertingXXX classes are wrappers around standard Python containers,
53 9e98ba3c Giorgos Verigakis
# and they serve to convert any suitable values in the container. The
54 9e98ba3c Giorgos Verigakis
# conversion converts base dicts, lists and tuples to their wrapped
55 9e98ba3c Giorgos Verigakis
# equivalents, whereas strings which match a conversion format are converted
56 9e98ba3c Giorgos Verigakis
# appropriately.
57 9e98ba3c Giorgos Verigakis
#
58 9e98ba3c Giorgos Verigakis
# Each wrapper should have a configurator attribute holding the actual
59 9e98ba3c Giorgos Verigakis
# configurator to use for conversion.
60 9e98ba3c Giorgos Verigakis
61 9e98ba3c Giorgos Verigakis
class ConvertingDict(dict):
62 9e98ba3c Giorgos Verigakis
    """A converting dictionary wrapper."""
63 9e98ba3c Giorgos Verigakis
64 9e98ba3c Giorgos Verigakis
    def __getitem__(self, key):
65 9e98ba3c Giorgos Verigakis
        value = dict.__getitem__(self, key)
66 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
67 9e98ba3c Giorgos Verigakis
        #If the converted value is different, save for next time
68 9e98ba3c Giorgos Verigakis
        if value is not result:
69 9e98ba3c Giorgos Verigakis
            self[key] = result
70 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
71 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
72 9e98ba3c Giorgos Verigakis
                result.parent = self
73 9e98ba3c Giorgos Verigakis
                result.key = key
74 9e98ba3c Giorgos Verigakis
        return result
75 9e98ba3c Giorgos Verigakis
76 9e98ba3c Giorgos Verigakis
    def get(self, key, default=None):
77 9e98ba3c Giorgos Verigakis
        value = dict.get(self, key, default)
78 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
79 9e98ba3c Giorgos Verigakis
        #If the converted value is different, save for next time
80 9e98ba3c Giorgos Verigakis
        if value is not result:
81 9e98ba3c Giorgos Verigakis
            self[key] = result
82 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
83 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
84 9e98ba3c Giorgos Verigakis
                result.parent = self
85 9e98ba3c Giorgos Verigakis
                result.key = key
86 9e98ba3c Giorgos Verigakis
        return result
87 9e98ba3c Giorgos Verigakis
88 9e98ba3c Giorgos Verigakis
    def pop(self, key, default=None):
89 9e98ba3c Giorgos Verigakis
        value = dict.pop(self, key, default)
90 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
91 9e98ba3c Giorgos Verigakis
        if value is not result:
92 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
93 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
94 9e98ba3c Giorgos Verigakis
                result.parent = self
95 9e98ba3c Giorgos Verigakis
                result.key = key
96 9e98ba3c Giorgos Verigakis
        return result
97 9e98ba3c Giorgos Verigakis
98 9e98ba3c Giorgos Verigakis
class ConvertingList(list):
99 9e98ba3c Giorgos Verigakis
    """A converting list wrapper."""
100 9e98ba3c Giorgos Verigakis
    def __getitem__(self, key):
101 9e98ba3c Giorgos Verigakis
        value = list.__getitem__(self, key)
102 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
103 9e98ba3c Giorgos Verigakis
        #If the converted value is different, save for next time
104 9e98ba3c Giorgos Verigakis
        if value is not result:
105 9e98ba3c Giorgos Verigakis
            self[key] = result
106 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
107 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
108 9e98ba3c Giorgos Verigakis
                result.parent = self
109 9e98ba3c Giorgos Verigakis
                result.key = key
110 9e98ba3c Giorgos Verigakis
        return result
111 9e98ba3c Giorgos Verigakis
112 9e98ba3c Giorgos Verigakis
    def pop(self, idx=-1):
113 9e98ba3c Giorgos Verigakis
        value = list.pop(self, idx)
114 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
115 9e98ba3c Giorgos Verigakis
        if value is not result:
116 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
117 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
118 9e98ba3c Giorgos Verigakis
                result.parent = self
119 9e98ba3c Giorgos Verigakis
        return result
120 9e98ba3c Giorgos Verigakis
121 9e98ba3c Giorgos Verigakis
class ConvertingTuple(tuple):
122 9e98ba3c Giorgos Verigakis
    """A converting tuple wrapper."""
123 9e98ba3c Giorgos Verigakis
    def __getitem__(self, key):
124 9e98ba3c Giorgos Verigakis
        value = tuple.__getitem__(self, key)
125 9e98ba3c Giorgos Verigakis
        result = self.configurator.convert(value)
126 9e98ba3c Giorgos Verigakis
        if value is not result:
127 9e98ba3c Giorgos Verigakis
            if type(result) in (ConvertingDict, ConvertingList,
128 9e98ba3c Giorgos Verigakis
                                ConvertingTuple):
129 9e98ba3c Giorgos Verigakis
                result.parent = self
130 9e98ba3c Giorgos Verigakis
                result.key = key
131 9e98ba3c Giorgos Verigakis
        return result
132 9e98ba3c Giorgos Verigakis
133 9e98ba3c Giorgos Verigakis
class BaseConfigurator(object):
134 9e98ba3c Giorgos Verigakis
    """
135 9e98ba3c Giorgos Verigakis
    The configurator base class which defines some useful defaults.
136 9e98ba3c Giorgos Verigakis
    """
137 9e98ba3c Giorgos Verigakis
138 9e98ba3c Giorgos Verigakis
    CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
139 9e98ba3c Giorgos Verigakis
140 9e98ba3c Giorgos Verigakis
    WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
141 9e98ba3c Giorgos Verigakis
    DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
142 9e98ba3c Giorgos Verigakis
    INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
143 9e98ba3c Giorgos Verigakis
    DIGIT_PATTERN = re.compile(r'^\d+$')
144 9e98ba3c Giorgos Verigakis
145 9e98ba3c Giorgos Verigakis
    value_converters = {
146 9e98ba3c Giorgos Verigakis
        'ext' : 'ext_convert',
147 9e98ba3c Giorgos Verigakis
        'cfg' : 'cfg_convert',
148 9e98ba3c Giorgos Verigakis
    }
149 9e98ba3c Giorgos Verigakis
150 9e98ba3c Giorgos Verigakis
    # We might want to use a different one, e.g. importlib
151 9e98ba3c Giorgos Verigakis
    importer = __import__
152 9e98ba3c Giorgos Verigakis
153 9e98ba3c Giorgos Verigakis
    def __init__(self, config):
154 9e98ba3c Giorgos Verigakis
        self.config = ConvertingDict(config)
155 9e98ba3c Giorgos Verigakis
        self.config.configurator = self
156 9e98ba3c Giorgos Verigakis
157 9e98ba3c Giorgos Verigakis
    def resolve(self, s):
158 9e98ba3c Giorgos Verigakis
        """
159 9e98ba3c Giorgos Verigakis
        Resolve strings to objects using standard import and attribute
160 9e98ba3c Giorgos Verigakis
        syntax.
161 9e98ba3c Giorgos Verigakis
        """
162 9e98ba3c Giorgos Verigakis
        name = s.split('.')
163 9e98ba3c Giorgos Verigakis
        used = name.pop(0)
164 9e98ba3c Giorgos Verigakis
        try:
165 9e98ba3c Giorgos Verigakis
            found = self.importer(used)
166 9e98ba3c Giorgos Verigakis
            for frag in name:
167 9e98ba3c Giorgos Verigakis
                used += '.' + frag
168 9e98ba3c Giorgos Verigakis
                try:
169 9e98ba3c Giorgos Verigakis
                    found = getattr(found, frag)
170 9e98ba3c Giorgos Verigakis
                except AttributeError:
171 9e98ba3c Giorgos Verigakis
                    self.importer(used)
172 9e98ba3c Giorgos Verigakis
                    found = getattr(found, frag)
173 9e98ba3c Giorgos Verigakis
            return found
174 9e98ba3c Giorgos Verigakis
        except ImportError:
175 9e98ba3c Giorgos Verigakis
            e, tb = sys.exc_info()[1:]
176 9e98ba3c Giorgos Verigakis
            v = ValueError('Cannot resolve %r: %s' % (s, e))
177 9e98ba3c Giorgos Verigakis
            v.__cause__, v.__traceback__ = e, tb
178 9e98ba3c Giorgos Verigakis
            raise v
179 9e98ba3c Giorgos Verigakis
180 9e98ba3c Giorgos Verigakis
    def ext_convert(self, value):
181 9e98ba3c Giorgos Verigakis
        """Default converter for the ext:// protocol."""
182 9e98ba3c Giorgos Verigakis
        return self.resolve(value)
183 9e98ba3c Giorgos Verigakis
184 9e98ba3c Giorgos Verigakis
    def cfg_convert(self, value):
185 9e98ba3c Giorgos Verigakis
        """Default converter for the cfg:// protocol."""
186 9e98ba3c Giorgos Verigakis
        rest = value
187 9e98ba3c Giorgos Verigakis
        m = self.WORD_PATTERN.match(rest)
188 9e98ba3c Giorgos Verigakis
        if m is None:
189 9e98ba3c Giorgos Verigakis
            raise ValueError("Unable to convert %r" % value)
190 9e98ba3c Giorgos Verigakis
        else:
191 9e98ba3c Giorgos Verigakis
            rest = rest[m.end():]
192 9e98ba3c Giorgos Verigakis
            d = self.config[m.groups()[0]]
193 9e98ba3c Giorgos Verigakis
            #print d, rest
194 9e98ba3c Giorgos Verigakis
            while rest:
195 9e98ba3c Giorgos Verigakis
                m = self.DOT_PATTERN.match(rest)
196 9e98ba3c Giorgos Verigakis
                if m:
197 9e98ba3c Giorgos Verigakis
                    d = d[m.groups()[0]]
198 9e98ba3c Giorgos Verigakis
                else:
199 9e98ba3c Giorgos Verigakis
                    m = self.INDEX_PATTERN.match(rest)
200 9e98ba3c Giorgos Verigakis
                    if m:
201 9e98ba3c Giorgos Verigakis
                        idx = m.groups()[0]
202 9e98ba3c Giorgos Verigakis
                        if not self.DIGIT_PATTERN.match(idx):
203 9e98ba3c Giorgos Verigakis
                            d = d[idx]
204 9e98ba3c Giorgos Verigakis
                        else:
205 9e98ba3c Giorgos Verigakis
                            try:
206 9e98ba3c Giorgos Verigakis
                                n = int(idx) # try as number first (most likely)
207 9e98ba3c Giorgos Verigakis
                                d = d[n]
208 9e98ba3c Giorgos Verigakis
                            except TypeError:
209 9e98ba3c Giorgos Verigakis
                                d = d[idx]
210 9e98ba3c Giorgos Verigakis
                if m:
211 9e98ba3c Giorgos Verigakis
                    rest = rest[m.end():]
212 9e98ba3c Giorgos Verigakis
                else:
213 9e98ba3c Giorgos Verigakis
                    raise ValueError('Unable to convert '
214 9e98ba3c Giorgos Verigakis
                                     '%r at %r' % (value, rest))
215 9e98ba3c Giorgos Verigakis
        #rest should be empty
216 9e98ba3c Giorgos Verigakis
        return d
217 9e98ba3c Giorgos Verigakis
218 9e98ba3c Giorgos Verigakis
    def convert(self, value):
219 9e98ba3c Giorgos Verigakis
        """
220 9e98ba3c Giorgos Verigakis
        Convert values to an appropriate type. dicts, lists and tuples are
221 9e98ba3c Giorgos Verigakis
        replaced by their converting alternatives. Strings are checked to
222 9e98ba3c Giorgos Verigakis
        see if they have a conversion format and are converted if they do.
223 9e98ba3c Giorgos Verigakis
        """
224 9e98ba3c Giorgos Verigakis
        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
225 9e98ba3c Giorgos Verigakis
            value = ConvertingDict(value)
226 9e98ba3c Giorgos Verigakis
            value.configurator = self
227 9e98ba3c Giorgos Verigakis
        elif not isinstance(value, ConvertingList) and isinstance(value, list):
228 9e98ba3c Giorgos Verigakis
            value = ConvertingList(value)
229 9e98ba3c Giorgos Verigakis
            value.configurator = self
230 9e98ba3c Giorgos Verigakis
        elif not isinstance(value, ConvertingTuple) and\
231 9e98ba3c Giorgos Verigakis
                 isinstance(value, tuple):
232 9e98ba3c Giorgos Verigakis
            value = ConvertingTuple(value)
233 9e98ba3c Giorgos Verigakis
            value.configurator = self
234 9e98ba3c Giorgos Verigakis
        elif isinstance(value, basestring): # str for py3k
235 9e98ba3c Giorgos Verigakis
            m = self.CONVERT_PATTERN.match(value)
236 9e98ba3c Giorgos Verigakis
            if m:
237 9e98ba3c Giorgos Verigakis
                d = m.groupdict()
238 9e98ba3c Giorgos Verigakis
                prefix = d['prefix']
239 9e98ba3c Giorgos Verigakis
                converter = self.value_converters.get(prefix, None)
240 9e98ba3c Giorgos Verigakis
                if converter:
241 9e98ba3c Giorgos Verigakis
                    suffix = d['suffix']
242 9e98ba3c Giorgos Verigakis
                    converter = getattr(self, converter)
243 9e98ba3c Giorgos Verigakis
                    value = converter(suffix)
244 9e98ba3c Giorgos Verigakis
        return value
245 9e98ba3c Giorgos Verigakis
246 9e98ba3c Giorgos Verigakis
    def configure_custom(self, config):
247 9e98ba3c Giorgos Verigakis
        """Configure an object with a user-supplied factory."""
248 9e98ba3c Giorgos Verigakis
        c = config.pop('()')
249 9e98ba3c Giorgos Verigakis
        if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
250 9e98ba3c Giorgos Verigakis
            c = self.resolve(c)
251 9e98ba3c Giorgos Verigakis
        props = config.pop('.', None)
252 9e98ba3c Giorgos Verigakis
        # Check for valid identifiers
253 9e98ba3c Giorgos Verigakis
        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
254 9e98ba3c Giorgos Verigakis
        result = c(**kwargs)
255 9e98ba3c Giorgos Verigakis
        if props:
256 9e98ba3c Giorgos Verigakis
            for name, value in props.items():
257 9e98ba3c Giorgos Verigakis
                setattr(result, name, value)
258 9e98ba3c Giorgos Verigakis
        return result
259 9e98ba3c Giorgos Verigakis
260 9e98ba3c Giorgos Verigakis
    def as_tuple(self, value):
261 9e98ba3c Giorgos Verigakis
        """Utility function which converts lists to tuples."""
262 9e98ba3c Giorgos Verigakis
        if isinstance(value, list):
263 9e98ba3c Giorgos Verigakis
            value = tuple(value)
264 9e98ba3c Giorgos Verigakis
        return value
265 9e98ba3c Giorgos Verigakis
266 9e98ba3c Giorgos Verigakis
class DictConfigurator(BaseConfigurator):
267 9e98ba3c Giorgos Verigakis
    """
268 9e98ba3c Giorgos Verigakis
    Configure logging using a dictionary-like object to describe the
269 9e98ba3c Giorgos Verigakis
    configuration.
270 9e98ba3c Giorgos Verigakis
    """
271 9e98ba3c Giorgos Verigakis
272 9e98ba3c Giorgos Verigakis
    def configure(self):
273 9e98ba3c Giorgos Verigakis
        """Do the configuration."""
274 9e98ba3c Giorgos Verigakis
275 9e98ba3c Giorgos Verigakis
        config = self.config
276 9e98ba3c Giorgos Verigakis
        if 'version' not in config:
277 9e98ba3c Giorgos Verigakis
            raise ValueError("dictionary doesn't specify a version")
278 9e98ba3c Giorgos Verigakis
        if config['version'] != 1:
279 9e98ba3c Giorgos Verigakis
            raise ValueError("Unsupported version: %s" % config['version'])
280 9e98ba3c Giorgos Verigakis
        incremental = config.pop('incremental', False)
281 9e98ba3c Giorgos Verigakis
        EMPTY_DICT = {}
282 9e98ba3c Giorgos Verigakis
        logging._acquireLock()
283 9e98ba3c Giorgos Verigakis
        try:
284 9e98ba3c Giorgos Verigakis
            if incremental:
285 9e98ba3c Giorgos Verigakis
                handlers = config.get('handlers', EMPTY_DICT)
286 9e98ba3c Giorgos Verigakis
                # incremental handler config only if handler name
287 9e98ba3c Giorgos Verigakis
                # ties in to logging._handlers (Python 2.7)
288 9e98ba3c Giorgos Verigakis
                if sys.version_info[:2] == (2, 7):
289 9e98ba3c Giorgos Verigakis
                    for name in handlers:
290 9e98ba3c Giorgos Verigakis
                        if name not in logging._handlers:
291 9e98ba3c Giorgos Verigakis
                            raise ValueError('No handler found with '
292 9e98ba3c Giorgos Verigakis
                                             'name %r'  % name)
293 9e98ba3c Giorgos Verigakis
                        else:
294 9e98ba3c Giorgos Verigakis
                            try:
295 9e98ba3c Giorgos Verigakis
                                handler = logging._handlers[name]
296 9e98ba3c Giorgos Verigakis
                                handler_config = handlers[name]
297 9e98ba3c Giorgos Verigakis
                                level = handler_config.get('level', None)
298 9e98ba3c Giorgos Verigakis
                                if level:
299 9e98ba3c Giorgos Verigakis
                                    handler.setLevel(_checkLevel(level))
300 9e98ba3c Giorgos Verigakis
                            except StandardError, e:
301 9e98ba3c Giorgos Verigakis
                                raise ValueError('Unable to configure handler '
302 9e98ba3c Giorgos Verigakis
                                                 '%r: %s' % (name, e))
303 9e98ba3c Giorgos Verigakis
                loggers = config.get('loggers', EMPTY_DICT)
304 9e98ba3c Giorgos Verigakis
                for name in loggers:
305 9e98ba3c Giorgos Verigakis
                    try:
306 9e98ba3c Giorgos Verigakis
                        self.configure_logger(name, loggers[name], True)
307 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
308 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure logger '
309 9e98ba3c Giorgos Verigakis
                                         '%r: %s' % (name, e))
310 9e98ba3c Giorgos Verigakis
                root = config.get('root', None)
311 9e98ba3c Giorgos Verigakis
                if root:
312 9e98ba3c Giorgos Verigakis
                    try:
313 9e98ba3c Giorgos Verigakis
                        self.configure_root(root, True)
314 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
315 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure root '
316 9e98ba3c Giorgos Verigakis
                                         'logger: %s' % e)
317 9e98ba3c Giorgos Verigakis
            else:
318 9e98ba3c Giorgos Verigakis
                disable_existing = config.pop('disable_existing_loggers', True)
319 9e98ba3c Giorgos Verigakis
320 9e98ba3c Giorgos Verigakis
                logging._handlers.clear()
321 9e98ba3c Giorgos Verigakis
                del logging._handlerList[:]
322 9e98ba3c Giorgos Verigakis
323 9e98ba3c Giorgos Verigakis
                # Do formatters first - they don't refer to anything else
324 9e98ba3c Giorgos Verigakis
                formatters = config.get('formatters', EMPTY_DICT)
325 9e98ba3c Giorgos Verigakis
                for name in formatters:
326 9e98ba3c Giorgos Verigakis
                    try:
327 9e98ba3c Giorgos Verigakis
                        formatters[name] = self.configure_formatter(
328 9e98ba3c Giorgos Verigakis
                                                            formatters[name])
329 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
330 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure '
331 9e98ba3c Giorgos Verigakis
                                         'formatter %r: %s' % (name, e))
332 9e98ba3c Giorgos Verigakis
                # Next, do filters - they don't refer to anything else, either
333 9e98ba3c Giorgos Verigakis
                filters = config.get('filters', EMPTY_DICT)
334 9e98ba3c Giorgos Verigakis
                for name in filters:
335 9e98ba3c Giorgos Verigakis
                    try:
336 9e98ba3c Giorgos Verigakis
                        filters[name] = self.configure_filter(filters[name])
337 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
338 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure '
339 9e98ba3c Giorgos Verigakis
                                         'filter %r: %s' % (name, e))
340 9e98ba3c Giorgos Verigakis
341 9e98ba3c Giorgos Verigakis
                # Next, do handlers - they refer to formatters and filters
342 9e98ba3c Giorgos Verigakis
                # As handlers can refer to other handlers, sort the keys
343 9e98ba3c Giorgos Verigakis
                # to allow a deterministic order of configuration
344 9e98ba3c Giorgos Verigakis
                handlers = config.get('handlers', EMPTY_DICT)
345 9e98ba3c Giorgos Verigakis
                for name in sorted(handlers):
346 9e98ba3c Giorgos Verigakis
                    try:
347 9e98ba3c Giorgos Verigakis
                        handler = self.configure_handler(handlers[name])
348 9e98ba3c Giorgos Verigakis
                        handler.name = name
349 9e98ba3c Giorgos Verigakis
                        handlers[name] = handler
350 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
351 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure handler '
352 9e98ba3c Giorgos Verigakis
                                         '%r: %s' % (name, e))
353 9e98ba3c Giorgos Verigakis
                # Next, do loggers - they refer to handlers and filters
354 9e98ba3c Giorgos Verigakis
355 9e98ba3c Giorgos Verigakis
                #we don't want to lose the existing loggers,
356 9e98ba3c Giorgos Verigakis
                #since other threads may have pointers to them.
357 9e98ba3c Giorgos Verigakis
                #existing is set to contain all existing loggers,
358 9e98ba3c Giorgos Verigakis
                #and as we go through the new configuration we
359 9e98ba3c Giorgos Verigakis
                #remove any which are configured. At the end,
360 9e98ba3c Giorgos Verigakis
                #what's left in existing is the set of loggers
361 9e98ba3c Giorgos Verigakis
                #which were in the previous configuration but
362 9e98ba3c Giorgos Verigakis
                #which are not in the new configuration.
363 9e98ba3c Giorgos Verigakis
                root = logging.root
364 9e98ba3c Giorgos Verigakis
                existing = root.manager.loggerDict.keys()
365 9e98ba3c Giorgos Verigakis
                #The list needs to be sorted so that we can
366 9e98ba3c Giorgos Verigakis
                #avoid disabling child loggers of explicitly
367 9e98ba3c Giorgos Verigakis
                #named loggers. With a sorted list it is easier
368 9e98ba3c Giorgos Verigakis
                #to find the child loggers.
369 9e98ba3c Giorgos Verigakis
                existing.sort()
370 9e98ba3c Giorgos Verigakis
                #We'll keep the list of existing loggers
371 9e98ba3c Giorgos Verigakis
                #which are children of named loggers here...
372 9e98ba3c Giorgos Verigakis
                child_loggers = []
373 9e98ba3c Giorgos Verigakis
                #now set up the new ones...
374 9e98ba3c Giorgos Verigakis
                loggers = config.get('loggers', EMPTY_DICT)
375 9e98ba3c Giorgos Verigakis
                for name in loggers:
376 9e98ba3c Giorgos Verigakis
                    if name in existing:
377 9e98ba3c Giorgos Verigakis
                        i = existing.index(name)
378 9e98ba3c Giorgos Verigakis
                        prefixed = name + "."
379 9e98ba3c Giorgos Verigakis
                        pflen = len(prefixed)
380 9e98ba3c Giorgos Verigakis
                        num_existing = len(existing)
381 9e98ba3c Giorgos Verigakis
                        i = i + 1 # look at the entry after name
382 9e98ba3c Giorgos Verigakis
                        while (i < num_existing) and\
383 9e98ba3c Giorgos Verigakis
                              (existing[i][:pflen] == prefixed):
384 9e98ba3c Giorgos Verigakis
                            child_loggers.append(existing[i])
385 9e98ba3c Giorgos Verigakis
                            i = i + 1
386 9e98ba3c Giorgos Verigakis
                        existing.remove(name)
387 9e98ba3c Giorgos Verigakis
                    try:
388 9e98ba3c Giorgos Verigakis
                        self.configure_logger(name, loggers[name])
389 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
390 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure logger '
391 9e98ba3c Giorgos Verigakis
                                         '%r: %s' % (name, e))
392 9e98ba3c Giorgos Verigakis
393 9e98ba3c Giorgos Verigakis
                #Disable any old loggers. There's no point deleting
394 9e98ba3c Giorgos Verigakis
                #them as other threads may continue to hold references
395 9e98ba3c Giorgos Verigakis
                #and by disabling them, you stop them doing any logging.
396 9e98ba3c Giorgos Verigakis
                #However, don't disable children of named loggers, as that's
397 9e98ba3c Giorgos Verigakis
                #probably not what was intended by the user.
398 9e98ba3c Giorgos Verigakis
                for log in existing:
399 9e98ba3c Giorgos Verigakis
                    logger = root.manager.loggerDict[log]
400 9e98ba3c Giorgos Verigakis
                    if log in child_loggers:
401 9e98ba3c Giorgos Verigakis
                        logger.level = logging.NOTSET
402 9e98ba3c Giorgos Verigakis
                        logger.handlers = []
403 9e98ba3c Giorgos Verigakis
                        logger.propagate = True
404 9e98ba3c Giorgos Verigakis
                    elif disable_existing:
405 9e98ba3c Giorgos Verigakis
                        logger.disabled = True
406 9e98ba3c Giorgos Verigakis
407 9e98ba3c Giorgos Verigakis
                # And finally, do the root logger
408 9e98ba3c Giorgos Verigakis
                root = config.get('root', None)
409 9e98ba3c Giorgos Verigakis
                if root:
410 9e98ba3c Giorgos Verigakis
                    try:
411 9e98ba3c Giorgos Verigakis
                        self.configure_root(root)
412 9e98ba3c Giorgos Verigakis
                    except StandardError, e:
413 9e98ba3c Giorgos Verigakis
                        raise ValueError('Unable to configure root '
414 9e98ba3c Giorgos Verigakis
                                         'logger: %s' % e)
415 9e98ba3c Giorgos Verigakis
        finally:
416 9e98ba3c Giorgos Verigakis
            logging._releaseLock()
417 9e98ba3c Giorgos Verigakis
418 9e98ba3c Giorgos Verigakis
    def configure_formatter(self, config):
419 9e98ba3c Giorgos Verigakis
        """Configure a formatter from a dictionary."""
420 9e98ba3c Giorgos Verigakis
        if '()' in config:
421 9e98ba3c Giorgos Verigakis
            factory = config['()'] # for use in exception handler
422 9e98ba3c Giorgos Verigakis
            try:
423 9e98ba3c Giorgos Verigakis
                result = self.configure_custom(config)
424 9e98ba3c Giorgos Verigakis
            except TypeError, te:
425 9e98ba3c Giorgos Verigakis
                if "'format'" not in str(te):
426 9e98ba3c Giorgos Verigakis
                    raise
427 9e98ba3c Giorgos Verigakis
                #Name of parameter changed from fmt to format.
428 9e98ba3c Giorgos Verigakis
                #Retry with old name.
429 9e98ba3c Giorgos Verigakis
                #This is so that code can be used with older Python versions
430 9e98ba3c Giorgos Verigakis
                #(e.g. by Django)
431 9e98ba3c Giorgos Verigakis
                config['fmt'] = config.pop('format')
432 9e98ba3c Giorgos Verigakis
                config['()'] = factory
433 9e98ba3c Giorgos Verigakis
                result = self.configure_custom(config)
434 9e98ba3c Giorgos Verigakis
        else:
435 9e98ba3c Giorgos Verigakis
            fmt = config.get('format', None)
436 9e98ba3c Giorgos Verigakis
            dfmt = config.get('datefmt', None)
437 9e98ba3c Giorgos Verigakis
            result = logging.Formatter(fmt, dfmt)
438 9e98ba3c Giorgos Verigakis
        return result
439 9e98ba3c Giorgos Verigakis
440 9e98ba3c Giorgos Verigakis
    def configure_filter(self, config):
441 9e98ba3c Giorgos Verigakis
        """Configure a filter from a dictionary."""
442 9e98ba3c Giorgos Verigakis
        if '()' in config:
443 9e98ba3c Giorgos Verigakis
            result = self.configure_custom(config)
444 9e98ba3c Giorgos Verigakis
        else:
445 9e98ba3c Giorgos Verigakis
            name = config.get('name', '')
446 9e98ba3c Giorgos Verigakis
            result = logging.Filter(name)
447 9e98ba3c Giorgos Verigakis
        return result
448 9e98ba3c Giorgos Verigakis
449 9e98ba3c Giorgos Verigakis
    def add_filters(self, filterer, filters):
450 9e98ba3c Giorgos Verigakis
        """Add filters to a filterer from a list of names."""
451 9e98ba3c Giorgos Verigakis
        for f in filters:
452 9e98ba3c Giorgos Verigakis
            try:
453 9e98ba3c Giorgos Verigakis
                filterer.addFilter(self.config['filters'][f])
454 9e98ba3c Giorgos Verigakis
            except StandardError, e:
455 9e98ba3c Giorgos Verigakis
                raise ValueError('Unable to add filter %r: %s' % (f, e))
456 9e98ba3c Giorgos Verigakis
457 9e98ba3c Giorgos Verigakis
    def configure_handler(self, config):
458 9e98ba3c Giorgos Verigakis
        """Configure a handler from a dictionary."""
459 9e98ba3c Giorgos Verigakis
        formatter = config.pop('formatter', None)
460 9e98ba3c Giorgos Verigakis
        if formatter:
461 9e98ba3c Giorgos Verigakis
            try:
462 9e98ba3c Giorgos Verigakis
                formatter = self.config['formatters'][formatter]
463 9e98ba3c Giorgos Verigakis
            except StandardError, e:
464 9e98ba3c Giorgos Verigakis
                raise ValueError('Unable to set formatter '
465 9e98ba3c Giorgos Verigakis
                                 '%r: %s' % (formatter, e))
466 9e98ba3c Giorgos Verigakis
        level = config.pop('level', None)
467 9e98ba3c Giorgos Verigakis
        filters = config.pop('filters', None)
468 9e98ba3c Giorgos Verigakis
        if '()' in config:
469 9e98ba3c Giorgos Verigakis
            c = config.pop('()')
470 9e98ba3c Giorgos Verigakis
            if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
471 9e98ba3c Giorgos Verigakis
                c = self.resolve(c)
472 9e98ba3c Giorgos Verigakis
            factory = c
473 9e98ba3c Giorgos Verigakis
        else:
474 9e98ba3c Giorgos Verigakis
            klass = self.resolve(config.pop('class'))
475 9e98ba3c Giorgos Verigakis
            #Special case for handler which refers to another handler
476 9e98ba3c Giorgos Verigakis
            if issubclass(klass, logging.handlers.MemoryHandler) and\
477 9e98ba3c Giorgos Verigakis
                'target' in config:
478 9e98ba3c Giorgos Verigakis
                try:
479 9e98ba3c Giorgos Verigakis
                    config['target'] = self.config['handlers'][config['target']]
480 9e98ba3c Giorgos Verigakis
                except StandardError, e:
481 9e98ba3c Giorgos Verigakis
                    raise ValueError('Unable to set target handler '
482 9e98ba3c Giorgos Verigakis
                                     '%r: %s' % (config['target'], e))
483 9e98ba3c Giorgos Verigakis
            elif issubclass(klass, logging.handlers.SMTPHandler) and\
484 9e98ba3c Giorgos Verigakis
                'mailhost' in config:
485 9e98ba3c Giorgos Verigakis
                config['mailhost'] = self.as_tuple(config['mailhost'])
486 9e98ba3c Giorgos Verigakis
            elif issubclass(klass, logging.handlers.SysLogHandler) and\
487 9e98ba3c Giorgos Verigakis
                'address' in config:
488 9e98ba3c Giorgos Verigakis
                config['address'] = self.as_tuple(config['address'])
489 9e98ba3c Giorgos Verigakis
            factory = klass
490 9e98ba3c Giorgos Verigakis
        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
491 9e98ba3c Giorgos Verigakis
        try:
492 9e98ba3c Giorgos Verigakis
            result = factory(**kwargs)
493 9e98ba3c Giorgos Verigakis
        except TypeError, te:
494 9e98ba3c Giorgos Verigakis
            if "'stream'" not in str(te):
495 9e98ba3c Giorgos Verigakis
                raise
496 9e98ba3c Giorgos Verigakis
            #The argument name changed from strm to stream
497 9e98ba3c Giorgos Verigakis
            #Retry with old name.
498 9e98ba3c Giorgos Verigakis
            #This is so that code can be used with older Python versions
499 9e98ba3c Giorgos Verigakis
            #(e.g. by Django)
500 9e98ba3c Giorgos Verigakis
            kwargs['strm'] = kwargs.pop('stream')
501 9e98ba3c Giorgos Verigakis
            result = factory(**kwargs)
502 9e98ba3c Giorgos Verigakis
        if formatter:
503 9e98ba3c Giorgos Verigakis
            result.setFormatter(formatter)
504 9e98ba3c Giorgos Verigakis
        if level is not None:
505 9e98ba3c Giorgos Verigakis
            result.setLevel(_checkLevel(level))
506 9e98ba3c Giorgos Verigakis
        if filters:
507 9e98ba3c Giorgos Verigakis
            self.add_filters(result, filters)
508 9e98ba3c Giorgos Verigakis
        return result
509 9e98ba3c Giorgos Verigakis
510 9e98ba3c Giorgos Verigakis
    def add_handlers(self, logger, handlers):
511 9e98ba3c Giorgos Verigakis
        """Add handlers to a logger from a list of names."""
512 9e98ba3c Giorgos Verigakis
        for h in handlers:
513 9e98ba3c Giorgos Verigakis
            try:
514 9e98ba3c Giorgos Verigakis
                logger.addHandler(self.config['handlers'][h])
515 9e98ba3c Giorgos Verigakis
            except StandardError, e:
516 9e98ba3c Giorgos Verigakis
                raise ValueError('Unable to add handler %r: %s' % (h, e))
517 9e98ba3c Giorgos Verigakis
518 9e98ba3c Giorgos Verigakis
    def common_logger_config(self, logger, config, incremental=False):
519 9e98ba3c Giorgos Verigakis
        """
520 9e98ba3c Giorgos Verigakis
        Perform configuration which is common to root and non-root loggers.
521 9e98ba3c Giorgos Verigakis
        """
522 9e98ba3c Giorgos Verigakis
        level = config.get('level', None)
523 9e98ba3c Giorgos Verigakis
        if level is not None:
524 9e98ba3c Giorgos Verigakis
            logger.setLevel(_checkLevel(level))
525 9e98ba3c Giorgos Verigakis
        if not incremental:
526 9e98ba3c Giorgos Verigakis
            #Remove any existing handlers
527 9e98ba3c Giorgos Verigakis
            for h in logger.handlers[:]:
528 9e98ba3c Giorgos Verigakis
                logger.removeHandler(h)
529 9e98ba3c Giorgos Verigakis
            handlers = config.get('handlers', None)
530 9e98ba3c Giorgos Verigakis
            if handlers:
531 9e98ba3c Giorgos Verigakis
                self.add_handlers(logger, handlers)
532 9e98ba3c Giorgos Verigakis
            filters = config.get('filters', None)
533 9e98ba3c Giorgos Verigakis
            if filters:
534 9e98ba3c Giorgos Verigakis
                self.add_filters(logger, filters)
535 9e98ba3c Giorgos Verigakis
536 9e98ba3c Giorgos Verigakis
    def configure_logger(self, name, config, incremental=False):
537 9e98ba3c Giorgos Verigakis
        """Configure a non-root logger from a dictionary."""
538 9e98ba3c Giorgos Verigakis
        logger = logging.getLogger(name)
539 9e98ba3c Giorgos Verigakis
        self.common_logger_config(logger, config, incremental)
540 9e98ba3c Giorgos Verigakis
        propagate = config.get('propagate', None)
541 9e98ba3c Giorgos Verigakis
        if propagate is not None:
542 9e98ba3c Giorgos Verigakis
            logger.propagate = propagate
543 9e98ba3c Giorgos Verigakis
544 9e98ba3c Giorgos Verigakis
    def configure_root(self, config, incremental=False):
545 9e98ba3c Giorgos Verigakis
        """Configure a root logger from a dictionary."""
546 9e98ba3c Giorgos Verigakis
        root = logging.getLogger()
547 9e98ba3c Giorgos Verigakis
        self.common_logger_config(root, config, incremental)
548 9e98ba3c Giorgos Verigakis
549 9e98ba3c Giorgos Verigakis
dictConfigClass = DictConfigurator
550 9e98ba3c Giorgos Verigakis
551 9e98ba3c Giorgos Verigakis
def dictConfig(config):
552 9e98ba3c Giorgos Verigakis
    """Configure logging using a dictionary."""
553 9e98ba3c Giorgos Verigakis
    dictConfigClass(config).configure()