summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/event.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-11-07 12:49:48 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2010-11-07 12:49:48 -0500
commit13fedc23ecca81d0881a994a45efae3a77b74fcb (patch)
treebb5cb4e6b407e3eacd2284e1e42f24c53c8af401 /lib/sqlalchemy/event.py
parent1f2423d23cc3e8bf8db60b56436752fbd3d83f9d (diff)
downloadsqlalchemy-13fedc23ecca81d0881a994a45efae3a77b74fcb.tar.gz
- propagate flag on event.listen() results in the listener being placed
in a separate collection. this collection also propagates during update() - ClassManager now handles bases, subclasses collections. - ClassManager looks at __bases__ instead of __mro__ for superclasses. It's assumed ClassManagers are in an unbroken chain upwards through __mro__. - trying to get a clear() that makes sense on cls.dispatch - implemented propagate for attribute events, plus permutation-based test - implemented propagate for mapper / instance events with rudimentary test - some pool events tests are failing for some reason
Diffstat (limited to 'lib/sqlalchemy/event.py')
-rw-r--r--lib/sqlalchemy/event.py49
1 files changed, 35 insertions, 14 deletions
diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py
index 955c33797..75512f7d2 100644
--- a/lib/sqlalchemy/event.py
+++ b/lib/sqlalchemy/event.py
@@ -18,8 +18,6 @@ def listen(fn, identifier, target, *args, **kw):
for evt_cls in _registrars[identifier]:
tgt = evt_cls.accept_with(target)
if tgt is not None:
- if kw.pop('propagate', False):
- fn._sa_event_propagate = True
tgt.dispatch.listen(fn, identifier, tgt, *args, **kw)
return
raise exc.InvalidRequestError("No such event %s for target %s" %
@@ -37,7 +35,7 @@ def remove(fn, identifier, target):
for tgt in evt_cls.accept_with(target):
tgt.dispatch.remove(fn, identifier, tgt, *args, **kw)
return
-
+
_registrars = util.defaultdict(list)
class _UnpickleDispatch(object):
@@ -60,7 +58,7 @@ class _Dispatch(object):
def __reduce__(self):
return _UnpickleDispatch(), (self.parent_cls, )
-
+
@property
def descriptors(self):
return (getattr(self, k) for k in dir(self) if k.startswith("on_"))
@@ -71,7 +69,8 @@ class _Dispatch(object):
for ls in other.descriptors:
getattr(self, ls.name).update(ls, only_propagate=only_propagate)
-
+
+
class _EventMeta(type):
"""Intercept new Event subclasses and create
associated _Dispatch classes."""
@@ -88,7 +87,8 @@ def _create_dispatcher_class(cls, classname, bases, dict_):
cls.dispatch = dispatch_cls = type("%sDispatch" % classname,
(dispatch_base, ), {})
dispatch_cls.listen = cls.listen
-
+ dispatch_cls.clear = cls.clear
+
for k in dict_:
if k.startswith('on_'):
setattr(dispatch_cls, k, _DispatchDescriptor(dict_[k]))
@@ -121,13 +121,19 @@ class Events(object):
return None
@classmethod
- def listen(cls, fn, identifier, target):
- getattr(target.dispatch, identifier).append(fn, target)
+ def listen(cls, fn, identifier, target, propagate=False):
+ getattr(target.dispatch, identifier).append(fn, target, propagate)
@classmethod
def remove(cls, fn, identifier, target):
getattr(target.dispatch, identifier).remove(fn, target)
-
+
+ @classmethod
+ def clear(cls):
+ for attr in dir(cls.dispatch):
+ if attr.startswith("on_"):
+ getattr(cls.dispatch, attr).clear()
+
class _DispatchDescriptor(object):
"""Class-level attributes on _Dispatch classes."""
@@ -136,7 +142,7 @@ class _DispatchDescriptor(object):
self.__doc__ = fn.__doc__
self._clslevel = util.defaultdict(list)
- def append(self, obj, target):
+ def append(self, obj, target, propagate):
assert isinstance(target, type), \
"Class-level Event targets must be classes."
@@ -146,7 +152,13 @@ class _DispatchDescriptor(object):
def remove(self, obj, target):
for cls in [target] + target.__subclasses__():
self._clslevel[cls].remove(obj)
+
+ def clear(self):
+ """Clear all class level listeners"""
+ for dispatcher in self._clslevel.values():
+ dispatcher[:] = []
+
def __get__(self, obj, cls):
if obj is None:
return self
@@ -166,7 +178,8 @@ class _ListenerCollection(object):
self.parent_listeners = parent._clslevel[target_cls]
self.name = parent.__name__
self.listeners = []
-
+ self.propagate = set()
+
def exec_once(self, *args, **kw):
"""Execute this event, but only if it has not been
executed already for this collection."""
@@ -200,22 +213,30 @@ class _ListenerCollection(object):
def update(self, other, only_propagate=True):
"""Populate from the listeners in another :class:`_Dispatch`
object."""
-
+
existing_listeners = self.listeners
existing_listener_set = set(existing_listeners)
+ self.propagate.update(other.propagate)
existing_listeners.extend([l for l
in other.listeners
if l not in existing_listener_set
- and not only_propagate or getattr(l, '_sa_event_propagate', False)
+ and not only_propagate or l in self.propagate
])
- def append(self, obj, target):
+ def append(self, obj, target, propagate):
if obj not in self.listeners:
self.listeners.append(obj)
+ if propagate:
+ self.propagate.add(obj)
def remove(self, obj, target):
if obj in self.listeners:
self.listeners.remove(obj)
+ self.propagate.discard(obj)
+
+ def clear(self):
+ self.listeners[:] = []
+ self.propagate.clear()
class dispatcher(object):
"""Descriptor used by target classes to