diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-11-07 12:49:48 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-11-07 12:49:48 -0500 |
commit | 13fedc23ecca81d0881a994a45efae3a77b74fcb (patch) | |
tree | bb5cb4e6b407e3eacd2284e1e42f24c53c8af401 /lib/sqlalchemy/event.py | |
parent | 1f2423d23cc3e8bf8db60b56436752fbd3d83f9d (diff) | |
download | sqlalchemy-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.py | 49 |
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 |