diff options
Diffstat (limited to 'Lib/logging/__init__.py')
-rw-r--r-- | Lib/logging/__init__.py | 198 |
1 files changed, 119 insertions, 79 deletions
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 9f436f3680..9a8685b3d4 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2014 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,12 +18,13 @@ Logging package for Python. Based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ -import sys, os, time, io, traceback, warnings, weakref +import sys, os, time, io, traceback, warnings, weakref, collections + from string import Template __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR', @@ -42,6 +43,7 @@ except ImportError: #pragma: no cover __author__ = "Vinay Sajip <vinay_sajip@red-dove.com>" __status__ = "production" +# The following module attributes are no longer updated. __version__ = "0.5.1.2" __date__ = "07 February 2010" @@ -50,34 +52,6 @@ __date__ = "07 February 2010" #--------------------------------------------------------------------------- # -# _srcfile is used when walking the stack to check when we've got the first -# caller stack frame. -# -if hasattr(sys, 'frozen'): #support for py2exe - _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:]) -else: - _srcfile = __file__ -_srcfile = os.path.normcase(_srcfile) - - -if hasattr(sys, '_getframe'): - currentframe = lambda: sys._getframe(3) -else: #pragma: no cover - def currentframe(): - """Return the frame object for the caller's stack frame.""" - try: - raise Exception - except: - return sys.exc_info()[2].tb_frame.f_back - -# _srcfile is only used in conjunction with sys._getframe(). -# To provide compatibility with older versions of Python, set _srcfile -# to None if _getframe() is not available; this value will prevent -# findCaller() from being called. -#if not hasattr(sys, "_getframe"): -# _srcfile = None - -# #_startTime is used as the base when calculating the relative time of events # _startTime = time.time() @@ -123,20 +97,22 @@ INFO = 20 DEBUG = 10 NOTSET = 0 -_levelNames = { - CRITICAL : 'CRITICAL', - ERROR : 'ERROR', - WARNING : 'WARNING', - INFO : 'INFO', - DEBUG : 'DEBUG', - NOTSET : 'NOTSET', - 'CRITICAL' : CRITICAL, - 'ERROR' : ERROR, - 'WARN' : WARNING, - 'WARNING' : WARNING, - 'INFO' : INFO, - 'DEBUG' : DEBUG, - 'NOTSET' : NOTSET, +_levelToName = { + CRITICAL: 'CRITICAL', + ERROR: 'ERROR', + WARNING: 'WARNING', + INFO: 'INFO', + DEBUG: 'DEBUG', + NOTSET: 'NOTSET', +} +_nameToLevel = { + 'CRITICAL': CRITICAL, + 'ERROR': ERROR, + 'WARN': WARNING, + 'WARNING': WARNING, + 'INFO': INFO, + 'DEBUG': DEBUG, + 'NOTSET': NOTSET, } def getLevelName(level): @@ -153,7 +129,8 @@ def getLevelName(level): Otherwise, the string "Level %s" % level is returned. """ - return _levelNames.get(level, ("Level %s" % level)) + # See Issue #22386 for the reason for this convoluted expression + return _levelToName.get(level, _nameToLevel.get(level, ("Level %s" % level))) def addLevelName(level, levelName): """ @@ -163,18 +140,52 @@ def addLevelName(level, levelName): """ _acquireLock() try: #unlikely to cause an exception, but you never know... - _levelNames[level] = levelName - _levelNames[levelName] = level + _levelToName[level] = levelName + _nameToLevel[levelName] = level finally: _releaseLock() +if hasattr(sys, '_getframe'): + currentframe = lambda: sys._getframe(3) +else: #pragma: no cover + def currentframe(): + """Return the frame object for the caller's stack frame.""" + try: + raise Exception + except Exception: + return sys.exc_info()[2].tb_frame.f_back + +# +# _srcfile is used when walking the stack to check when we've got the first +# caller stack frame, by skipping frames whose filename is that of this +# module's source. It therefore should contain the filename of this module's +# source file. +# +# Ordinarily we would use __file__ for this, but frozen modules don't always +# have __file__ set, for some reason (see Issue #21736). Thus, we get the +# filename from a handy code object from a function defined in this module. +# (There's no particular reason for picking addLevelName.) +# + +_srcfile = os.path.normcase(addLevelName.__code__.co_filename) + +# _srcfile is only used in conjunction with sys._getframe(). +# To provide compatibility with older versions of Python, set _srcfile +# to None if _getframe() is not available; this value will prevent +# findCaller() from being called. You can also do this if you want to avoid +# the overhead of fetching caller information, even when _getframe() is +# available. +#if not hasattr(sys, '_getframe'): +# _srcfile = None + + def _checkLevel(level): if isinstance(level, int): rv = level elif str(level) == level: - if level not in _levelNames: + if level not in _nameToLevel: raise ValueError("Unknown level: %r" % level) - rv = _levelNames[level] + rv = _nameToLevel[level] else: raise TypeError("Level not an integer or a valid string: %r" % level) return rv @@ -250,7 +261,13 @@ class LogRecord(object): # 'Value is %d' instead of 'Value is 0'. # For the use case of passing a dictionary, this should not be a # problem. - if args and len(args) == 1 and isinstance(args[0], dict) and args[0]: + # Issue #21172: a request was made to relax the isinstance check + # to hasattr(args[0], '__getitem__'). However, the docs on string + # formatting still seem to suggest a mapping object is required. + # Thus, while not removing the isinstance check, it does now look + # for collections.Mapping rather than, as before, dict. + if (args and len(args) == 1 and isinstance(args[0], collections.Mapping) + and args[0]): args = args[0] self.args = args self.levelname = getLevelName(level) @@ -708,16 +725,17 @@ def _removeHandlerRef(wr): Remove a handler reference from the internal cleanup list. """ # This function can be called during module teardown, when globals are - # set to None. If _acquireLock is None, assume this is the case and do - # nothing. - if (_acquireLock is not None and _handlerList is not None and - _releaseLock is not None): - _acquireLock() + # set to None. It can also be called from another thread. So we need to + # pre-emptively grab the necessary globals and check if they're None, + # to prevent race conditions and failures during interpreter shutdown. + acquire, release, handlers = _acquireLock, _releaseLock, _handlerList + if acquire and release and handlers: + acquire() try: - if wr in _handlerList: - _handlerList.remove(wr) + if wr in handlers: + handlers.remove(wr) finally: - _releaseLock() + release() def _addHandlerRef(handler): """ @@ -882,16 +900,37 @@ class Handler(Filterer): The record which was being processed is passed in to this method. """ if raiseExceptions and sys.stderr: # see issue 13807 - ei = sys.exc_info() + t, v, tb = sys.exc_info() try: - traceback.print_exception(ei[0], ei[1], ei[2], - None, sys.stderr) - sys.stderr.write('Logged from file %s, line %s\n' % ( - record.filename, record.lineno)) - except IOError: #pragma: no cover + sys.stderr.write('--- Logging error ---\n') + traceback.print_exception(t, v, tb, None, sys.stderr) + sys.stderr.write('Call stack:\n') + # Walk the stack frame up until we're out of logging, + # so as to print the calling context. + frame = tb.tb_frame + while (frame and os.path.dirname(frame.f_code.co_filename) == + __path__[0]): + frame = frame.f_back + if frame: + traceback.print_stack(frame, file=sys.stderr) + else: + # couldn't find the right stack frame, for some reason + sys.stderr.write('Logged from file %s, line %s\n' % ( + record.filename, record.lineno)) + # Issue 18671: output logging message and arguments + try: + sys.stderr.write('Message: %r\n' + 'Arguments: %s\n' % (record.msg, + record.args)) + except Exception: + sys.stderr.write('Unable to print the message and arguments' + ' - possible formatting error.\nUse the' + ' traceback above to help find the error.\n' + ) + except OSError: #pragma: no cover pass # see issue 5971 finally: - del ei + del t, v, tb class StreamHandler(Handler): """ @@ -941,9 +980,7 @@ class StreamHandler(Handler): stream.write(msg) stream.write(self.terminator) self.flush() - except (KeyboardInterrupt, SystemExit): #pragma: no cover - raise - except: + except Exception: self.handleError(record) class FileHandler(StreamHandler): @@ -1686,7 +1723,7 @@ def basicConfig(**kwargs): _acquireLock() try: if len(root.handlers) == 0: - handlers = kwargs.get("handlers") + handlers = kwargs.pop("handlers", None) if handlers is None: if "stream" in kwargs and "filename" in kwargs: raise ValueError("'stream' and 'filename' should not be " @@ -1696,28 +1733,31 @@ def basicConfig(**kwargs): raise ValueError("'stream' or 'filename' should not be " "specified together with 'handlers'") if handlers is None: - filename = kwargs.get("filename") + filename = kwargs.pop("filename", None) if filename: - mode = kwargs.get("filemode", 'a') + mode = kwargs.pop("filemode", 'a') h = FileHandler(filename, mode) else: - stream = kwargs.get("stream") + stream = kwargs.pop("stream", None) h = StreamHandler(stream) handlers = [h] - dfs = kwargs.get("datefmt", None) - style = kwargs.get("style", '%') + dfs = kwargs.pop("datefmt", None) + style = kwargs.pop("style", '%') if style not in _STYLES: raise ValueError('Style must be one of: %s' % ','.join( _STYLES.keys())) - fs = kwargs.get("format", _STYLES[style][1]) + fs = kwargs.pop("format", _STYLES[style][1]) fmt = Formatter(fs, dfs, style) for h in handlers: if h.formatter is None: h.setFormatter(fmt) root.addHandler(h) - level = kwargs.get("level") + level = kwargs.pop("level", None) if level is not None: root.setLevel(level) + if kwargs: + keys = ', '.join(kwargs.keys()) + raise ValueError('Unrecognised argument(s): %s' % keys) finally: _releaseLock() @@ -1836,7 +1876,7 @@ def shutdown(handlerList=_handlerList): h.acquire() h.flush() h.close() - except (IOError, ValueError): + except (OSError, ValueError): # Ignore errors which might be caused # because handlers have been closed but # references to them are still around at @@ -1844,7 +1884,7 @@ def shutdown(handlerList=_handlerList): pass finally: h.release() - except: + except: # ignore everything, as we're shutting down if raiseExceptions: raise #else, swallow |