Source code for webapp2_extras.local

# -*- coding: utf-8 -*-
# Copyright 2011 webapp2 AUTHORS.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
webapp2_extras.local
~~~~~~~~~~~~~~~~~~~~

This module implements thread-local utilities.

This implementation comes from werkzeug.local.
"""
import six

try:
    from greenlet import getcurrent as get_current_greenlet
except ImportError:  # pragma: no cover
    try:
        from py.magic import greenlet

        get_current_greenlet = greenlet.getcurrent
        del greenlet
    except:
        # catch all, py.* fails with so many different errors.
        get_current_greenlet = int
try:
    from six.moves._thread import allocate_lock
    from six.moves._thread import get_ident as get_current_thread
except ImportError:  # pragma: no cover
    from six.moves._dummy_thread import allocate_lock
    from six.moves._dummy_thread import get_ident as get_current_thread

# get the best ident function.  if greenlets are not installed we can
# safely just use the builtin thread function and save a python methodcall
# and the cost of calculating a hash.
if get_current_greenlet is int:  # pragma: no cover
    get_ident = get_current_thread
else:
    def get_ident():
        return get_current_thread(), get_current_greenlet()


if six.PY3:  # pragma: no cover
    long = int


[docs]class Local(object): """A container for thread-local objects. Attributes are assigned or retrieved using the current thread. """ __slots__ = ('__storage__', '__lock__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__lock__', allocate_lock()) def __iter__(self): return six.iteritems(self.__storage__) def __call__(self, proxy): """Creates a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(get_ident(), None) def __getattr__(self, name): self.__lock__.acquire() try: try: return self.__storage__[get_ident()][name] except KeyError: raise AttributeError(name) finally: self.__lock__.release() def __setattr__(self, name, value): self.__lock__.acquire() try: ident = get_ident() storage = self.__storage__ if ident in storage: storage[ident][name] = value else: storage[ident] = {name: value} finally: self.__lock__.release() def __delattr__(self, name): self.__lock__.acquire() try: try: del self.__storage__[get_ident()][name] except KeyError: raise AttributeError(name) finally: self.__lock__.release()
@six.python_2_unicode_compatible
[docs]class LocalProxy(object): """Acts as a proxy for a local object. Forwards all operations to a proxied object. The only operations not supported for forwarding are right handed operands and any kind of assignment. Example usage:: from webapp2_extras import Local l = Local() # these are proxies request = l('request') user = l('user') Whenever something is bound to l.user or l.request the proxy objects will forward all operations. If no object is bound a :exc:`RuntimeError` will be raised. To create proxies to :class:`Local` object, call the object as shown above. If you want to have a proxy to an object looked up by a function, you can pass a function to the :class:`LocalProxy` constructor:: route_kwargs = LocalProxy(lambda: webapp2.get_request().route_kwargs) """ __slots__ = ('__local', '__dict__', '__name__') def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) def _get_current_object(self): """Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. """ if not hasattr(self.__local, '__release_local__'): return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__) @property def __dict__(self): try: return self._get_current_object().__dict__ except RuntimeError: return AttributeError('__dict__') def __repr__(self): try: obj = self._get_current_object() except RuntimeError: return '<%s unbound>' % self.__class__.__name__ return repr(obj) def __bool__(self): try: return bool(self._get_current_object()) except RuntimeError: return False __nonzero__ = __bool__ def __str__(self): try: return self._get_current_object() except RuntimeError: return repr(self) def __dir__(self): try: return dir(self._get_current_object()) except RuntimeError: return [] def __getattr__(self, name): if name == '__members__': return dir(self._get_current_object()) return getattr(self._get_current_object(), name) def __setitem__(self, key, value): self._get_current_object()[key] = value def __delitem__(self, key): del self._get_current_object()[key] def __setslice__(self, i, j, seq): self._get_current_object()[i:j] = seq def __delslice__(self, i, j): del self._get_current_object()[i:j] def __setattr__(self, attr, value): setattr(self._get_current_object(), attr, value) def __delattr__(self, item): delattr(self._get_current_object(), item) def __lt__(self, other): return self._get_current_object() < other def __le__(self, other): return self._get_current_object() <= other def __eq__(self, other): return self._get_current_object() == other def __ne__(self, other): return self._get_current_object() != other def __gt__(self, other): return self._get_current_object() > other def __ge__(self, other): return self._get_current_object() >= other def __hash__(self): return hash(self._get_current_object()) def __call__(self, *args, **kwargs): return self._get_current_object()(*args, **kwargs) def __len__(self): return len(self._get_current_object()) def __getitem__(self, item): return self._get_current_object()[item] def __iter__(self): return iter(self._get_current_object()) def __contains__(self, item): return item in self._get_current_object() def __getslice__(self, i, j): return self._get_current_object()[i:j] def __add__(self, other): return self._get_current_object() + other def __sub__(self, other): return self._get_current_object() - other def __mul__(self, other): return self._get_current_object() * other def __floordiv__(self, other): return self._get_current_object() // other def __mod__(self, other): return self._get_current_object() % other def __divmod__(self, other): return self._get_current_object().__divmod__(other) def __pow__(self, o): return self._get_current_object() ** o def __lshift__(self, other): return self._get_current_object() << other def __rshift__(self, other): return self._get_current_object() >> other def __and__(self, other): return self._get_current_object() & other def __xor__(self, other): return self._get_current_object() ^ other def __or__(self, other): return self._get_current_object() | other def __div__(self, other): return self._get_current_object().__div__(other) def __truediv__(self, other): return self._get_current_object().__truediv__(other) def __neg__(self): return -(self._get_current_object()) def __pos__(self): return +(self._get_current_object()) def __abs__(self): return abs(self._get_current_object()) def __invert__(self): return ~(self._get_current_object()) def __complex__(self): return complex(self._get_current_object()) def __int__(self): return int(self._get_current_object()) def __long__(self): if six.PY2: return long(self._get_current_object()) return None def __float__(self): return float(self._get_current_object()) def __oct__(self): return oct(self._get_current_object()) def __hex__(self): return hex(self._get_current_object()) def __index__(self): return self._get_current_object().__index__() def __coerce__(self, other): return self.__coerce__(other) def __enter__(self): return self.__enter__() def __exit__(self, *a, **kw): return self.__exit__(*a, **kw)