]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Implement remove() for _empty_collection
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 19 Feb 2018 21:59:42 +0000 (16:59 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 19 Feb 2018 22:01:43 +0000 (17:01 -0500)
Fixed regression caused in 1.2.3 due to fix from :ticket:`4181` where
the changes to the event system involving :class:`.Engine` and
:class:`.OptionEngine` did not accommodate for event removals, which
would raise an ``AttributeError`` when invoked at the class
level.

Change-Id: I1c9083829d74dd710716d28b0eaca4fa15e86313
Fixes: #4190
doc/build/changelog/unreleased_12/4190.rst [new file with mode: 0644]
lib/sqlalchemy/event/attr.py
test/base/test_events.py
test/engine/test_execute.py

diff --git a/doc/build/changelog/unreleased_12/4190.rst b/doc/build/changelog/unreleased_12/4190.rst
new file mode 100644 (file)
index 0000000..0969ea3
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, engine
+    :tickets: 4190
+
+    Fixed regression caused in 1.2.3 due to fix from :ticket:`4181` where
+    the changes to the event system involving :class:`.Engine` and
+    :class:`.OptionEngine` did not accommodate for event removals, which
+    would raise an ``AttributeError`` when invoked at the class
+    level.
index efa8fab4297ed3e6200645f931341b5d64953725..c33ec82ff6ea9af97d0344cdca670760c880a84d 100644 (file)
@@ -54,6 +54,9 @@ class _empty_collection(object):
     def extend(self, other):
         pass
 
+    def remove(self, element):
+        pass
+
     def __iter__(self):
         return iter([])
 
index b502a0348bbc767e457b2fc7b6e23846c4ed2063..288c6091ff9f3a76cd4b5d61b0d59e01648fde2f 100644 (file)
@@ -1033,6 +1033,64 @@ class JoinTest(fixtures.TestBase):
         )
 
 
+class DisableClsPropagateTest(fixtures.TestBase):
+
+    def setUp(self):
+        class TargetEvents(event.Events):
+            def event_one(self, target, arg):
+                pass
+
+        class BaseTarget(object):
+            dispatch = event.dispatcher(TargetEvents)
+
+        class SubTarget(BaseTarget):
+            _sa_propagate_class_events = False
+
+            def __init__(self, parent):
+                self.dispatch = self.dispatch._join(parent.dispatch)
+
+        self.BaseTarget = BaseTarget
+        self.SubTarget = SubTarget
+
+    def tearDown(self):
+        for cls in (self.SubTarget, self.BaseTarget):
+            if 'dispatch' in cls.__dict__:
+                event.base._remove_dispatcher(cls.__dict__['dispatch'].events)
+
+    def test_listen_invoke_clslevel(self):
+        canary = Mock()
+
+        event.listen(self.BaseTarget, "event_one", canary)
+
+        s1 = self.SubTarget(self.BaseTarget())
+        s1.dispatch.event_one()
+
+        eq_(canary.mock_calls, [call.event_one()])
+
+    def test_insert_invoke_clslevel(self):
+        canary = Mock()
+
+        event.listen(self.BaseTarget, "event_one", canary, insert=True)
+
+        s1 = self.SubTarget(self.BaseTarget())
+        s1.dispatch.event_one()
+
+        eq_(canary.mock_calls, [call.event_one()])
+
+    def test_remove_invoke_clslevel(self):
+        canary = Mock()
+
+        event.listen(self.BaseTarget, "event_one", canary)
+
+        s1 = self.SubTarget(self.BaseTarget())
+
+        event.remove(self.BaseTarget, "event_one", canary)
+
+        s1.dispatch.event_one()
+
+        eq_(canary.mock_calls, [])
+
+
 class RemovalTest(fixtures.TestBase):
     def _fixture(self):
         class TargetEvents(event.Events):
index e0727f770c16f5fd996ff94270d67b8d06a2ffb2..2d602fa1258d04c03af2e8a6aebf2f272c717296 100644 (file)
@@ -1399,6 +1399,15 @@ class EngineEventsTest(fixtures.TestBase):
 
         eq_(canary, ["l1", "l2", "l3", "l4", "l1", "l2", "l3"])
 
+        canary[:] = []
+
+        event.remove(Engine, "before_execute", l1)
+        event.remove(eng1, "before_execute", l4)
+        event.remove(eng, "before_execute", l3)
+
+        eng1.execute(select([1])).close()
+        eq_(canary, ["l2"])
+
     @testing.requires.ad_hoc_engines
     def test_cant_listen_to_option_engine(self):
         from sqlalchemy.engine import base