1b2732e9dSimarom"""Miscellaneous utility functions and classes.
2b2732e9dSimarom
3b2732e9dSimaromThis module is used internally by Tornado.  It is not necessarily expected
4b2732e9dSimaromthat the functions and classes defined here will be useful to other
5b2732e9dSimaromapplications, but they are documented here in case they are.
6b2732e9dSimarom
7b2732e9dSimaromThe one public-facing part of this module is the `Configurable` class
8b2732e9dSimaromand its `~Configurable.configure` method, which becomes a part of the
9b2732e9dSimarominterface of its subclasses, including `.AsyncHTTPClient`, `.IOLoop`,
10b2732e9dSimaromand `.Resolver`.
11b2732e9dSimarom"""
12b2732e9dSimarom
13b2732e9dSimaromfrom __future__ import absolute_import, division, print_function, with_statement
14b2732e9dSimarom
15b2732e9dSimaromimport sys
16b2732e9dSimarom
17b2732e9dSimarom
18b2732e9dSimaromdef import_object(name):
19b2732e9dSimarom    """Imports an object by name.
20b2732e9dSimarom
21b2732e9dSimarom    import_object('x') is equivalent to 'import x'.
22b2732e9dSimarom    import_object('x.y.z') is equivalent to 'from x.y import z'.
23b2732e9dSimarom
24b2732e9dSimarom    >>> import tornado.escape
25b2732e9dSimarom    >>> import_object('tornado.escape') is tornado.escape
26b2732e9dSimarom    True
27b2732e9dSimarom    >>> import_object('tornado.escape.utf8') is tornado.escape.utf8
28b2732e9dSimarom    True
29b2732e9dSimarom    >>> import_object('tornado') is tornado
30b2732e9dSimarom    True
31b2732e9dSimarom    >>> import_object('tornado.missing_module')
32b2732e9dSimarom    Traceback (most recent call last):
33b2732e9dSimarom        ...
34b2732e9dSimarom    ImportError: No module named missing_module
35b2732e9dSimarom    """
36b2732e9dSimarom    if name.count('.') == 0:
37b2732e9dSimarom        return __import__(name, None, None)
38b2732e9dSimarom
39b2732e9dSimarom    parts = name.split('.')
40b2732e9dSimarom    obj = __import__('.'.join(parts[:-1]), None, None, [parts[-1]], 0)
41b2732e9dSimarom    try:
42b2732e9dSimarom        return getattr(obj, parts[-1])
43b2732e9dSimarom    except AttributeError:
44b2732e9dSimarom        raise ImportError("No module named %s" % parts[-1])
45b2732e9dSimarom
46b2732e9dSimarom
47b2732e9dSimarom# Fake unicode literal support:  Python 3.2 doesn't have the u'' marker for
48b2732e9dSimarom# literal strings, and alternative solutions like "from __future__ import
49b2732e9dSimarom# unicode_literals" have other problems (see PEP 414).  u() can be applied
50b2732e9dSimarom# to ascii strings that include \u escapes (but they must not contain
51b2732e9dSimarom# literal non-ascii characters).
52b2732e9dSimaromif type('') is not type(b''):
53b2732e9dSimarom    def u(s):
54b2732e9dSimarom        return s
55b2732e9dSimarom    bytes_type = bytes
56b2732e9dSimarom    unicode_type = str
57b2732e9dSimarom    basestring_type = str
58b2732e9dSimaromelse:
59b2732e9dSimarom    def u(s):
60b2732e9dSimarom        return s.decode('unicode_escape')
61b2732e9dSimarom    bytes_type = str
62b2732e9dSimarom    unicode_type = unicode
63b2732e9dSimarom    basestring_type = basestring
64b2732e9dSimarom
65b2732e9dSimarom
66b2732e9dSimaromif sys.version_info > (3,):
67b2732e9dSimarom    exec("""
68b2732e9dSimaromdef raise_exc_info(exc_info):
69b2732e9dSimarom    raise exc_info[1].with_traceback(exc_info[2])
70b2732e9dSimarom
71b2732e9dSimaromdef exec_in(code, glob, loc=None):
72b2732e9dSimarom    if isinstance(code, str):
73b2732e9dSimarom        code = compile(code, '<string>', 'exec', dont_inherit=True)
74b2732e9dSimarom    exec(code, glob, loc)
75b2732e9dSimarom""")
76b2732e9dSimaromelse:
77b2732e9dSimarom    exec("""
78b2732e9dSimaromdef raise_exc_info(exc_info):
79b2732e9dSimarom    raise exc_info[0], exc_info[1], exc_info[2]
80b2732e9dSimarom
81b2732e9dSimaromdef exec_in(code, glob, loc=None):
82b2732e9dSimarom    if isinstance(code, basestring):
83b2732e9dSimarom        # exec(string) inherits the caller's future imports; compile
84b2732e9dSimarom        # the string first to prevent that.
85b2732e9dSimarom        code = compile(code, '<string>', 'exec', dont_inherit=True)
86b2732e9dSimarom    exec code in glob, loc
87b2732e9dSimarom""")
88b2732e9dSimarom
89b2732e9dSimarom
90b2732e9dSimaromclass Configurable(object):
91b2732e9dSimarom    """Base class for configurable interfaces.
92b2732e9dSimarom
93b2732e9dSimarom    A configurable interface is an (abstract) class whose constructor
94b2732e9dSimarom    acts as a factory function for one of its implementation subclasses.
95b2732e9dSimarom    The implementation subclass as well as optional keyword arguments to
96b2732e9dSimarom    its initializer can be set globally at runtime with `configure`.
97b2732e9dSimarom
98b2732e9dSimarom    By using the constructor as the factory method, the interface
99b2732e9dSimarom    looks like a normal class, `isinstance` works as usual, etc.  This
100b2732e9dSimarom    pattern is most useful when the choice of implementation is likely
101b2732e9dSimarom    to be a global decision (e.g. when `~select.epoll` is available,
102b2732e9dSimarom    always use it instead of `~select.select`), or when a
103b2732e9dSimarom    previously-monolithic class has been split into specialized
104b2732e9dSimarom    subclasses.
105b2732e9dSimarom
106b2732e9dSimarom    Configurable subclasses must define the class methods
107b2732e9dSimarom    `configurable_base` and `configurable_default`, and use the instance
108b2732e9dSimarom    method `initialize` instead of ``__init__``.
109b2732e9dSimarom    """
110b2732e9dSimarom    __impl_class = None
111b2732e9dSimarom    __impl_kwargs = None
112b2732e9dSimarom
113b2732e9dSimarom    def __new__(cls, **kwargs):
114b2732e9dSimarom        base = cls.configurable_base()
115b2732e9dSimarom        args = {}
116b2732e9dSimarom        if cls is base:
117b2732e9dSimarom            impl = cls.configured_class()
118b2732e9dSimarom            if base.__impl_kwargs:
119b2732e9dSimarom                args.update(base.__impl_kwargs)
120b2732e9dSimarom        else:
121b2732e9dSimarom            impl = cls
122b2732e9dSimarom        args.update(kwargs)
123b2732e9dSimarom        instance = super(Configurable, cls).__new__(impl)
124b2732e9dSimarom        # initialize vs __init__ chosen for compatiblity with AsyncHTTPClient
125b2732e9dSimarom        # singleton magic.  If we get rid of that we can switch to __init__
126b2732e9dSimarom        # here too.
127b2732e9dSimarom        instance.initialize(**args)
128b2732e9dSimarom        return instance
129b2732e9dSimarom
130b2732e9dSimarom    @classmethod
131b2732e9dSimarom    def configurable_base(cls):
132b2732e9dSimarom        """Returns the base class of a configurable hierarchy.
133b2732e9dSimarom
134b2732e9dSimarom        This will normally return the class in which it is defined.
135b2732e9dSimarom        (which is *not* necessarily the same as the cls classmethod parameter).
136b2732e9dSimarom        """
137b2732e9dSimarom        raise NotImplementedError()
138b2732e9dSimarom
139b2732e9dSimarom    @classmethod
140b2732e9dSimarom    def configurable_default(cls):
141b2732e9dSimarom        """Returns the implementation class to be used if none is configured."""
142b2732e9dSimarom        raise NotImplementedError()
143b2732e9dSimarom
144b2732e9dSimarom    def initialize(self):
145b2732e9dSimarom        """Initialize a `Configurable` subclass instance.
146b2732e9dSimarom
147b2732e9dSimarom        Configurable classes should use `initialize` instead of ``__init__``.
148b2732e9dSimarom        """
149b2732e9dSimarom
150b2732e9dSimarom    @classmethod
151b2732e9dSimarom    def configure(cls, impl, **kwargs):
152b2732e9dSimarom        """Sets the class to use when the base class is instantiated.
153b2732e9dSimarom
154b2732e9dSimarom        Keyword arguments will be saved and added to the arguments passed
155b2732e9dSimarom        to the constructor.  This can be used to set global defaults for
156b2732e9dSimarom        some parameters.
157b2732e9dSimarom        """
158b2732e9dSimarom        base = cls.configurable_base()
159b2732e9dSimarom        if isinstance(impl, (unicode_type, bytes_type)):
160b2732e9dSimarom            impl = import_object(impl)
161b2732e9dSimarom        if impl is not None and not issubclass(impl, cls):
162b2732e9dSimarom            raise ValueError("Invalid subclass of %s" % cls)
163b2732e9dSimarom        base.__impl_class = impl
164b2732e9dSimarom        base.__impl_kwargs = kwargs
165b2732e9dSimarom
166b2732e9dSimarom    @classmethod
167b2732e9dSimarom    def configured_class(cls):
168b2732e9dSimarom        """Returns the currently configured class."""
169b2732e9dSimarom        base = cls.configurable_base()
170b2732e9dSimarom        if cls.__impl_class is None:
171b2732e9dSimarom            base.__impl_class = cls.configurable_default()
172b2732e9dSimarom        return base.__impl_class
173b2732e9dSimarom
174b2732e9dSimarom    @classmethod
175b2732e9dSimarom    def _save_configuration(cls):
176b2732e9dSimarom        base = cls.configurable_base()
177b2732e9dSimarom        return (base.__impl_class, base.__impl_kwargs)
178b2732e9dSimarom
179b2732e9dSimarom    @classmethod
180b2732e9dSimarom    def _restore_configuration(cls, saved):
181b2732e9dSimarom        base = cls.configurable_base()
182b2732e9dSimarom        base.__impl_class = saved[0]
183b2732e9dSimarom        base.__impl_kwargs = saved[1]
184b2732e9dSimarom
185