Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / dictconfig.py @ 1c65202f

History | View | Annotate | Download (22.4 kB)

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