summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/sqlalchemy/event.py12
-rw-r--r--lib/sqlalchemy/orm/deprecated_interfaces.py8
-rw-r--r--test/base/test_events.py47
3 files changed, 59 insertions, 8 deletions
diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py
index 5f46a1153..955c33797 100644
--- a/lib/sqlalchemy/event.py
+++ b/lib/sqlalchemy/event.py
@@ -18,6 +18,8 @@ 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" %
@@ -63,12 +65,12 @@ class _Dispatch(object):
def descriptors(self):
return (getattr(self, k) for k in dir(self) if k.startswith("on_"))
- def update(self, other):
+ def update(self, other, only_propagate=True):
"""Populate from the listeners in another :class:`_Dispatch`
object."""
for ls in other.descriptors:
- getattr(self, ls.name).update(ls)
+ getattr(self, ls.name).update(ls, only_propagate=only_propagate)
class _EventMeta(type):
"""Intercept new Event subclasses and create
@@ -195,7 +197,7 @@ class _ListenerCollection(object):
def __nonzero__(self):
return bool(self.listeners or self.parent_listeners)
- def update(self, other):
+ def update(self, other, only_propagate=True):
"""Populate from the listeners in another :class:`_Dispatch`
object."""
@@ -203,7 +205,9 @@ class _ListenerCollection(object):
existing_listener_set = set(existing_listeners)
existing_listeners.extend([l for l
in other.listeners
- if l not in existing_listener_set])
+ if l not in existing_listener_set
+ and not only_propagate or getattr(l, '_sa_event_propagate', False)
+ ])
def append(self, obj, target):
if obj not in self.listeners:
diff --git a/lib/sqlalchemy/orm/deprecated_interfaces.py b/lib/sqlalchemy/orm/deprecated_interfaces.py
index 3817dc2ee..76e023701 100644
--- a/lib/sqlalchemy/orm/deprecated_interfaces.py
+++ b/lib/sqlalchemy/orm/deprecated_interfaces.py
@@ -71,7 +71,7 @@ class MapperExtension(object):
ls_meth(self, instance)
return reconstruct
event.listen(go(ls_meth), 'on_load',
- self.class_manager, raw=False)
+ self.class_manager, raw=False, propagate=True)
elif meth == 'init_instance':
def go(ls_meth):
def init_instance(instance, args, kwargs):
@@ -80,7 +80,7 @@ class MapperExtension(object):
instance, args, kwargs)
return init_instance
event.listen(go(ls_meth), 'on_init',
- self.class_manager, raw=False)
+ self.class_manager, raw=False, propagate=True)
elif meth == 'init_failed':
def go(ls_meth):
def init_failed(instance, args, kwargs):
@@ -90,10 +90,10 @@ class MapperExtension(object):
return init_failed
event.listen(go(ls_meth), 'on_init_failure',
- self.class_manager, raw=False)
+ self.class_manager, raw=False, propagate=True)
else:
event.listen(ls_meth, "on_%s" % meth, self,
- raw=False, retval=True)
+ raw=False, retval=True, propagate=True)
def instrument_class(self, mapper, class_):
diff --git a/test/base/test_events.py b/test/base/test_events.py
index 9099619e5..995569c60 100644
--- a/test/base/test_events.py
+++ b/test/base/test_events.py
@@ -227,5 +227,52 @@ class TestListenOverride(TestBase):
]
)
+class TestPropagate(TestBase):
+ def setUp(self):
+ global Target
+
+ class TargetEvents(event.Events):
+ def on_event_one(self, arg):
+ pass
+
+ def on_event_two(self, arg):
+ pass
+
+ class Target(object):
+ dispatch = event.dispatcher(TargetEvents)
+
+
+ def test_propagate(self):
+ result = []
+ def listen_one(target, arg):
+ result.append((target, arg))
+
+ def listen_two(target, arg):
+ result.append((target, arg))
+
+ t1 = Target()
+ event.listen(listen_one, "on_event_one", t1, propagate=True)
+ event.listen(listen_two, "on_event_two", t1)
+
+ t2 = Target()
+
+ t2.dispatch.update(t1.dispatch)
+
+ t2.dispatch.on_event_one(t2, 1)
+ t2.dispatch.on_event_two(t2, 2)
+ eq_(result, [(t2, 1)])
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file