PageRenderTime 227ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/python/lib/Lib/site-packages/django/utils/dictconfig.py

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