|
@@ -280,6 +280,96 @@ class Local(object):
|
|
|
raise AttributeError(name)
|
|
|
|
|
|
|
|
|
+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.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self._local = Local()
|
|
|
+
|
|
|
+ def __release_local__(self):
|
|
|
+ self._local.__release_local__()
|
|
|
+
|
|
|
+ def _get__ident_func__(self):
|
|
|
+ return self._local.__ident_func__
|
|
|
+
|
|
|
+ def _set__ident_func__(self, value):
|
|
|
+ object.__setattr__(self._local, '__ident_func__', value)
|
|
|
+ __ident_func__ = property(_get__ident_func__, _set__ident_func__)
|
|
|
+ del _get__ident_func__, _set__ident_func__
|
|
|
+
|
|
|
+ def __call__(self):
|
|
|
+ def _lookup():
|
|
|
+ rv = self.top
|
|
|
+ if rv is None:
|
|
|
+ raise RuntimeError('object unbound')
|
|
|
+ return rv
|
|
|
+ return Proxy(_lookup)
|
|
|
+
|
|
|
+ def push(self, obj):
|
|
|
+ """Pushes a new item to the stack"""
|
|
|
+ rv = getattr(self._local, 'stack', None)
|
|
|
+ if rv is None:
|
|
|
+ self._local.stack = rv = []
|
|
|
+ rv.append(obj)
|
|
|
+ return rv
|
|
|
+
|
|
|
+ def pop(self):
|
|
|
+ """Removes the topmost item from the stack, will return the
|
|
|
+ old value or `None` if the stack was already empty.
|
|
|
+ """
|
|
|
+ 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()
|
|
|
+
|
|
|
+ @property
|
|
|
+ def stack(self):
|
|
|
+ """get_current_worker_task uses this to find
|
|
|
+ the original task that was executed by the worker."""
|
|
|
+ stack = getattr(self._local, 'stack', None)
|
|
|
+ if stack is not None:
|
|
|
+ return stack
|
|
|
+ return []
|
|
|
+
|
|
|
+ @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
|