diff options
Diffstat (limited to 'sphinx/util/logging.py')
-rw-r--r-- | sphinx/util/logging.py | 144 |
1 files changed, 88 insertions, 56 deletions
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 09cef4b48..6e661400f 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ sphinx.util.logging ~~~~~~~~~~~~~~~~~~~ @@ -8,7 +7,6 @@ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -from __future__ import absolute_import import logging import logging.handlers @@ -17,7 +15,6 @@ from contextlib import contextmanager from docutils import nodes from docutils.utils import get_source_line -from six import PY2, StringIO from sphinx.errors import SphinxWarning from sphinx.util.console import colorize @@ -50,12 +47,12 @@ VERBOSITY_MAP.update({ 2: logging.DEBUG, }) -COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode] -COLOR_MAP.update({ - logging.ERROR: 'darkred', - logging.WARNING: 'red', - logging.DEBUG: 'darkgray', -}) +COLOR_MAP = defaultdict(lambda: 'blue', + { + logging.ERROR: 'darkred', + logging.WARNING: 'red', + logging.DEBUG: 'darkgray' + }) def getLogger(name): @@ -101,7 +98,7 @@ class SphinxLogRecord(logging.LogRecord): def getMessage(self): # type: () -> str - message = super(SphinxLogRecord, self).getMessage() + message = super().getMessage() location = getattr(self, 'location', None) if location: message = '%s: %s%s' % (location, self.prefix, message) @@ -125,19 +122,19 @@ class SphinxLoggerAdapter(logging.LoggerAdapter): """LoggerAdapter allowing ``type`` and ``subtype`` keywords.""" def log(self, level, msg, *args, **kwargs): # type: ignore - # type: (Union[int, str], unicode, Any, Any) -> None + # type: (Union[int, str], str, Any, Any) -> None if isinstance(level, int): - super(SphinxLoggerAdapter, self).log(level, msg, *args, **kwargs) + super().log(level, msg, *args, **kwargs) else: levelno = LEVEL_NAMES[level] - super(SphinxLoggerAdapter, self).log(levelno, msg, *args, **kwargs) + super().log(levelno, msg, *args, **kwargs) def verbose(self, msg, *args, **kwargs): - # type: (unicode, Any, Any) -> None + # type: (str, Any, Any) -> None self.log(VERBOSE, msg, *args, **kwargs) def process(self, msg, kwargs): # type: ignore - # type: (unicode, Dict) -> Tuple[unicode, Dict] + # type: (str, Dict) -> Tuple[str, Dict] extra = kwargs.setdefault('extra', {}) if 'type' in kwargs: extra['type'] = kwargs.pop('type') @@ -162,28 +159,7 @@ class WarningStreamHandler(logging.StreamHandler): pass -class NewLineStreamHandlerPY2(logging.StreamHandler): - """StreamHandler which switches line terminator by record.nonl flag.""" - - def emit(self, record): - # type: (logging.LogRecord) -> None - try: - self.acquire() - stream = self.stream - if getattr(record, 'nonl', False): - # remove return code forcely when nonl=True - self.stream = StringIO() - super(NewLineStreamHandlerPY2, self).emit(record) - stream.write(self.stream.getvalue()[:-1]) - stream.flush() - else: - super(NewLineStreamHandlerPY2, self).emit(record) - finally: - self.stream = stream - self.release() - - -class NewLineStreamHandlerPY3(logging.StreamHandler): +class NewLineStreamHandler(logging.StreamHandler): """StreamHandler which switches line terminator by record.nonl flag.""" def emit(self, record): @@ -193,24 +169,18 @@ class NewLineStreamHandlerPY3(logging.StreamHandler): if getattr(record, 'nonl', False): # skip appending terminator when nonl=True self.terminator = '' - super(NewLineStreamHandlerPY3, self).emit(record) + super().emit(record) finally: self.terminator = '\n' self.release() -if PY2: - NewLineStreamHandler = NewLineStreamHandlerPY2 -else: - NewLineStreamHandler = NewLineStreamHandlerPY3 - - class MemoryHandler(logging.handlers.BufferingHandler): """Handler buffering all logs.""" def __init__(self): # type: () -> None - super(MemoryHandler, self).__init__(-1) + super().__init__(-1) def shouldFlush(self, record): # type: (logging.LogRecord) -> bool @@ -315,7 +285,54 @@ def skip_warningiserror(skip=True): handler.removeFilter(disabler) -class LogCollector(object): +@contextmanager +def prefixed_warnings(prefix): + # type: (str) -> Generator + """Prepend prefix to all records for a while. + + For example:: + + >>> with prefixed_warnings("prefix:"): + >>> logger.warning('Warning message!') # => prefix: Warning message! + + .. versionadded:: 2.0 + """ + logger = logging.getLogger(NAMESPACE) + warning_handler = None + for handler in logger.handlers: + if isinstance(handler, WarningStreamHandler): + warning_handler = handler + break + else: + # warning stream not found + yield + return + + prefix_filter = None + for _filter in warning_handler.filters: + if isinstance(_filter, MessagePrefixFilter): + prefix_filter = _filter + break + + if prefix_filter: + # already prefixed + try: + previous = prefix_filter.prefix + prefix_filter.prefix = prefix + yield + finally: + prefix_filter.prefix = previous + else: + # not prefixed yet + try: + prefix_filter = MessagePrefixFilter(prefix) + warning_handler.addFilter(prefix_filter) + yield + finally: + warning_handler.removeFilter(prefix_filter) + + +class LogCollector: def __init__(self): # type: () -> None self.logs = [] # type: List[logging.LogRecord] @@ -341,7 +358,7 @@ class InfoFilter(logging.Filter): def is_suppressed_warning(type, subtype, suppress_warnings): - # type: (unicode, unicode, List[unicode]) -> bool + # type: (str, str, List[str]) -> bool """Check the warning is suppressed or not.""" if type is None: return False @@ -366,7 +383,7 @@ class WarningSuppressor(logging.Filter): def __init__(self, app): # type: (Sphinx) -> None self.app = app - super(WarningSuppressor, self).__init__() + super().__init__() def filter(self, record): # type: (logging.LogRecord) -> bool @@ -392,7 +409,7 @@ class WarningIsErrorFilter(logging.Filter): def __init__(self, app): # type: (Sphinx) -> None self.app = app - super(WarningIsErrorFilter, self).__init__() + super().__init__() def filter(self, record): # type: (logging.LogRecord) -> bool @@ -423,6 +440,21 @@ class DisableWarningIsErrorFilter(logging.Filter): return True +class MessagePrefixFilter(logging.Filter): + """Prepend prefix to all records.""" + + def __init__(self, prefix): + # type: (str) -> None + self.prefix = prefix + super().__init__() + + def filter(self, record): + # type: (logging.LogRecord) -> bool + if self.prefix: + record.msg = self.prefix + ' ' + record.msg + return True + + class SphinxLogRecordTranslator(logging.Filter): """Converts a log record to one Sphinx expects @@ -434,7 +466,7 @@ class SphinxLogRecordTranslator(logging.Filter): def __init__(self, app): # type: (Sphinx) -> None self.app = app - super(SphinxLogRecordTranslator, self).__init__() + super().__init__() def filter(self, record): # type: ignore # type: (SphinxWarningLogRecord) -> bool @@ -485,18 +517,18 @@ def get_node_location(node): class ColorizeFormatter(logging.Formatter): def format(self, record): # type: (logging.LogRecord) -> str - message = super(ColorizeFormatter, self).format(record) + message = super().format(record) color = getattr(record, 'color', None) if color is None: color = COLOR_MAP.get(record.levelno) if color: - return colorize(color, message) # type: ignore + return colorize(color, message) else: return message -class SafeEncodingWriter(object): +class SafeEncodingWriter: """Stream writer which ignores UnicodeEncodeError silently""" def __init__(self, stream): # type: (IO) -> None @@ -504,7 +536,7 @@ class SafeEncodingWriter(object): self.encoding = getattr(stream, 'encoding', 'ascii') or 'ascii' def write(self, data): - # type: (unicode) -> None + # type: (str) -> None try: self.stream.write(data) except UnicodeEncodeError: @@ -518,14 +550,14 @@ class SafeEncodingWriter(object): self.stream.flush() -class LastMessagesWriter(object): +class LastMessagesWriter: """Stream writer which memories last 10 messages to save trackback""" def __init__(self, app, stream): # type: (Sphinx, IO) -> None self.app = app def write(self, data): - # type: (unicode) -> None + # type: (str) -> None self.app.messagelog.append(data) |