]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Merge remote-tracking branch 'kachayev/import-object'
authorBen Darnell <ben@bendarnell.com>
Sun, 14 Apr 2013 20:03:08 +0000 (16:03 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 14 Apr 2013 20:03:08 +0000 (16:03 -0400)
1  2 
tornado/util.py

diff --cc tornado/util.py
index 939ee6ab1b77d3cbd1e290357e6f8b6a65b5a375,584f917918347d41f707a7377cf7bf8b71c4262a..3e6574315156803f6333b5f4d5323358b43cf252
@@@ -1,25 -1,9 +1,27 @@@
 -"""Miscellaneous utility functions."""
 +"""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 inspect
 +import sys
 +import zlib
 +
  
+ import sys
  class ObjectDict(dict):
 -    """Makes a dictionary behave like an object."""
 +    """Makes a dictionary behave like an object, with attribute-style access.
 +    """
      def __getattr__(self, name):
          try:
              return self[name]
@@@ -70,186 -25,42 +73,203 @@@ def import_object(name)
      True
      >>> import_object('tornado.escape.utf8') is tornado.escape.utf8
      True
+     >>> import_object('tornado') is tornado
+     True
+     >>> import_object('missing_module')
+     Traceback (most recent call last):
+         ...
+     ImportError: No module named missing_module
+     >>> 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)
-     return getattr(obj, parts[-1])
+     try:
+         return getattr(obj, parts[-1])
+     except AttributeError:
+         exc_info = sys.exc_info()
+         raise ImportError, "No module named %s" % parts[-1], exc_info[2] 
  
 -# 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')
 +# 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
      bytes_type = bytes
 +    unicode_type = str
 +    basestring_type = str
  else:
 -    def b(s):
 -        return s
 +    def u(s):
 +        return s.decode('unicode_escape')
      bytes_type = str
 +    unicode_type = unicode
 +    basestring_type = basestring
 +
 +
 +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
 +""")
 +
 +
 +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 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_type, 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]
 +
 +
 +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 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 doctests():
      import doctest