mirror of
https://github.com/gryf/ebook-converter.git
synced 2025-12-28 04:02:27 +01:00
Here is the first batch of modules, which are needed for converting several formats to LRF. Some of the logic has been change, more cleanups will follow.
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 ebook_converter import isbytestring, force_unicode, as_unicode, prints
|
|
from ebook_converter.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 ebook_converter.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()
|