mirror of
https://github.com/gryf/ebook-converter.git
synced 2025-12-28 12:12:26 +01:00
276 lines
7.0 KiB
Python
276 lines
7.0 KiB
Python
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
__license__ = 'GPL 3'
|
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
|
__docformat__ = 'restructuredtext en'
|
|
|
|
'A simplified logging system'
|
|
|
|
DEBUG = 0
|
|
INFO = 1
|
|
WARN = 2
|
|
ERROR = 3
|
|
|
|
import sys, traceback, io
|
|
from functools import partial
|
|
from threading import Lock
|
|
|
|
from calibre import isbytestring, force_unicode, as_unicode, prints
|
|
from polyglot.builtins import unicode_type, iteritems
|
|
|
|
|
|
class Stream(object):
|
|
|
|
def __init__(self, stream=None):
|
|
if stream is None:
|
|
stream = io.BytesIO()
|
|
self.stream = getattr(stream, 'buffer', stream)
|
|
self._prints = partial(prints, safe_encode=True, file=stream)
|
|
|
|
def flush(self):
|
|
self.stream.flush()
|
|
|
|
def prints(self, level, *args, **kwargs):
|
|
self._prints(*args, **kwargs)
|
|
|
|
|
|
class ANSIStream(Stream):
|
|
|
|
def __init__(self, stream=sys.stdout):
|
|
Stream.__init__(self, stream)
|
|
self.color = {
|
|
DEBUG: u'green',
|
|
INFO: None,
|
|
WARN: u'yellow',
|
|
ERROR: u'red',
|
|
}
|
|
|
|
def prints(self, level, *args, **kwargs):
|
|
from calibre.utils.terminal import ColoredStream
|
|
with ColoredStream(self.stream, self.color[level]):
|
|
self._prints(*args, **kwargs)
|
|
|
|
def flush(self):
|
|
self.stream.flush()
|
|
|
|
|
|
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: b'<span style="color:green">',
|
|
INFO: b'<span>',
|
|
WARN: b'<span style="color:blue">',
|
|
ERROR: b'<span style="color:red">'
|
|
}
|
|
normal = b'</span>'
|
|
|
|
def __init__(self, stream=sys.stdout):
|
|
Stream.__init__(self, stream)
|
|
|
|
def prints(self, level, *args, **kwargs):
|
|
self.stream.write(self.color[level])
|
|
kwargs['file'] = self.stream
|
|
self._prints(*args, **kwargs)
|
|
self.stream.write(self.normal)
|
|
|
|
def flush(self):
|
|
self.stream.flush()
|
|
|
|
|
|
class UnicodeHTMLStream(HTMLStream):
|
|
|
|
color = {k: v.decode('ascii') for k, v in iteritems(HTMLStream.color)}
|
|
normal = HTMLStream.normal.decode('ascii')
|
|
|
|
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(u'sep', u' ')
|
|
end = kwargs.get(u'end', u'\n')
|
|
|
|
for arg in args:
|
|
if isbytestring(arg):
|
|
arg = force_unicode(arg)
|
|
elif not isinstance(arg, unicode_type):
|
|
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 u''
|
|
return u''.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(object):
|
|
|
|
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 u''.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()
|