comparison venv/lib/python2.7/site-packages/pip/compat/dictconfig.py @ 0:d67268158946 draft

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