From: Mike Bayer Date: Mon, 19 Feb 2018 21:59:42 +0000 (-0500) Subject: Implement remove() for _empty_collection X-Git-Tag: rel_1_2_4~4^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1393eac44c49299e733a5181f387ba140aaa3e44;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Implement remove() for _empty_collection 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 --- diff --git a/doc/build/changelog/unreleased_12/4190.rst b/doc/build/changelog/unreleased_12/4190.rst new file mode 100644 index 0000000000..0969ea3a20 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4190.rst @@ -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. diff --git a/lib/sqlalchemy/event/attr.py b/lib/sqlalchemy/event/attr.py index efa8fab429..c33ec82ff6 100644 --- a/lib/sqlalchemy/event/attr.py +++ b/lib/sqlalchemy/event/attr.py @@ -54,6 +54,9 @@ class _empty_collection(object): def extend(self, other): pass + def remove(self, element): + pass + def __iter__(self): return iter([]) diff --git a/test/base/test_events.py b/test/base/test_events.py index b502a0348b..288c6091ff 100644 --- a/test/base/test_events.py +++ b/test/base/test_events.py @@ -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): diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index e0727f770c..2d602fa125 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -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