%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/utils/ |
| Current File : //usr/lib/calibre/calibre/utils/logging.py |
#!/usr/bin/env python3
# License: GPLv3 Copyright: 2009, Kovid Goyal <kovid at kovidgoyal.net>
# A simplified logging system
import io
import sys
import traceback
from contextlib import suppress
from functools import partial
from threading import Lock
from calibre.prints import prints
from polyglot.builtins import as_unicode
DEBUG = 0
INFO = 1
WARN = 2
ERROR = 3
class Stream:
def __init__(self, stream=None):
if stream is None:
stream = io.StringIO()
self.stream = stream
self.encoding = getattr(self.stream, 'encoding', None) or 'utf-8'
def write(self, text):
self._prints(text, end='')
def flush(self):
with suppress(BrokenPipeError):
# Don't fail if we were logging to a pipe and it got closed
self.stream.flush()
def prints(self, level, *args, **kwargs):
self._prints(*args, **kwargs)
def _prints(self, *args, **kwargs):
prints(*args, **kwargs, file=self.stream)
stdout_sentinel = object()
class ANSIStream(Stream):
def __init__(self, stream=stdout_sentinel):
if stream is stdout_sentinel:
stream = sys.stdout
Stream.__init__(self, stream)
self.color = {
DEBUG: 'green',
INFO: None,
WARN: 'yellow',
ERROR: 'red',
}
def prints(self, level, *args, **kwargs):
from calibre.utils.terminal import ColoredStream
with ColoredStream(self.stream, self.color[level]):
self._prints(*args, **kwargs)
class FileStream(Stream):
def __init__(self, stream=None):
Stream.__init__(self, stream)
def prints(self, level, *args, **kwargs):
self._prints(*args, **kwargs)
class HTMLStream(Stream):
color = {
DEBUG: '<span style="color:green">',
INFO: '<span>',
WARN: '<span style="color:blue">',
ERROR: '<span style="color:red">'
}
normal = '</span>'
def __init__(self, stream=stdout_sentinel):
if stream is stdout_sentinel:
stream = sys.stdout
Stream.__init__(self, stream)
def prints(self, level, *args, **kwargs):
self._prints(self.color[level], end='')
self._prints(*args, **kwargs)
self._prints(self.normal, end='')
class UnicodeHTMLStream(HTMLStream):
def __init__(self):
self.clear()
def flush(self):
pass
def prints(self, level, *args, **kwargs):
col = self.color[level]
if col != self.last_col:
if self.data:
self.data.append(self.normal)
self.data.append(col)
self.last_col = col
sep = kwargs.get('sep', ' ')
end = kwargs.get('end', '\n')
for arg in args:
arg = as_unicode(arg)
self.data.append(arg+sep)
self.plain_text.append(arg+sep)
self.data.append(end)
self.plain_text.append(end)
def clear(self):
self.data = []
self.plain_text = []
self.last_col = self.color[INFO]
@property
def html(self):
end = self.normal if self.data else ''
return ''.join(self.data) + end
def dump(self):
return [self.data, self.plain_text, self.last_col]
def load(self, dump):
self.data, self.plain_text, self.last_col = dump
def append_dump(self, dump):
d, p, lc = dump
self.data.extend(d)
self.plain_text.extend(p)
self.last_col = lc
class Log:
DEBUG = DEBUG
INFO = INFO
WARN = WARN
ERROR = ERROR
def __init__(self, level=INFO):
self.filter_level = level
default_output = ANSIStream()
self.outputs = [default_output]
self.debug = partial(self.print_with_flush, DEBUG)
self.info = partial(self.print_with_flush, INFO)
self.warn = self.warning = partial(self.print_with_flush, WARN)
self.error = partial(self.print_with_flush, ERROR)
def prints(self, level, *args, **kwargs):
if level < self.filter_level:
return
for output in self.outputs:
output.prints(level, *args, **kwargs)
def print_with_flush(self, level, *args, **kwargs):
if level < self.filter_level:
return
for output in self.outputs:
output.prints(level, *args, **kwargs)
self.flush()
def exception(self, *args, **kwargs):
limit = kwargs.pop('limit', None)
self.print_with_flush(ERROR, *args, **kwargs)
self.print_with_flush(DEBUG, traceback.format_exc(limit))
def __call__(self, *args, **kwargs):
self.info(*args, **kwargs)
def __enter__(self):
self.orig_filter_level = self.filter_level
self.filter_level = self.ERROR + 100
def __exit__(self, *args):
self.filter_level = self.orig_filter_level
def flush(self):
for o in self.outputs:
if hasattr(o, 'flush'):
o.flush()
def close(self):
for o in self.outputs:
if hasattr(o, 'close'):
o.close()
class DevNull(Log):
def __init__(self):
Log.__init__(self, level=Log.ERROR)
self.outputs = []
class ThreadSafeLog(Log):
exception_traceback_level = Log.DEBUG
def __init__(self, level=Log.INFO):
Log.__init__(self, level=level)
self._lock = Lock()
def prints(self, *args, **kwargs):
with self._lock:
Log.prints(self, *args, **kwargs)
def print_with_flush(self, *args, **kwargs):
with self._lock:
Log.print_with_flush(self, *args, **kwargs)
def exception(self, *args, **kwargs):
limit = kwargs.pop('limit', None)
with self._lock:
Log.print_with_flush(self, ERROR, *args, **kwargs)
Log.print_with_flush(self, self.exception_traceback_level, traceback.format_exc(limit))
class ThreadSafeWrapper(Log):
def __init__(self, other_log):
Log.__init__(self, level=other_log.filter_level)
self.outputs = list(other_log.outputs)
self._lock = Lock()
def prints(self, *args, **kwargs):
with self._lock:
Log.prints(self, *args, **kwargs)
def print_with_flush(self, *args, **kwargs):
with self._lock:
Log.print_with_flush(self, *args, **kwargs)
class GUILog(ThreadSafeLog):
'''
Logs in HTML and plain text as unicode. Ideal for display in a GUI context.
'''
def __init__(self):
ThreadSafeLog.__init__(self, level=self.DEBUG)
self.outputs = [UnicodeHTMLStream()]
def clear(self):
self.outputs[0].clear()
@property
def html(self):
return self.outputs[0].html
@property
def plain_text(self):
return ''.join(self.outputs[0].plain_text)
def dump(self):
return self.outputs[0].dump()
def load(self, dump):
return self.outputs[0].load(dump)
def append_dump(self, dump):
return self.outputs[0].append_dump(dump)
default_log = Log()