1# -*- coding: utf-8 -*-
2"""
3    webapp2_extras.local
4    ~~~~~~~~~~~~~~~~~~~~
5
6    This module implements thread-local utilities.
7
8    This implementation comes from werkzeug.local.
9
10    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
11    :license: BSD, see LICENSE for more details.
12"""
13try:
14    from greenlet import getcurrent as get_current_greenlet
15except ImportError: # pragma: no cover
16    try:
17        from py.magic import greenlet
18        get_current_greenlet = greenlet.getcurrent
19        del greenlet
20    except:
21        # catch all, py.* fails with so many different errors.
22        get_current_greenlet = int
23try:
24    from thread import get_ident as get_current_thread, allocate_lock
25except ImportError: # pragma: no cover
26    from dummy_thread import get_ident as get_current_thread, allocate_lock
27
28
29# get the best ident function.  if greenlets are not installed we can
30# safely just use the builtin thread function and save a python methodcall
31# and the cost of calculating a hash.
32if get_current_greenlet is int: # pragma: no cover
33    get_ident = get_current_thread
34else:
35    get_ident = lambda: (get_current_thread(), get_current_greenlet())
36
37
38class Local(object):
39    """A container for thread-local objects.
40
41    Attributes are assigned or retrieved using the current thread.
42    """
43
44    __slots__ = ('__storage__', '__lock__')
45
46    def __init__(self):
47        object.__setattr__(self, '__storage__', {})
48        object.__setattr__(self, '__lock__', allocate_lock())
49
50    def __iter__(self):
51        return self.__storage__.iteritems()
52
53    def __call__(self, proxy):
54        """Creates a proxy for a name."""
55        return LocalProxy(self, proxy)
56
57    def __release_local__(self):
58        self.__storage__.pop(get_ident(), None)
59
60    def __getattr__(self, name):
61        self.__lock__.acquire()
62        try:
63            try:
64                return self.__storage__[get_ident()][name]
65            except KeyError:
66                raise AttributeError(name)
67        finally:
68            self.__lock__.release()
69
70    def __setattr__(self, name, value):
71        self.__lock__.acquire()
72        try:
73            ident = get_ident()
74            storage = self.__storage__
75            if ident in storage:
76                storage[ident][name] = value
77            else:
78                storage[ident] = {name: value}
79        finally:
80            self.__lock__.release()
81
82    def __delattr__(self, name):
83        self.__lock__.acquire()
84        try:
85            try:
86                del self.__storage__[get_ident()][name]
87            except KeyError:
88                raise AttributeError(name)
89        finally:
90            self.__lock__.release()
91
92
93class LocalProxy(object):
94    """Acts as a proxy for a local object.
95
96    Forwards all operations to a proxied object. The only operations not
97    supported for forwarding are right handed operands and any kind of
98    assignment.
99
100    Example usage::
101
102        from webapp2_extras import Local
103        l = Local()
104
105        # these are proxies
106        request = l('request')
107        user = l('user')
108
109    Whenever something is bound to l.user or l.request the proxy objects
110    will forward all operations. If no object is bound a :exc:`RuntimeError`
111    will be raised.
112
113    To create proxies to :class:`Local` object, call the object as shown above.
114    If you want to have a proxy to an object looked up by a function, you can
115    pass a function to the :class:`LocalProxy` constructor::
116
117        route_kwargs = LocalProxy(lambda: webapp2.get_request().route_kwargs)
118    """
119
120    __slots__ = ('__local', '__dict__', '__name__')
121
122    def __init__(self, local, name=None):
123        object.__setattr__(self, '_LocalProxy__local', local)
124        object.__setattr__(self, '__name__', name)
125
126    def _get_current_object(self):
127        """Return the current object.  This is useful if you want the real
128        object behind the proxy at a time for performance reasons or because
129        you want to pass the object into a different context.
130        """
131        if not hasattr(self.__local, '__release_local__'):
132            return self.__local()
133        try:
134            return getattr(self.__local, self.__name__)
135        except AttributeError:
136            raise RuntimeError('no object bound to %s' % self.__name__)
137
138    @property
139    def __dict__(self):
140        try:
141            return self._get_current_object().__dict__
142        except RuntimeError:
143            return AttributeError('__dict__')
144
145    def __repr__(self):
146        try:
147            obj = self._get_current_object()
148        except RuntimeError:
149            return '<%s unbound>' % self.__class__.__name__
150        return repr(obj)
151
152    def __nonzero__(self):
153        try:
154            return bool(self._get_current_object())
155        except RuntimeError:
156            return False
157
158    def __unicode__(self):
159        try:
160            return unicode(self._get_current_object())
161        except RuntimeError:
162            return repr(self)
163
164    def __dir__(self):
165        try:
166            return dir(self._get_current_object())
167        except RuntimeError:
168            return []
169
170    def __getattr__(self, name):
171        if name == '__members__':
172            return dir(self._get_current_object())
173        return getattr(self._get_current_object(), name)
174
175    def __setitem__(self, key, value):
176        self._get_current_object()[key] = value
177
178    def __delitem__(self, key):
179        del self._get_current_object()[key]
180
181    def __setslice__(self, i, j, seq):
182        self._get_current_object()[i:j] = seq
183
184    def __delslice__(self, i, j):
185        del self._get_current_object()[i:j]
186
187    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
188    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
189    __str__ = lambda x: str(x._get_current_object())
190    __lt__ = lambda x, o: x._get_current_object() < o
191    __le__ = lambda x, o: x._get_current_object() <= o
192    __eq__ = lambda x, o: x._get_current_object() == o
193    __ne__ = lambda x, o: x._get_current_object() != o
194    __gt__ = lambda x, o: x._get_current_object() > o
195    __ge__ = lambda x, o: x._get_current_object() >= o
196    __cmp__ = lambda x, o: cmp(x._get_current_object(), o)
197    __hash__ = lambda x: hash(x._get_current_object())
198    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
199    __len__ = lambda x: len(x._get_current_object())
200    __getitem__ = lambda x, i: x._get_current_object()[i]
201    __iter__ = lambda x: iter(x._get_current_object())
202    __contains__ = lambda x, i: i in x._get_current_object()
203    __getslice__ = lambda x, i, j: x._get_current_object()[i:j]
204    __add__ = lambda x, o: x._get_current_object() + o
205    __sub__ = lambda x, o: x._get_current_object() - o
206    __mul__ = lambda x, o: x._get_current_object() * o
207    __floordiv__ = lambda x, o: x._get_current_object() // o
208    __mod__ = lambda x, o: x._get_current_object() % o
209    __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
210    __pow__ = lambda x, o: x._get_current_object() ** o
211    __lshift__ = lambda x, o: x._get_current_object() << o
212    __rshift__ = lambda x, o: x._get_current_object() >> o
213    __and__ = lambda x, o: x._get_current_object() & o
214    __xor__ = lambda x, o: x._get_current_object() ^ o
215    __or__ = lambda x, o: x._get_current_object() | o
216    __div__ = lambda x, o: x._get_current_object().__div__(o)
217    __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
218    __neg__ = lambda x: -(x._get_current_object())
219    __pos__ = lambda x: +(x._get_current_object())
220    __abs__ = lambda x: abs(x._get_current_object())
221    __invert__ = lambda x: ~(x._get_current_object())
222    __complex__ = lambda x: complex(x._get_current_object())
223    __int__ = lambda x: int(x._get_current_object())
224    __long__ = lambda x: long(x._get_current_object())
225    __float__ = lambda x: float(x._get_current_object())
226    __oct__ = lambda x: oct(x._get_current_object())
227    __hex__ = lambda x: hex(x._get_current_object())
228    __index__ = lambda x: x._get_current_object().__index__()
229    __coerce__ = lambda x, o: x.__coerce__(x, o)
230    __enter__ = lambda x: x.__enter__()
231    __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)
232