216 lines
7.3 KiB
Python
216 lines
7.3 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 sys
|
|
|
|
|
|
# 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 not isinstance(b'', type('')):
|
|
def u(s):
|
|
return s
|
|
unicode_type = str
|
|
basestring_type = str
|
|
else:
|
|
def u(s):
|
|
return s.decode('unicode_escape')
|
|
# These names don't exist in py3, so use noqa comments to disable
|
|
# warnings in flake8.
|
|
unicode_type = unicode # noqa
|
|
basestring_type = basestring # noqa
|
|
|
|
|
|
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 isinstance(name, unicode_type) and str is not unicode_type:
|
|
# On python 2 a byte string is required.
|
|
name = name.encode('utf-8')
|
|
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])
|
|
|
|
|
|
# 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, *args, **kwargs):
|
|
base = cls.configurable_base()
|
|
init_kwargs = {}
|
|
if cls is base:
|
|
impl = cls.configured_class()
|
|
if base.__impl_kwargs:
|
|
init_kwargs.update(base.__impl_kwargs)
|
|
else:
|
|
impl = cls
|
|
init_kwargs.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, **init_kwargs)
|
|
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__``.
|
|
|
|
.. versionchanged:: 4.2
|
|
Now accepts positional arguments in addition to keyword arguments.
|
|
"""
|
|
|
|
@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]
|
|
|
|
|
|
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)
|