|
|
|
"""Miscellaneous utility functions."""
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, with_statement
|
|
|
|
|
|
|
|
import zlib
|
|
|
|
|
|
|
|
|
|
|
|
class ObjectDict(dict):
|
|
|
|
"""Makes a dictionary behave like an object."""
|
|
|
|
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 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):
|
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
return self.decompressobj.decompress(value)
|
|
|
|
|
|
|
|
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.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
|
|
|
|
"""
|
|
|
|
parts = name.split('.')
|
|
|
|
obj = __import__('.'.join(parts[:-1]), None, None, [parts[-1]], 0)
|
|
|
|
return getattr(obj, parts[-1])
|
|
|
|
|
|
|
|
# Fake byte literal support: In python 2.6+, you can say b"foo" to get
|
|
|
|
# a byte literal (str in 2.x, bytes in 3.x). There's no way to do this
|
|
|
|
# in a way that supports 2.5, though, so we need a function wrapper
|
|
|
|
# to convert our string literals. b() should only be applied to literal
|
|
|
|
# latin1 strings. Once we drop support for 2.5, we can remove this function
|
|
|
|
# and just use byte literals.
|
|
|
|
if str is unicode:
|
|
|
|
def b(s):
|
|
|
|
return s.encode('latin1')
|
|
|
|
bytes_type = bytes
|
|
|
|
else:
|
|
|
|
def b(s):
|
|
|
|
return s
|
|
|
|
bytes_type = str
|
|
|
|
|
|
|
|
|
|
|
|
def raise_exc_info(exc_info):
|
|
|
|
"""Re-raise an exception (with original traceback) from an exc_info tuple.
|
|
|
|
|
|
|
|
The argument is a ``(type, value, traceback)`` tuple as returned by
|
|
|
|
`sys.exc_info`.
|
|
|
|
"""
|
|
|
|
# 2to3 isn't smart enough to convert three-argument raise
|
|
|
|
# statements correctly in some cases.
|
|
|
|
if isinstance(exc_info[1], exc_info[0]):
|
|
|
|
raise exc_info[1], None, exc_info[2]
|
|
|
|
# After 2to3: raise exc_info[1].with_traceback(exc_info[2])
|
|
|
|
else:
|
|
|
|
# I think this branch is only taken for string exceptions,
|
|
|
|
# which were removed in Python 2.6.
|
|
|
|
raise exc_info[0], exc_info[1], exc_info[2]
|
|
|
|
# After 2to3: raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
|
|
|
|
|
|
|
|
|
|
|
|
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 epoll is available, always use it instead of
|
|
|
|
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 compatiblity 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, bytes_type)):
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
|
|
|
def doctests():
|
|
|
|
import doctest
|
|
|
|
return doctest.DocTestSuite()
|