diff options
Diffstat (limited to 'lib/Python/Lib/colorlog')
-rw-r--r-- | lib/Python/Lib/colorlog/__init__.py | 14 | ||||
-rw-r--r-- | lib/Python/Lib/colorlog/colorlog.py | 137 | ||||
-rw-r--r-- | lib/Python/Lib/colorlog/escape_codes.py | 57 | ||||
-rw-r--r-- | lib/Python/Lib/colorlog/logging.py | 44 |
4 files changed, 252 insertions, 0 deletions
diff --git a/lib/Python/Lib/colorlog/__init__.py b/lib/Python/Lib/colorlog/__init__.py new file mode 100644 index 000000000..1d57f586d --- /dev/null +++ b/lib/Python/Lib/colorlog/__init__.py @@ -0,0 +1,14 @@ +"""A logging formatter for colored output.""" + +from __future__ import absolute_import + +from colorlog.colorlog import ( + ColoredFormatter, escape_codes, default_log_colors) + +from colorlog.logging import ( + basicConfig, root, getLogger, log, + debug, info, warning, error, exception, critical) + +__all__ = ('ColoredFormatter', 'default_log_colors', 'escape_codes', + 'basicConfig', 'root', 'getLogger', 'debug', 'info', 'warning', + 'error', 'exception', 'critical', 'log', 'exception') diff --git a/lib/Python/Lib/colorlog/colorlog.py b/lib/Python/Lib/colorlog/colorlog.py new file mode 100644 index 000000000..49860dd50 --- /dev/null +++ b/lib/Python/Lib/colorlog/colorlog.py @@ -0,0 +1,137 @@ +"""The ColoredFormatter class.""" + +from __future__ import absolute_import + +import logging +import collections +import sys + +from colorlog.escape_codes import escape_codes, parse_colors + +__all__ = ('escape_codes', 'default_log_colors', 'ColoredFormatter') + +# The default colors to use for the debug levels +default_log_colors = { + 'DEBUG': 'white', + 'INFO': 'green', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'bold_red', +} + +# The default format to use for each style +default_formats = { + '%': '%(log_color)s%(levelname)s:%(name)s:%(message)s', + '{': '{log_color}{levelname}:{name}:{message}', + '$': '${log_color}${levelname}:${name}:${message}' +} + + +class ColoredRecord(object): + """ + Wraps a LogRecord and attempts to parse missing keys as escape codes. + + When the record is formatted, the logging library uses ``record.__dict__`` + directly - so this class replaced the dict with a ``defaultdict`` that + checks if a missing key is an escape code. + """ + + class __dict(collections.defaultdict): + def __missing__(self, name): + try: + return parse_colors(name) + except Exception: + raise KeyError("{} is not a valid record attribute " + "or color sequence".format(name)) + + def __init__(self, record): + # Replace the internal dict with one that can handle missing keys + self.__dict__ = self.__dict() + self.__dict__.update(record.__dict__) + + # Keep a refrence to the original refrence so ``__getattr__`` can + # access functions that are not in ``__dict__`` + self.__record = record + + def __getattr__(self, name): + return getattr(self.__record, name) + + +class ColoredFormatter(logging.Formatter): + """ + A formatter that allows colors to be placed in the format string. + + Intended to help in creating more readable logging output. + """ + + def __init__(self, fmt=None, datefmt=None, + log_colors=None, reset=True, style='%', + secondary_log_colors=None): + """ + Set the format and colors the ColoredFormatter will use. + + The ``fmt``, ``datefmt`` and ``style`` args are passed on to the + ``logging.Formatter`` constructor. + + The ``secondary_log_colors`` argument can be used to create additional + ``log_color`` attributes. Each key in the dictionary will set + ``log_color_{key}``, using the value to select from a different + ``log_colors`` set. + + :Parameters: + - fmt (str): The format string to use + - datefmt (str): A format string for the date + - log_colors (dict): + A mapping of log level names to color names + - reset (bool): + Implictly append a color reset to all records unless False + - style ('%' or '{' or '$'): + The format style to use. (*No meaning prior to Python 3.2.*) + - secondary_log_colors (dict): + Map secondary ``log_color`` attributes. (*New in version 2.6.*) + """ + if fmt is None: + if sys.version_info > (3, 2): + fmt = default_formats[style] + else: + fmt = default_formats['%'] + + if sys.version_info > (3, 2): + super(ColoredFormatter, self).__init__(fmt, datefmt, style) + elif sys.version_info > (2, 7): + super(ColoredFormatter, self).__init__(fmt, datefmt) + else: + logging.Formatter.__init__(self, fmt, datefmt) + + self.log_colors = ( + log_colors if log_colors is not None else default_log_colors) + self.secondary_log_colors = secondary_log_colors + self.reset = reset + + def color(self, log_colors, name): + """Return escape codes from a ``log_colors`` dict.""" + return parse_colors(log_colors.get(name, "")) + + def format(self, record): + """Format a message from a record object.""" + record = ColoredRecord(record) + record.log_color = self.color(self.log_colors, record.levelname) + + # Set secondary log colors + if self.secondary_log_colors: + for name, log_colors in self.secondary_log_colors.items(): + color = self.color(log_colors, record.levelname) + setattr(record, name + '_log_color', color) + + # Format the message + if sys.version_info > (2, 7): + message = super(ColoredFormatter, self).format(record) + else: + message = logging.Formatter.format(self, record) + + # Add a reset code to the end of the message + # (if it wasn't explicitly added in format str) + if self.reset and not message.endswith(escape_codes['reset']): + message += escape_codes['reset'] + + return message diff --git a/lib/Python/Lib/colorlog/escape_codes.py b/lib/Python/Lib/colorlog/escape_codes.py new file mode 100644 index 000000000..848eb6489 --- /dev/null +++ b/lib/Python/Lib/colorlog/escape_codes.py @@ -0,0 +1,57 @@ +""" +Generates a dictionary of ANSI escape codes. + +http://en.wikipedia.org/wiki/ANSI_escape_code + +Uses colorama as an optional dependancy to support color on Windows +""" + +try: + import colorama +except ImportError: + pass +else: + colorama.init() + +__all__ = ('escape_codes', 'parse_colors') + +# Returns escape codes from format codes +esc = lambda *x: '\033[' + ';'.join(x) + 'm' + +# The initial list of escape codes +escape_codes = { + 'reset': esc('0'), + 'bold': esc('01'), +} + +# The color names +COLORS = [ + 'black', + 'red', + 'green', + 'yellow', + 'blue', + 'purple', + 'cyan', + 'white' +] + +PREFIXES = [ + # Foreground without prefix + ('3', ''), ('01;3', 'bold_'), + + # Foreground with fg_ prefix + ('3', 'fg_'), ('01;3', 'fg_bold_'), + + # Background with bg_ prefix - bold/light works differently + ('4', 'bg_'), ('10', 'bg_bold_'), +] + +for prefix, prefix_name in PREFIXES: + for code, name in enumerate(COLORS): + escape_codes[prefix_name + name] = esc(prefix + str(code)) + + +def parse_colors(sequence): + """Return escape codes from a color sequence.""" + return ''.join(escape_codes[n] for n in sequence.split(',') if n) diff --git a/lib/Python/Lib/colorlog/logging.py b/lib/Python/Lib/colorlog/logging.py new file mode 100644 index 000000000..13f0c4ffb --- /dev/null +++ b/lib/Python/Lib/colorlog/logging.py @@ -0,0 +1,44 @@ +"""Wrappers around the logging module.""" + +from __future__ import absolute_import + +import functools +import logging + +from colorlog.colorlog import ColoredFormatter + +BASIC_FORMAT = "%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s" + + +def basicConfig(**kwargs): + """Call ``logging.basicConfig`` and override the formatter it creates.""" + logging.basicConfig(**kwargs) + logging._acquireLock() + try: + stream = logging.root.handlers[0] + stream.setFormatter( + ColoredFormatter( + fmt=kwargs.get('format', BASIC_FORMAT), + datefmt=kwargs.get('datefmt', None))) + finally: + logging._releaseLock() + + +def ensure_configured(func): + """Modify a function to call ``basicConfig`` first if no handlers exist.""" + @functools.wraps(func) + def wrapper(*args, **kwargs): + if len(logging.root.handlers) == 0: + basicConfig() + return func(*args, **kwargs) + return wrapper + +root = logging.root +getLogger = logging.getLogger +debug = ensure_configured(logging.debug) +info = ensure_configured(logging.info) +warning = ensure_configured(logging.warning) +error = ensure_configured(logging.error) +critical = ensure_configured(logging.critical) +log = ensure_configured(logging.log) +exception = ensure_configured(logging.exception) |