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