Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/werkzeug/local.py
diff options
context:
space:
mode:
Diffstat (limited to 'werkzeug/local.py')
-rw-r--r--werkzeug/local.py405
1 files changed, 405 insertions, 0 deletions
diff --git a/werkzeug/local.py b/werkzeug/local.py
new file mode 100644
index 0000000..595c87d
--- /dev/null
+++ b/werkzeug/local.py
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.local
+ ~~~~~~~~~~~~~~
+
+ This module implements context-local objects.
+
+ :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+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 thread import get_ident as get_current_thread, allocate_lock
+except ImportError: # pragma: no cover
+ from dummy_thread import get_ident as get_current_thread, allocate_lock
+
+from werkzeug.wsgi import ClosingIterator
+from werkzeug._internal import _patch_wrapper
+
+
+# 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:
+ get_ident = lambda: (get_current_thread(), get_current_greenlet())
+
+
+def release_local(local):
+ """Releases the contents of the local for the current context.
+ This makes it possible to use locals without a manager.
+
+ Example::
+
+ >>> loc = Local()
+ >>> loc.foo = 42
+ >>> release_local(loc)
+ >>> hasattr(loc, 'foo')
+ False
+
+ With this function one can release :class:`Local` objects as well
+ as :class:`StackLocal` objects. However it is not possible to
+ release data held by proxies that way, one always has to retain
+ a reference to the underlying local object in order to be able
+ to release it.
+
+ .. versionadded:: 0.6.1
+ """
+ local.__release_local__()
+
+
+class Local(object):
+ __slots__ = ('__storage__', '__lock__')
+
+ def __init__(self):
+ object.__setattr__(self, '__storage__', {})
+ object.__setattr__(self, '__lock__', allocate_lock())
+
+ def __iter__(self):
+ return self.__storage__.iteritems()
+
+ def __call__(self, proxy):
+ """Create 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()
+
+
+class LocalStack(object):
+ """This class works similar to a :class:`Local` but keeps a stack
+ of objects instead. This is best explained with an example::
+
+ >>> ls = LocalStack()
+ >>> ls.push(42)
+ >>> ls.top
+ 42
+ >>> ls.push(23)
+ >>> ls.top
+ 23
+ >>> ls.pop()
+ 23
+ >>> ls.top
+ 42
+
+ They can be force released by using a :class:`LocalManager` or with
+ the :func:`release_local` function but the correct way is to pop the
+ item from the stack after using. When the stack is empty it will
+ no longer be bound to the current context (and as such released).
+
+ By calling the stack without arguments it returns a proxy that resolves to
+ the topmost item on the stack.
+
+ .. versionadded:: 0.6.1
+ """
+
+ def __init__(self):
+ self._local = Local()
+ self._lock = allocate_lock()
+
+ def __release_local__(self):
+ self._local.__release_local__()
+
+ def __call__(self):
+ def _lookup():
+ rv = self.top
+ if rv is None:
+ raise RuntimeError('object unbound')
+ return rv
+ return LocalProxy(_lookup)
+
+ def push(self, obj):
+ """Pushes a new item to the stack"""
+ self._lock.acquire()
+ try:
+ rv = getattr(self._local, 'stack', None)
+ if rv is None:
+ self._local.stack = rv = []
+ rv.append(obj)
+ return rv
+ finally:
+ self._lock.release()
+
+ def pop(self):
+ """Removes the topmost item from the stack, will return the
+ old value or `None` if the stack was already empty.
+ """
+ self._lock.acquire()
+ try:
+ stack = getattr(self._local, 'stack', None)
+ if stack is None:
+ return None
+ elif len(stack) == 1:
+ release_local(self._local)
+ return stack[-1]
+ else:
+ return stack.pop()
+ finally:
+ self._lock.release()
+
+ @property
+ def top(self):
+ """The topmost item on the stack. If the stack is empty,
+ `None` is returned.
+ """
+ try:
+ return self._local.stack[-1]
+ except (AttributeError, IndexError):
+ return None
+
+
+class LocalManager(object):
+ """Local objects cannot manage themselves. For that you need a local
+ manager. You can pass a local manager multiple locals or add them later
+ by appending them to `manager.locals`. Everytime the manager cleans up
+ it, will clean up all the data left in the locals for this context.
+
+ .. versionchanged:: 0.6.1
+ Instead of a manager the :func:`release_local` function can be used
+ as well.
+ """
+
+ def __init__(self, locals=None):
+ if locals is None:
+ self.locals = []
+ elif isinstance(locals, Local):
+ self.locals = [locals]
+ else:
+ self.locals = list(locals)
+
+ def get_ident(self):
+ """Return the context identifier the local objects use internally for
+ this context. You cannot override this method to change the behavior
+ but use it to link other context local objects (such as SQLAlchemy's
+ scoped sessions) to the Werkzeug locals.
+ """
+ return get_ident()
+
+ def cleanup(self):
+ """Manually clean up the data in the locals for this context. Call
+ this at the end of the request or use `make_middleware()`.
+ """
+ ident = self.get_ident()
+ for local in self.locals:
+ release_local(local)
+
+ def make_middleware(self, app):
+ """Wrap a WSGI application so that cleaning up happens after
+ request end.
+ """
+ def application(environ, start_response):
+ return ClosingIterator(app(environ, start_response), self.cleanup)
+ return application
+
+ def middleware(self, func):
+ """Like `make_middleware` but for decorating functions.
+
+ Example usage::
+
+ @manager.middleware
+ def application(environ, start_response):
+ ...
+
+ The difference to `make_middleware` is that the function passed
+ will have all the arguments copied from the inner application
+ (name, docstring, module).
+ """
+ return _patch_wrapper(func, self.make_middleware(func))
+
+ def __repr__(self):
+ return '<%s storages: %d>' % (
+ self.__class__.__name__,
+ len(self.locals)
+ )
+
+
+class LocalProxy(object):
+ """Acts as a proxy for a werkzeug local. 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 werkzeug import Local
+ l = Local()
+
+ # these are proxies
+ request = l('request')
+ user = l('user')
+
+
+ from werkzeug import LocalStack
+ _response_local = LocalStack()
+
+ # this is a proxy
+ response = _response_local()
+
+ Whenever something is bound to l.user / 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` or :class:`LocalStack` objects,
+ call the object as shown above. If you want to have a proxy to an
+ object looked up by a function, you can (as of Werkzeug 0.6.1) pass
+ a function to the :class:`LocalProxy` constructor::
+
+ session = LocalProxy(lambda: get_current_request().session)
+
+ .. versionchanged:: 0.6.1
+ The class can be instanciated with a callable as well now.
+ """
+ __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 __nonzero__(self):
+ try:
+ return bool(self._get_current_object())
+ except RuntimeError:
+ return False
+
+ def __unicode__(self):
+ try:
+ return unicode(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]
+
+ __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
+ __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
+ __str__ = lambda x: str(x._get_current_object())
+ __lt__ = lambda x, o: x._get_current_object() < o
+ __le__ = lambda x, o: x._get_current_object() <= o
+ __eq__ = lambda x, o: x._get_current_object() == o
+ __ne__ = lambda x, o: x._get_current_object() != o
+ __gt__ = lambda x, o: x._get_current_object() > o
+ __ge__ = lambda x, o: x._get_current_object() >= o
+ __cmp__ = lambda x, o: cmp(x._get_current_object(), o)
+ __hash__ = lambda x: hash(x._get_current_object())
+ __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
+ __len__ = lambda x: len(x._get_current_object())
+ __getitem__ = lambda x, i: x._get_current_object()[i]
+ __iter__ = lambda x: iter(x._get_current_object())
+ __contains__ = lambda x, i: i in x._get_current_object()
+ __getslice__ = lambda x, i, j: x._get_current_object()[i:j]
+ __add__ = lambda x, o: x._get_current_object() + o
+ __sub__ = lambda x, o: x._get_current_object() - o
+ __mul__ = lambda x, o: x._get_current_object() * o
+ __floordiv__ = lambda x, o: x._get_current_object() // o
+ __mod__ = lambda x, o: x._get_current_object() % o
+ __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
+ __pow__ = lambda x, o: x._get_current_object() ** o
+ __lshift__ = lambda x, o: x._get_current_object() << o
+ __rshift__ = lambda x, o: x._get_current_object() >> o
+ __and__ = lambda x, o: x._get_current_object() & o
+ __xor__ = lambda x, o: x._get_current_object() ^ o
+ __or__ = lambda x, o: x._get_current_object() | o
+ __div__ = lambda x, o: x._get_current_object().__div__(o)
+ __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
+ __neg__ = lambda x: -(x._get_current_object())
+ __pos__ = lambda x: +(x._get_current_object())
+ __abs__ = lambda x: abs(x._get_current_object())
+ __invert__ = lambda x: ~(x._get_current_object())
+ __complex__ = lambda x: complex(x._get_current_object())
+ __int__ = lambda x: int(x._get_current_object())
+ __long__ = lambda x: long(x._get_current_object())
+ __float__ = lambda x: float(x._get_current_object())
+ __oct__ = lambda x: oct(x._get_current_object())
+ __hex__ = lambda x: hex(x._get_current_object())
+ __index__ = lambda x: x._get_current_object().__index__()
+ __coerce__ = lambda x, o: x.__coerce__(x, o)
+ __enter__ = lambda x: x.__enter__()
+ __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)