Mercurial > repos > bcclaywell > argo_navis
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() |