mirror of
https://github.com/janeczku/calibre-web
synced 2024-10-31 15:20:28 +00:00
bbf6d9b026
Bugfix for feeds - removed categories related and up - load new books now working - category random now working login page is free of non accessible elements boolean custom column is vivible in UI books with only with certain languages can be shown book shelfs can be deleted from UI Anonymous user view is more resticted Added browse of series in sidebar Dependencys in vendor folder are updated to newer versions (licencs files are now present) Bugfix editing Authors names Made upload on windows working
358 lines
12 KiB
Python
358 lines
12 KiB
Python
"""Miscellaneous utility functions and classes.
|
|
|
|
This module is used internally by Tornado. It is not necessarily expected
|
|
that the functions and classes defined here will be useful to other
|
|
applications, but they are documented here in case they are.
|
|
|
|
The one public-facing part of this module is the `Configurable` class
|
|
and its `~Configurable.configure` method, which becomes a part of the
|
|
interface of its subclasses, including `.AsyncHTTPClient`, `.IOLoop`,
|
|
and `.Resolver`.
|
|
"""
|
|
|
|
from __future__ import absolute_import, division, print_function, with_statement
|
|
|
|
import array
|
|
import inspect
|
|
import os
|
|
import sys
|
|
import zlib
|
|
|
|
|
|
try:
|
|
xrange # py2
|
|
except NameError:
|
|
xrange = range # py3
|
|
|
|
|
|
class ObjectDict(dict):
|
|
"""Makes a dictionary behave like an object, with attribute-style access.
|
|
"""
|
|
def __getattr__(self, name):
|
|
try:
|
|
return self[name]
|
|
except KeyError:
|
|
raise AttributeError(name)
|
|
|
|
def __setattr__(self, name, value):
|
|
self[name] = value
|
|
|
|
|
|
class GzipDecompressor(object):
|
|
"""Streaming gzip decompressor.
|
|
|
|
The interface is like that of `zlib.decompressobj` (without some of the
|
|
optional arguments, but it understands gzip headers and checksums.
|
|
"""
|
|
def __init__(self):
|
|
# Magic parameter makes zlib module understand gzip header
|
|
# http://stackoverflow.com/questions/1838699/how-can-i-decompress-a-gzip-stream-with-zlib
|
|
# This works on cpython and pypy, but not jython.
|
|
self.decompressobj = zlib.decompressobj(16 + zlib.MAX_WBITS)
|
|
|
|
def decompress(self, value, max_length=None):
|
|
"""Decompress a chunk, returning newly-available data.
|
|
|
|
Some data may be buffered for later processing; `flush` must
|
|
be called when there is no more input data to ensure that
|
|
all data was processed.
|
|
|
|
If ``max_length`` is given, some input data may be left over
|
|
in ``unconsumed_tail``; you must retrieve this value and pass
|
|
it back to a future call to `decompress` if it is not empty.
|
|
"""
|
|
return self.decompressobj.decompress(value, max_length)
|
|
|
|
@property
|
|
def unconsumed_tail(self):
|
|
"""Returns the unconsumed portion left over
|
|
"""
|
|
return self.decompressobj.unconsumed_tail
|
|
|
|
def flush(self):
|
|
"""Return any remaining buffered data not yet returned by decompress.
|
|
|
|
Also checks for errors such as truncated input.
|
|
No other methods may be called on this object after `flush`.
|
|
"""
|
|
return self.decompressobj.flush()
|
|
|
|
|
|
def import_object(name):
|
|
"""Imports an object by name.
|
|
|
|
import_object('x') is equivalent to 'import x'.
|
|
import_object('x.y.z') is equivalent to 'from x.y import z'.
|
|
|
|
>>> import tornado.escape
|
|
>>> import_object('tornado.escape') is tornado.escape
|
|
True
|
|
>>> import_object('tornado.escape.utf8') is tornado.escape.utf8
|
|
True
|
|
>>> import_object('tornado') is tornado
|
|
True
|
|
>>> import_object('tornado.missing_module')
|
|
Traceback (most recent call last):
|
|
...
|
|
ImportError: No module named missing_module
|
|
"""
|
|
if name.count('.') == 0:
|
|
return __import__(name, None, None)
|
|
|
|
parts = name.split('.')
|
|
obj = __import__('.'.join(parts[:-1]), None, None, [parts[-1]], 0)
|
|
try:
|
|
return getattr(obj, parts[-1])
|
|
except AttributeError:
|
|
raise ImportError("No module named %s" % parts[-1])
|
|
|
|
|
|
# Fake unicode literal support: Python 3.2 doesn't have the u'' marker for
|
|
# literal strings, and alternative solutions like "from __future__ import
|
|
# unicode_literals" have other problems (see PEP 414). u() can be applied
|
|
# to ascii strings that include \u escapes (but they must not contain
|
|
# literal non-ascii characters).
|
|
if type('') is not type(b''):
|
|
def u(s):
|
|
return s
|
|
unicode_type = str
|
|
basestring_type = str
|
|
else:
|
|
def u(s):
|
|
return s.decode('unicode_escape')
|
|
unicode_type = unicode
|
|
basestring_type = basestring
|
|
|
|
# Deprecated alias that was used before we dropped py25 support.
|
|
# Left here in case anyone outside Tornado is using it.
|
|
bytes_type = bytes
|
|
|
|
if sys.version_info > (3,):
|
|
exec("""
|
|
def raise_exc_info(exc_info):
|
|
raise exc_info[1].with_traceback(exc_info[2])
|
|
|
|
def exec_in(code, glob, loc=None):
|
|
if isinstance(code, str):
|
|
code = compile(code, '<string>', 'exec', dont_inherit=True)
|
|
exec(code, glob, loc)
|
|
""")
|
|
else:
|
|
exec("""
|
|
def raise_exc_info(exc_info):
|
|
raise exc_info[0], exc_info[1], exc_info[2]
|
|
|
|
def exec_in(code, glob, loc=None):
|
|
if isinstance(code, basestring):
|
|
# exec(string) inherits the caller's future imports; compile
|
|
# the string first to prevent that.
|
|
code = compile(code, '<string>', 'exec', dont_inherit=True)
|
|
exec code in glob, loc
|
|
""")
|
|
|
|
|
|
def errno_from_exception(e):
|
|
"""Provides the errno from an Exception object.
|
|
|
|
There are cases that the errno attribute was not set so we pull
|
|
the errno out of the args but if someone instantiates an Exception
|
|
without any args you will get a tuple error. So this function
|
|
abstracts all that behavior to give you a safe way to get the
|
|
errno.
|
|
"""
|
|
|
|
if hasattr(e, 'errno'):
|
|
return e.errno
|
|
elif e.args:
|
|
return e.args[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
class Configurable(object):
|
|
"""Base class for configurable interfaces.
|
|
|
|
A configurable interface is an (abstract) class whose constructor
|
|
acts as a factory function for one of its implementation subclasses.
|
|
The implementation subclass as well as optional keyword arguments to
|
|
its initializer can be set globally at runtime with `configure`.
|
|
|
|
By using the constructor as the factory method, the interface
|
|
looks like a normal class, `isinstance` works as usual, etc. This
|
|
pattern is most useful when the choice of implementation is likely
|
|
to be a global decision (e.g. when `~select.epoll` is available,
|
|
always use it instead of `~select.select`), or when a
|
|
previously-monolithic class has been split into specialized
|
|
subclasses.
|
|
|
|
Configurable subclasses must define the class methods
|
|
`configurable_base` and `configurable_default`, and use the instance
|
|
method `initialize` instead of ``__init__``.
|
|
"""
|
|
__impl_class = None
|
|
__impl_kwargs = None
|
|
|
|
def __new__(cls, **kwargs):
|
|
base = cls.configurable_base()
|
|
args = {}
|
|
if cls is base:
|
|
impl = cls.configured_class()
|
|
if base.__impl_kwargs:
|
|
args.update(base.__impl_kwargs)
|
|
else:
|
|
impl = cls
|
|
args.update(kwargs)
|
|
instance = super(Configurable, cls).__new__(impl)
|
|
# initialize vs __init__ chosen for compatibility with AsyncHTTPClient
|
|
# singleton magic. If we get rid of that we can switch to __init__
|
|
# here too.
|
|
instance.initialize(**args)
|
|
return instance
|
|
|
|
@classmethod
|
|
def configurable_base(cls):
|
|
"""Returns the base class of a configurable hierarchy.
|
|
|
|
This will normally return the class in which it is defined.
|
|
(which is *not* necessarily the same as the cls classmethod parameter).
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@classmethod
|
|
def configurable_default(cls):
|
|
"""Returns the implementation class to be used if none is configured."""
|
|
raise NotImplementedError()
|
|
|
|
def initialize(self):
|
|
"""Initialize a `Configurable` subclass instance.
|
|
|
|
Configurable classes should use `initialize` instead of ``__init__``.
|
|
"""
|
|
|
|
@classmethod
|
|
def configure(cls, impl, **kwargs):
|
|
"""Sets the class to use when the base class is instantiated.
|
|
|
|
Keyword arguments will be saved and added to the arguments passed
|
|
to the constructor. This can be used to set global defaults for
|
|
some parameters.
|
|
"""
|
|
base = cls.configurable_base()
|
|
if isinstance(impl, (unicode_type, bytes)):
|
|
impl = import_object(impl)
|
|
if impl is not None and not issubclass(impl, cls):
|
|
raise ValueError("Invalid subclass of %s" % cls)
|
|
base.__impl_class = impl
|
|
base.__impl_kwargs = kwargs
|
|
|
|
@classmethod
|
|
def configured_class(cls):
|
|
"""Returns the currently configured class."""
|
|
base = cls.configurable_base()
|
|
if cls.__impl_class is None:
|
|
base.__impl_class = cls.configurable_default()
|
|
return base.__impl_class
|
|
|
|
@classmethod
|
|
def _save_configuration(cls):
|
|
base = cls.configurable_base()
|
|
return (base.__impl_class, base.__impl_kwargs)
|
|
|
|
@classmethod
|
|
def _restore_configuration(cls, saved):
|
|
base = cls.configurable_base()
|
|
base.__impl_class = saved[0]
|
|
base.__impl_kwargs = saved[1]
|
|
|
|
|
|
class ArgReplacer(object):
|
|
"""Replaces one value in an ``args, kwargs`` pair.
|
|
|
|
Inspects the function signature to find an argument by name
|
|
whether it is passed by position or keyword. For use in decorators
|
|
and similar wrappers.
|
|
"""
|
|
def __init__(self, func, name):
|
|
self.name = name
|
|
try:
|
|
self.arg_pos = inspect.getargspec(func).args.index(self.name)
|
|
except ValueError:
|
|
# Not a positional parameter
|
|
self.arg_pos = None
|
|
|
|
def get_old_value(self, args, kwargs, default=None):
|
|
"""Returns the old value of the named argument without replacing it.
|
|
|
|
Returns ``default`` if the argument is not present.
|
|
"""
|
|
if self.arg_pos is not None and len(args) > self.arg_pos:
|
|
return args[self.arg_pos]
|
|
else:
|
|
return kwargs.get(self.name, default)
|
|
|
|
def replace(self, new_value, args, kwargs):
|
|
"""Replace the named argument in ``args, kwargs`` with ``new_value``.
|
|
|
|
Returns ``(old_value, args, kwargs)``. The returned ``args`` and
|
|
``kwargs`` objects may not be the same as the input objects, or
|
|
the input objects may be mutated.
|
|
|
|
If the named argument was not found, ``new_value`` will be added
|
|
to ``kwargs`` and None will be returned as ``old_value``.
|
|
"""
|
|
if self.arg_pos is not None and len(args) > self.arg_pos:
|
|
# The arg to replace is passed positionally
|
|
old_value = args[self.arg_pos]
|
|
args = list(args) # *args is normally a tuple
|
|
args[self.arg_pos] = new_value
|
|
else:
|
|
# The arg to replace is either omitted or passed by keyword.
|
|
old_value = kwargs.get(self.name)
|
|
kwargs[self.name] = new_value
|
|
return old_value, args, kwargs
|
|
|
|
|
|
def timedelta_to_seconds(td):
|
|
"""Equivalent to td.total_seconds() (introduced in python 2.7)."""
|
|
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / float(10 ** 6)
|
|
|
|
|
|
def _websocket_mask_python(mask, data):
|
|
"""Websocket masking function.
|
|
|
|
`mask` is a `bytes` object of length 4; `data` is a `bytes` object of any length.
|
|
Returns a `bytes` object of the same length as `data` with the mask applied
|
|
as specified in section 5.3 of RFC 6455.
|
|
|
|
This pure-python implementation may be replaced by an optimized version when available.
|
|
"""
|
|
mask = array.array("B", mask)
|
|
unmasked = array.array("B", data)
|
|
for i in xrange(len(data)):
|
|
unmasked[i] = unmasked[i] ^ mask[i % 4]
|
|
if hasattr(unmasked, 'tobytes'):
|
|
# tostring was deprecated in py32. It hasn't been removed,
|
|
# but since we turn on deprecation warnings in our tests
|
|
# we need to use the right one.
|
|
return unmasked.tobytes()
|
|
else:
|
|
return unmasked.tostring()
|
|
|
|
if (os.environ.get('TORNADO_NO_EXTENSION') or
|
|
os.environ.get('TORNADO_EXTENSION') == '0'):
|
|
# These environment variables exist to make it easier to do performance
|
|
# comparisons; they are not guaranteed to remain supported in the future.
|
|
_websocket_mask = _websocket_mask_python
|
|
else:
|
|
try:
|
|
from tornado.speedups import websocket_mask as _websocket_mask
|
|
except ImportError:
|
|
if os.environ.get('TORNADO_EXTENSION') == '1':
|
|
raise
|
|
_websocket_mask = _websocket_mask_python
|
|
|
|
|
|
def doctests():
|
|
import doctest
|
|
return doctest.DocTestSuite()
|