123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- """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)
|