mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-27 11:01:44 -04:00
d1d15c9d60
Fixed an issue whereby if the dialect "initialize" process which occurs on
first connect would encounter an unexpected exception, the initialize
process would fail to complete and then no longer attempt on subsequent
connection attempts, leaving the dialect in an un-initialized, or partially
initialized state, within the scope of parameters that need to be
established based on inspection of a live connection. The "invoke once"
logic in the event system has been reworked to accommodate for this
occurrence using new, private API features that establish an "exec once"
hook that will continue to allow the initializer to fire off on subsequent
connections, until it completes without raising an exception. This does not
impact the behavior of the existing ``once=True`` flag within the event
system.
Fixes: #4807
Change-Id: Iec32999b61b6af4b38b6719e0c2651454619078c
(cherry picked from commit 2051fa2ce9)
1487 lines
39 KiB
Python
1487 lines
39 KiB
Python
"""Test event registration and listening."""
|
|
|
|
from sqlalchemy import event
|
|
from sqlalchemy import exc
|
|
from sqlalchemy import testing
|
|
from sqlalchemy.testing import assert_raises
|
|
from sqlalchemy.testing import assert_raises_message
|
|
from sqlalchemy.testing import eq_
|
|
from sqlalchemy.testing import fixtures
|
|
from sqlalchemy.testing import is_
|
|
from sqlalchemy.testing import is_not_
|
|
from sqlalchemy.testing.mock import call
|
|
from sqlalchemy.testing.mock import Mock
|
|
from sqlalchemy.testing.util import gc_collect
|
|
|
|
|
|
class EventsTest(fixtures.TestBase):
|
|
"""Test class- and instance-level event registration."""
|
|
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
def event_two(self, x):
|
|
pass
|
|
|
|
def event_three(self, x):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
self.Target = Target
|
|
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
|
|
|
|
def test_register_class(self):
|
|
def listen(x, y):
|
|
pass
|
|
|
|
event.listen(self.Target, "event_one", listen)
|
|
|
|
eq_(len(self.Target().dispatch.event_one), 1)
|
|
eq_(len(self.Target().dispatch.event_two), 0)
|
|
|
|
def test_register_instance(self):
|
|
def listen(x, y):
|
|
pass
|
|
|
|
t1 = self.Target()
|
|
event.listen(t1, "event_one", listen)
|
|
|
|
eq_(len(self.Target().dispatch.event_one), 0)
|
|
eq_(len(t1.dispatch.event_one), 1)
|
|
eq_(len(self.Target().dispatch.event_two), 0)
|
|
eq_(len(t1.dispatch.event_two), 0)
|
|
|
|
def test_bool_clslevel(self):
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
event.listen(self.Target, "event_one", listen_one)
|
|
t = self.Target()
|
|
assert t.dispatch.event_one
|
|
|
|
def test_register_class_instance(self):
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
event.listen(self.Target, "event_one", listen_one)
|
|
|
|
t1 = self.Target()
|
|
event.listen(t1, "event_one", listen_two)
|
|
|
|
eq_(len(self.Target().dispatch.event_one), 1)
|
|
eq_(len(t1.dispatch.event_one), 2)
|
|
eq_(len(self.Target().dispatch.event_two), 0)
|
|
eq_(len(t1.dispatch.event_two), 0)
|
|
|
|
def listen_three(x, y):
|
|
pass
|
|
|
|
event.listen(self.Target, "event_one", listen_three)
|
|
eq_(len(self.Target().dispatch.event_one), 2)
|
|
eq_(len(t1.dispatch.event_one), 3)
|
|
|
|
def test_append_vs_insert_cls(self):
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
def listen_three(x, y):
|
|
pass
|
|
|
|
event.listen(self.Target, "event_one", listen_one)
|
|
event.listen(self.Target, "event_one", listen_two)
|
|
event.listen(self.Target, "event_one", listen_three, insert=True)
|
|
|
|
eq_(
|
|
list(self.Target().dispatch.event_one),
|
|
[listen_three, listen_one, listen_two],
|
|
)
|
|
|
|
def test_append_vs_insert_instance(self):
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
def listen_three(x, y):
|
|
pass
|
|
|
|
target = self.Target()
|
|
event.listen(target, "event_one", listen_one)
|
|
event.listen(target, "event_one", listen_two)
|
|
event.listen(target, "event_one", listen_three, insert=True)
|
|
|
|
eq_(
|
|
list(target.dispatch.event_one),
|
|
[listen_three, listen_one, listen_two],
|
|
)
|
|
|
|
def test_decorator(self):
|
|
@event.listens_for(self.Target, "event_one")
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
@event.listens_for(self.Target, "event_two")
|
|
@event.listens_for(self.Target, "event_three")
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
eq_(list(self.Target().dispatch.event_one), [listen_one])
|
|
|
|
eq_(list(self.Target().dispatch.event_two), [listen_two])
|
|
|
|
eq_(list(self.Target().dispatch.event_three), [listen_two])
|
|
|
|
def test_no_instance_level_collections(self):
|
|
@event.listens_for(self.Target, "event_one")
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
t1 = self.Target()
|
|
t2 = self.Target()
|
|
t1.dispatch.event_one(5, 6)
|
|
t2.dispatch.event_one(5, 6)
|
|
is_(
|
|
self.Target.dispatch._empty_listener_reg[self.Target]["event_one"],
|
|
t1.dispatch.event_one,
|
|
)
|
|
|
|
@event.listens_for(t1, "event_one")
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
is_not_(
|
|
self.Target.dispatch._empty_listener_reg[self.Target]["event_one"],
|
|
t1.dispatch.event_one,
|
|
)
|
|
is_(
|
|
self.Target.dispatch._empty_listener_reg[self.Target]["event_one"],
|
|
t2.dispatch.event_one,
|
|
)
|
|
|
|
def test_exec_once(self):
|
|
m1 = Mock()
|
|
|
|
event.listen(self.Target, "event_one", m1)
|
|
|
|
t1 = self.Target()
|
|
t2 = self.Target()
|
|
|
|
t1.dispatch.event_one.for_modify(t1.dispatch).exec_once(5, 6)
|
|
|
|
t1.dispatch.event_one.for_modify(t1.dispatch).exec_once(7, 8)
|
|
|
|
t2.dispatch.event_one.for_modify(t2.dispatch).exec_once(9, 10)
|
|
|
|
eq_(m1.mock_calls, [call(5, 6), call(9, 10)])
|
|
|
|
def test_exec_once_exception(self):
|
|
m1 = Mock()
|
|
m1.side_effect = ValueError
|
|
|
|
event.listen(self.Target, "event_one", m1)
|
|
|
|
t1 = self.Target()
|
|
|
|
assert_raises(
|
|
ValueError,
|
|
t1.dispatch.event_one.for_modify(t1.dispatch).exec_once,
|
|
5,
|
|
6,
|
|
)
|
|
|
|
t1.dispatch.event_one.for_modify(t1.dispatch).exec_once(7, 8)
|
|
|
|
eq_(m1.mock_calls, [call(5, 6)])
|
|
|
|
def test_exec_once_unless_exception(self):
|
|
m1 = Mock()
|
|
m1.side_effect = ValueError
|
|
|
|
event.listen(self.Target, "event_one", m1)
|
|
|
|
t1 = self.Target()
|
|
|
|
assert_raises(
|
|
ValueError,
|
|
t1.dispatch.event_one.for_modify(
|
|
t1.dispatch
|
|
).exec_once_unless_exception,
|
|
5,
|
|
6,
|
|
)
|
|
|
|
assert_raises(
|
|
ValueError,
|
|
t1.dispatch.event_one.for_modify(
|
|
t1.dispatch
|
|
).exec_once_unless_exception,
|
|
7,
|
|
8,
|
|
)
|
|
|
|
m1.side_effect = None
|
|
t1.dispatch.event_one.for_modify(
|
|
t1.dispatch
|
|
).exec_once_unless_exception(9, 10)
|
|
|
|
t1.dispatch.event_one.for_modify(
|
|
t1.dispatch
|
|
).exec_once_unless_exception(11, 12)
|
|
|
|
eq_(m1.mock_calls, [call(5, 6), call(7, 8), call(9, 10)])
|
|
|
|
def test_immutable_methods(self):
|
|
t1 = self.Target()
|
|
for meth in [
|
|
t1.dispatch.event_one.exec_once,
|
|
t1.dispatch.event_one.insert,
|
|
t1.dispatch.event_one.append,
|
|
t1.dispatch.event_one.remove,
|
|
t1.dispatch.event_one.clear,
|
|
]:
|
|
assert_raises_message(
|
|
NotImplementedError, r"need to call for_modify\(\)", meth
|
|
)
|
|
|
|
|
|
class NamedCallTest(fixtures.TestBase):
|
|
def _fixture(self):
|
|
class TargetEventsOne(event.Events):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
def event_two(self, x, y, **kw):
|
|
pass
|
|
|
|
def event_five(self, x, y, z, q):
|
|
pass
|
|
|
|
class TargetOne(object):
|
|
dispatch = event.dispatcher(TargetEventsOne)
|
|
|
|
return TargetOne
|
|
|
|
def _wrapped_fixture(self):
|
|
class TargetEvents(event.Events):
|
|
@classmethod
|
|
def _listen(cls, event_key):
|
|
fn = event_key._listen_fn
|
|
|
|
def adapt(*args):
|
|
fn(*["adapted %s" % arg for arg in args])
|
|
|
|
event_key = event_key.with_wrapper(adapt)
|
|
|
|
event_key.base_listen()
|
|
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
def event_five(self, x, y, z, q):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
return Target
|
|
|
|
def test_kw_accept(self):
|
|
TargetOne = self._fixture()
|
|
|
|
canary = Mock()
|
|
|
|
@event.listens_for(TargetOne, "event_one", named=True)
|
|
def handler1(**kw):
|
|
canary(kw)
|
|
|
|
TargetOne().dispatch.event_one(4, 5)
|
|
|
|
eq_(canary.mock_calls, [call({"x": 4, "y": 5})])
|
|
|
|
def test_kw_accept_wrapped(self):
|
|
TargetOne = self._wrapped_fixture()
|
|
|
|
canary = Mock()
|
|
|
|
@event.listens_for(TargetOne, "event_one", named=True)
|
|
def handler1(**kw):
|
|
canary(kw)
|
|
|
|
TargetOne().dispatch.event_one(4, 5)
|
|
|
|
eq_(canary.mock_calls, [call({"y": "adapted 5", "x": "adapted 4"})])
|
|
|
|
def test_partial_kw_accept(self):
|
|
TargetOne = self._fixture()
|
|
|
|
canary = Mock()
|
|
|
|
@event.listens_for(TargetOne, "event_five", named=True)
|
|
def handler1(z, y, **kw):
|
|
canary(z, y, kw)
|
|
|
|
TargetOne().dispatch.event_five(4, 5, 6, 7)
|
|
|
|
eq_(canary.mock_calls, [call(6, 5, {"x": 4, "q": 7})])
|
|
|
|
def test_partial_kw_accept_wrapped(self):
|
|
TargetOne = self._wrapped_fixture()
|
|
|
|
canary = Mock()
|
|
|
|
@event.listens_for(TargetOne, "event_five", named=True)
|
|
def handler1(z, y, **kw):
|
|
canary(z, y, kw)
|
|
|
|
TargetOne().dispatch.event_five(4, 5, 6, 7)
|
|
|
|
eq_(
|
|
canary.mock_calls,
|
|
[
|
|
call(
|
|
"adapted 6",
|
|
"adapted 5",
|
|
{"q": "adapted 7", "x": "adapted 4"},
|
|
)
|
|
],
|
|
)
|
|
|
|
def test_kw_accept_plus_kw(self):
|
|
TargetOne = self._fixture()
|
|
canary = Mock()
|
|
|
|
@event.listens_for(TargetOne, "event_two", named=True)
|
|
def handler1(**kw):
|
|
canary(kw)
|
|
|
|
TargetOne().dispatch.event_two(4, 5, z=8, q=5)
|
|
|
|
eq_(canary.mock_calls, [call({"x": 4, "y": 5, "z": 8, "q": 5})])
|
|
|
|
|
|
class LegacySignatureTest(fixtures.TestBase):
|
|
"""test adaption of legacy args"""
|
|
|
|
def setUp(self):
|
|
class TargetEventsOne(event.Events):
|
|
@event._legacy_signature("0.9", ["x", "y"])
|
|
def event_three(self, x, y, z, q):
|
|
pass
|
|
|
|
@event._legacy_signature("0.9", ["x", "y", "**kw"])
|
|
def event_four(self, x, y, z, q, **kw):
|
|
pass
|
|
|
|
@event._legacy_signature(
|
|
"0.9", ["x", "y", "z", "q"], lambda x, y: (x, y, x + y, x * y)
|
|
)
|
|
def event_six(self, x, y):
|
|
pass
|
|
|
|
class TargetOne(object):
|
|
dispatch = event.dispatcher(TargetEventsOne)
|
|
|
|
self.TargetOne = TargetOne
|
|
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(
|
|
self.TargetOne.__dict__["dispatch"].events
|
|
)
|
|
|
|
def test_legacy_accept(self):
|
|
canary = Mock()
|
|
|
|
@event.listens_for(self.TargetOne, "event_three")
|
|
def handler1(x, y):
|
|
canary(x, y)
|
|
|
|
self.TargetOne().dispatch.event_three(4, 5, 6, 7)
|
|
|
|
eq_(canary.mock_calls, [call(4, 5)])
|
|
|
|
def test_legacy_accept_kw_cls(self):
|
|
canary = Mock()
|
|
|
|
@event.listens_for(self.TargetOne, "event_four")
|
|
def handler1(x, y, **kw):
|
|
canary(x, y, kw)
|
|
|
|
self._test_legacy_accept_kw(self.TargetOne(), canary)
|
|
|
|
def test_legacy_accept_kw_instance(self):
|
|
canary = Mock()
|
|
|
|
inst = self.TargetOne()
|
|
|
|
@event.listens_for(inst, "event_four")
|
|
def handler1(x, y, **kw):
|
|
canary(x, y, kw)
|
|
|
|
self._test_legacy_accept_kw(inst, canary)
|
|
|
|
def test_legacy_accept_partial(self):
|
|
canary = Mock()
|
|
|
|
def evt(a, x, y, **kw):
|
|
canary(a, x, y, **kw)
|
|
|
|
from functools import partial
|
|
|
|
evt_partial = partial(evt, 5)
|
|
target = self.TargetOne()
|
|
event.listen(target, "event_four", evt_partial)
|
|
# can't do legacy accept on a partial; we can't inspect it
|
|
assert_raises(
|
|
TypeError, target.dispatch.event_four, 4, 5, 6, 7, foo="bar"
|
|
)
|
|
target.dispatch.event_four(4, 5, foo="bar")
|
|
eq_(canary.mock_calls, [call(5, 4, 5, foo="bar")])
|
|
|
|
def _test_legacy_accept_kw(self, target, canary):
|
|
target.dispatch.event_four(4, 5, 6, 7, foo="bar")
|
|
|
|
eq_(canary.mock_calls, [call(4, 5, {"foo": "bar"})])
|
|
|
|
def test_complex_legacy_accept(self):
|
|
canary = Mock()
|
|
|
|
@event.listens_for(self.TargetOne, "event_six")
|
|
def handler1(x, y, z, q):
|
|
canary(x, y, z, q)
|
|
|
|
self.TargetOne().dispatch.event_six(4, 5)
|
|
eq_(canary.mock_calls, [call(4, 5, 9, 20)])
|
|
|
|
def test_legacy_accept_from_method(self):
|
|
canary = Mock()
|
|
|
|
class MyClass(object):
|
|
def handler1(self, x, y):
|
|
canary(x, y)
|
|
|
|
event.listen(self.TargetOne, "event_three", MyClass().handler1)
|
|
|
|
self.TargetOne().dispatch.event_three(4, 5, 6, 7)
|
|
eq_(canary.mock_calls, [call(4, 5)])
|
|
|
|
def test_standard_accept_has_legacies(self):
|
|
canary = Mock()
|
|
|
|
event.listen(self.TargetOne, "event_three", canary)
|
|
|
|
self.TargetOne().dispatch.event_three(4, 5)
|
|
|
|
eq_(canary.mock_calls, [call(4, 5)])
|
|
|
|
def test_kw_accept_has_legacies(self):
|
|
canary = Mock()
|
|
|
|
@event.listens_for(self.TargetOne, "event_three", named=True)
|
|
def handler1(**kw):
|
|
canary(kw)
|
|
|
|
self.TargetOne().dispatch.event_three(4, 5, 6, 7)
|
|
|
|
eq_(canary.mock_calls, [call({"x": 4, "y": 5, "z": 6, "q": 7})])
|
|
|
|
def test_kw_accept_plus_kw_has_legacies(self):
|
|
canary = Mock()
|
|
|
|
@event.listens_for(self.TargetOne, "event_four", named=True)
|
|
def handler1(**kw):
|
|
canary(kw)
|
|
|
|
self.TargetOne().dispatch.event_four(4, 5, 6, 7, foo="bar")
|
|
|
|
eq_(
|
|
canary.mock_calls,
|
|
[call({"x": 4, "y": 5, "z": 6, "q": 7, "foo": "bar"})],
|
|
)
|
|
|
|
|
|
class ClsLevelListenTest(fixtures.TestBase):
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(
|
|
self.TargetOne.__dict__["dispatch"].events
|
|
)
|
|
|
|
def setUp(self):
|
|
class TargetEventsOne(event.Events):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
class TargetOne(object):
|
|
dispatch = event.dispatcher(TargetEventsOne)
|
|
|
|
self.TargetOne = TargetOne
|
|
|
|
def test_lis_subcalss_lis(self):
|
|
@event.listens_for(self.TargetOne, "event_one")
|
|
def handler1(x, y):
|
|
pass
|
|
|
|
class SubTarget(self.TargetOne):
|
|
pass
|
|
|
|
@event.listens_for(self.TargetOne, "event_one")
|
|
def handler2(x, y):
|
|
pass
|
|
|
|
eq_(len(SubTarget().dispatch.event_one), 2)
|
|
|
|
def test_lis_multisub_lis(self):
|
|
@event.listens_for(self.TargetOne, "event_one")
|
|
def handler1(x, y):
|
|
pass
|
|
|
|
class SubTarget(self.TargetOne):
|
|
pass
|
|
|
|
class SubSubTarget(SubTarget):
|
|
pass
|
|
|
|
@event.listens_for(self.TargetOne, "event_one")
|
|
def handler2(x, y):
|
|
pass
|
|
|
|
eq_(len(SubTarget().dispatch.event_one), 2)
|
|
eq_(len(SubSubTarget().dispatch.event_one), 2)
|
|
|
|
def test_two_sub_lis(self):
|
|
class SubTarget1(self.TargetOne):
|
|
pass
|
|
|
|
class SubTarget2(self.TargetOne):
|
|
pass
|
|
|
|
@event.listens_for(self.TargetOne, "event_one")
|
|
def handler1(x, y):
|
|
pass
|
|
|
|
@event.listens_for(SubTarget1, "event_one")
|
|
def handler2(x, y):
|
|
pass
|
|
|
|
s1 = SubTarget1()
|
|
assert handler1 in s1.dispatch.event_one
|
|
assert handler2 in s1.dispatch.event_one
|
|
|
|
s2 = SubTarget2()
|
|
assert handler1 in s2.dispatch.event_one
|
|
assert handler2 not in s2.dispatch.event_one
|
|
|
|
|
|
class AcceptTargetsTest(fixtures.TestBase):
|
|
"""Test default target acceptance."""
|
|
|
|
def setUp(self):
|
|
class TargetEventsOne(event.Events):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
class TargetEventsTwo(event.Events):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
class TargetOne(object):
|
|
dispatch = event.dispatcher(TargetEventsOne)
|
|
|
|
class TargetTwo(object):
|
|
dispatch = event.dispatcher(TargetEventsTwo)
|
|
|
|
self.TargetOne = TargetOne
|
|
self.TargetTwo = TargetTwo
|
|
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(
|
|
self.TargetOne.__dict__["dispatch"].events
|
|
)
|
|
event.base._remove_dispatcher(
|
|
self.TargetTwo.__dict__["dispatch"].events
|
|
)
|
|
|
|
def test_target_accept(self):
|
|
"""Test that events of the same name are routed to the correct
|
|
collection based on the type of target given.
|
|
|
|
"""
|
|
|
|
def listen_one(x, y):
|
|
pass
|
|
|
|
def listen_two(x, y):
|
|
pass
|
|
|
|
def listen_three(x, y):
|
|
pass
|
|
|
|
def listen_four(x, y):
|
|
pass
|
|
|
|
event.listen(self.TargetOne, "event_one", listen_one)
|
|
event.listen(self.TargetTwo, "event_one", listen_two)
|
|
|
|
eq_(list(self.TargetOne().dispatch.event_one), [listen_one])
|
|
|
|
eq_(list(self.TargetTwo().dispatch.event_one), [listen_two])
|
|
|
|
t1 = self.TargetOne()
|
|
t2 = self.TargetTwo()
|
|
|
|
event.listen(t1, "event_one", listen_three)
|
|
event.listen(t2, "event_one", listen_four)
|
|
|
|
eq_(list(t1.dispatch.event_one), [listen_one, listen_three])
|
|
|
|
eq_(list(t2.dispatch.event_one), [listen_two, listen_four])
|
|
|
|
|
|
class CustomTargetsTest(fixtures.TestBase):
|
|
"""Test custom target acceptance."""
|
|
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
@classmethod
|
|
def _accept_with(cls, target):
|
|
if target == "one":
|
|
return Target
|
|
else:
|
|
return None
|
|
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
self.Target = Target
|
|
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
|
|
|
|
def test_indirect(self):
|
|
def listen(x, y):
|
|
pass
|
|
|
|
event.listen("one", "event_one", listen)
|
|
|
|
eq_(list(self.Target().dispatch.event_one), [listen])
|
|
|
|
assert_raises(
|
|
exc.InvalidRequestError,
|
|
event.listen,
|
|
listen,
|
|
"event_one",
|
|
self.Target,
|
|
)
|
|
|
|
|
|
class SubclassGrowthTest(fixtures.TestBase):
|
|
"""test that ad-hoc subclasses are garbage collected."""
|
|
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
def some_event(self, x, y):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
self.Target = Target
|
|
|
|
def test_subclass(self):
|
|
class SubTarget(self.Target):
|
|
pass
|
|
|
|
st = SubTarget()
|
|
st.dispatch.some_event(1, 2)
|
|
del st
|
|
del SubTarget
|
|
gc_collect()
|
|
eq_(self.Target.__subclasses__(), [])
|
|
|
|
|
|
class ListenOverrideTest(fixtures.TestBase):
|
|
"""Test custom listen functions which change the listener function
|
|
signature."""
|
|
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
@classmethod
|
|
def _listen(cls, event_key, add=False):
|
|
fn = event_key.fn
|
|
if add:
|
|
|
|
def adapt(x, y):
|
|
fn(x + y)
|
|
|
|
event_key = event_key.with_wrapper(adapt)
|
|
|
|
event_key.base_listen()
|
|
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
self.Target = Target
|
|
|
|
def tearDown(self):
|
|
event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
|
|
|
|
def test_listen_override(self):
|
|
listen_one = Mock()
|
|
listen_two = Mock()
|
|
|
|
event.listen(self.Target, "event_one", listen_one, add=True)
|
|
event.listen(self.Target, "event_one", listen_two)
|
|
|
|
t1 = self.Target()
|
|
t1.dispatch.event_one(5, 7)
|
|
t1.dispatch.event_one(10, 5)
|
|
|
|
eq_(listen_one.mock_calls, [call(12), call(15)])
|
|
eq_(listen_two.mock_calls, [call(5, 7), call(10, 5)])
|
|
|
|
def test_remove_clslevel(self):
|
|
listen_one = Mock()
|
|
event.listen(self.Target, "event_one", listen_one, add=True)
|
|
t1 = self.Target()
|
|
t1.dispatch.event_one(5, 7)
|
|
eq_(listen_one.mock_calls, [call(12)])
|
|
event.remove(self.Target, "event_one", listen_one)
|
|
t1.dispatch.event_one(10, 5)
|
|
eq_(listen_one.mock_calls, [call(12)])
|
|
|
|
def test_remove_instancelevel(self):
|
|
listen_one = Mock()
|
|
t1 = self.Target()
|
|
event.listen(t1, "event_one", listen_one, add=True)
|
|
t1.dispatch.event_one(5, 7)
|
|
eq_(listen_one.mock_calls, [call(12)])
|
|
event.remove(t1, "event_one", listen_one)
|
|
t1.dispatch.event_one(10, 5)
|
|
eq_(listen_one.mock_calls, [call(12)])
|
|
|
|
|
|
class PropagateTest(fixtures.TestBase):
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
def event_one(self, arg):
|
|
pass
|
|
|
|
def event_two(self, arg):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
self.Target = Target
|
|
|
|
def test_propagate(self):
|
|
listen_one = Mock()
|
|
listen_two = Mock()
|
|
|
|
t1 = self.Target()
|
|
|
|
event.listen(t1, "event_one", listen_one, propagate=True)
|
|
event.listen(t1, "event_two", listen_two)
|
|
|
|
t2 = self.Target()
|
|
|
|
t2.dispatch._update(t1.dispatch)
|
|
|
|
t2.dispatch.event_one(t2, 1)
|
|
t2.dispatch.event_two(t2, 2)
|
|
|
|
eq_(listen_one.mock_calls, [call(t2, 1)])
|
|
eq_(listen_two.mock_calls, [])
|
|
|
|
|
|
class JoinTest(fixtures.TestBase):
|
|
def setUp(self):
|
|
class TargetEvents(event.Events):
|
|
def event_one(self, target, arg):
|
|
pass
|
|
|
|
class BaseTarget(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
class TargetFactory(BaseTarget):
|
|
def create(self):
|
|
return TargetElement(self)
|
|
|
|
class TargetElement(BaseTarget):
|
|
def __init__(self, parent):
|
|
self.dispatch = self.dispatch._join(parent.dispatch)
|
|
|
|
def run_event(self, arg):
|
|
list(self.dispatch.event_one)
|
|
self.dispatch.event_one(self, arg)
|
|
|
|
self.BaseTarget = BaseTarget
|
|
self.TargetFactory = TargetFactory
|
|
self.TargetElement = TargetElement
|
|
|
|
def tearDown(self):
|
|
for cls in (self.TargetElement, self.TargetFactory, self.BaseTarget):
|
|
if "dispatch" in cls.__dict__:
|
|
event.base._remove_dispatcher(cls.__dict__["dispatch"].events)
|
|
|
|
def test_neither(self):
|
|
element = self.TargetFactory().create()
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
def test_kw_ok(self):
|
|
l1 = Mock()
|
|
|
|
def listen(**kw):
|
|
l1(kw)
|
|
|
|
event.listen(self.TargetFactory, "event_one", listen, named=True)
|
|
element = self.TargetFactory().create()
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
eq_(
|
|
l1.mock_calls,
|
|
[
|
|
call({"target": element, "arg": 1}),
|
|
call({"target": element, "arg": 2}),
|
|
],
|
|
)
|
|
|
|
def test_parent_class_only(self):
|
|
l1 = Mock()
|
|
|
|
event.listen(self.TargetFactory, "event_one", l1)
|
|
|
|
element = self.TargetFactory().create()
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_class_child_class(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
|
|
event.listen(self.TargetFactory, "event_one", l1)
|
|
event.listen(self.TargetElement, "event_one", l2)
|
|
|
|
element = self.TargetFactory().create()
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
eq_(
|
|
l2.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_class_child_instance_apply_after(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
|
|
event.listen(self.TargetFactory, "event_one", l1)
|
|
element = self.TargetFactory().create()
|
|
|
|
element.run_event(1)
|
|
|
|
event.listen(element, "event_one", l2)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
eq_(l2.mock_calls, [call(element, 2), call(element, 3)])
|
|
|
|
def test_parent_class_child_instance_apply_before(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
|
|
event.listen(self.TargetFactory, "event_one", l1)
|
|
element = self.TargetFactory().create()
|
|
|
|
event.listen(element, "event_one", l2)
|
|
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
eq_(
|
|
l2.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_instance_child_class_apply_before(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
|
|
event.listen(self.TargetElement, "event_one", l2)
|
|
|
|
factory = self.TargetFactory()
|
|
event.listen(factory, "event_one", l1)
|
|
|
|
element = factory.create()
|
|
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
eq_(
|
|
l2.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_instance_child_class_apply_after(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
|
|
event.listen(self.TargetElement, "event_one", l2)
|
|
|
|
factory = self.TargetFactory()
|
|
element = factory.create()
|
|
|
|
element.run_event(1)
|
|
|
|
event.listen(factory, "event_one", l1)
|
|
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
# if _JoinedListener fixes .listeners
|
|
# at construction time, then we don't get
|
|
# the new listeners.
|
|
# eq_(l1.mock_calls, [])
|
|
|
|
# alternatively, if _JoinedListener shares the list
|
|
# using a @property, then we get them, at the arguable
|
|
# expense of the extra method call to access the .listeners
|
|
# collection
|
|
eq_(l1.mock_calls, [call(element, 2), call(element, 3)])
|
|
|
|
eq_(
|
|
l2.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_instance_child_instance_apply_before(self):
|
|
l1 = Mock()
|
|
l2 = Mock()
|
|
factory = self.TargetFactory()
|
|
|
|
event.listen(factory, "event_one", l1)
|
|
|
|
element = factory.create()
|
|
event.listen(element, "event_one", l2)
|
|
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
eq_(
|
|
l2.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
def test_parent_events_child_no_events(self):
|
|
l1 = Mock()
|
|
factory = self.TargetFactory()
|
|
|
|
event.listen(self.TargetElement, "event_one", l1)
|
|
element = factory.create()
|
|
|
|
element.run_event(1)
|
|
element.run_event(2)
|
|
element.run_event(3)
|
|
|
|
eq_(
|
|
l1.mock_calls,
|
|
[call(element, 1), call(element, 2), call(element, 3)],
|
|
)
|
|
|
|
|
|
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):
|
|
def event_one(self, x, y):
|
|
pass
|
|
|
|
def event_two(self, x):
|
|
pass
|
|
|
|
def event_three(self, x):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
return Target
|
|
|
|
def _wrapped_fixture(self):
|
|
class TargetEvents(event.Events):
|
|
@classmethod
|
|
def _listen(cls, event_key):
|
|
fn = event_key._listen_fn
|
|
|
|
def adapt(value):
|
|
fn("adapted " + value)
|
|
|
|
event_key = event_key.with_wrapper(adapt)
|
|
|
|
event_key.base_listen()
|
|
|
|
def event_one(self, x):
|
|
pass
|
|
|
|
class Target(object):
|
|
dispatch = event.dispatcher(TargetEvents)
|
|
|
|
return Target
|
|
|
|
def test_clslevel(self):
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
|
|
event.listen(Target, "event_two", m1)
|
|
|
|
t1 = Target()
|
|
t1.dispatch.event_two("x")
|
|
|
|
event.remove(Target, "event_two", m1)
|
|
|
|
t1.dispatch.event_two("y")
|
|
|
|
eq_(m1.mock_calls, [call("x")])
|
|
|
|
def test_clslevel_subclass(self):
|
|
Target = self._fixture()
|
|
|
|
class SubTarget(Target):
|
|
pass
|
|
|
|
m1 = Mock()
|
|
|
|
event.listen(Target, "event_two", m1)
|
|
|
|
t1 = SubTarget()
|
|
t1.dispatch.event_two("x")
|
|
|
|
event.remove(Target, "event_two", m1)
|
|
|
|
t1.dispatch.event_two("y")
|
|
|
|
eq_(m1.mock_calls, [call("x")])
|
|
|
|
def test_instance(self):
|
|
Target = self._fixture()
|
|
|
|
class Foo(object):
|
|
def __init__(self):
|
|
self.mock = Mock()
|
|
|
|
def evt(self, arg):
|
|
self.mock(arg)
|
|
|
|
f1 = Foo()
|
|
f2 = Foo()
|
|
|
|
event.listen(Target, "event_one", f1.evt)
|
|
event.listen(Target, "event_one", f2.evt)
|
|
|
|
t1 = Target()
|
|
t1.dispatch.event_one("x")
|
|
|
|
event.remove(Target, "event_one", f1.evt)
|
|
|
|
t1.dispatch.event_one("y")
|
|
|
|
eq_(f1.mock.mock_calls, [call("x")])
|
|
eq_(f2.mock.mock_calls, [call("x"), call("y")])
|
|
|
|
def test_once(self):
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
m2 = Mock()
|
|
m3 = Mock()
|
|
m4 = Mock()
|
|
|
|
event.listen(Target, "event_one", m1)
|
|
event.listen(Target, "event_one", m2, once=True)
|
|
event.listen(Target, "event_one", m3, once=True)
|
|
|
|
t1 = Target()
|
|
t1.dispatch.event_one("x")
|
|
t1.dispatch.event_one("y")
|
|
|
|
event.listen(Target, "event_one", m4, once=True)
|
|
t1.dispatch.event_one("z")
|
|
t1.dispatch.event_one("q")
|
|
|
|
eq_(m1.mock_calls, [call("x"), call("y"), call("z"), call("q")])
|
|
eq_(m2.mock_calls, [call("x")])
|
|
eq_(m3.mock_calls, [call("x")])
|
|
eq_(m4.mock_calls, [call("z")])
|
|
|
|
def test_once_unless_exception(self):
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
m2 = Mock()
|
|
m3 = Mock()
|
|
m4 = Mock()
|
|
|
|
m1.side_effect = ValueError
|
|
m2.side_effect = ValueError
|
|
m3.side_effect = ValueError
|
|
|
|
event.listen(Target, "event_one", m1)
|
|
event.listen(Target, "event_one", m2, _once_unless_exception=True)
|
|
event.listen(Target, "event_one", m3, _once_unless_exception=True)
|
|
|
|
t1 = Target()
|
|
|
|
# only m1 is called, raises
|
|
assert_raises(ValueError, t1.dispatch.event_one, "x")
|
|
|
|
# now m1 and m2 can be called but not m3
|
|
m1.side_effect = None
|
|
|
|
assert_raises(ValueError, t1.dispatch.event_one, "y")
|
|
|
|
# now m3 can be called
|
|
m2.side_effect = None
|
|
|
|
event.listen(Target, "event_one", m4, _once_unless_exception=True)
|
|
assert_raises(ValueError, t1.dispatch.event_one, "z")
|
|
|
|
assert_raises(ValueError, t1.dispatch.event_one, "q")
|
|
|
|
eq_(m1.mock_calls, [call("x"), call("y"), call("z"), call("q")])
|
|
eq_(m2.mock_calls, [call("y"), call("z")])
|
|
eq_(m3.mock_calls, [call("z"), call("q")])
|
|
eq_(m4.mock_calls, []) # m4 never got called because m3 blocked it
|
|
|
|
# now m4 can be called
|
|
m3.side_effect = None
|
|
|
|
t1.dispatch.event_one("p")
|
|
eq_(
|
|
m1.mock_calls,
|
|
[call("x"), call("y"), call("z"), call("q"), call("p")],
|
|
)
|
|
|
|
# m2 already got called, so no "p"
|
|
eq_(m2.mock_calls, [call("y"), call("z")])
|
|
eq_(m3.mock_calls, [call("z"), call("q"), call("p")])
|
|
eq_(m4.mock_calls, [call("p")])
|
|
|
|
t1.dispatch.event_one("j")
|
|
eq_(
|
|
m1.mock_calls,
|
|
[call("x"), call("y"), call("z"), call("q"), call("p"), call("j")],
|
|
)
|
|
|
|
# nobody got "j" because they've all been successful
|
|
eq_(m2.mock_calls, [call("y"), call("z")])
|
|
eq_(m3.mock_calls, [call("z"), call("q"), call("p")])
|
|
eq_(m4.mock_calls, [call("p")])
|
|
|
|
def test_once_doesnt_dereference_listener(self):
|
|
# test for [ticket:4794]
|
|
|
|
Target = self._fixture()
|
|
|
|
canary = Mock()
|
|
|
|
def go(target, given_id):
|
|
def anonymous(run_id):
|
|
canary(run_id, given_id)
|
|
|
|
event.listen(target, "event_one", anonymous, once=True)
|
|
|
|
t1 = Target()
|
|
|
|
assert_calls = []
|
|
given_ids = []
|
|
for given_id in range(100):
|
|
given_ids.append(given_id)
|
|
go(t1, given_id)
|
|
if given_id % 10 == 0:
|
|
t1.dispatch.event_one(given_id)
|
|
assert_calls.extend(call(given_id, i) for i in given_ids)
|
|
given_ids[:] = []
|
|
|
|
eq_(canary.mock_calls, assert_calls)
|
|
|
|
def test_propagate(self):
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
|
|
t1 = Target()
|
|
t2 = Target()
|
|
|
|
event.listen(t1, "event_one", m1, propagate=True)
|
|
event.listen(t1, "event_two", m1, propagate=False)
|
|
|
|
t2.dispatch._update(t1.dispatch)
|
|
|
|
t1.dispatch.event_one("t1e1x")
|
|
t1.dispatch.event_two("t1e2x")
|
|
t2.dispatch.event_one("t2e1x")
|
|
t2.dispatch.event_two("t2e2x")
|
|
|
|
event.remove(t1, "event_one", m1)
|
|
event.remove(t1, "event_two", m1)
|
|
|
|
t1.dispatch.event_one("t1e1y")
|
|
t1.dispatch.event_two("t1e2y")
|
|
t2.dispatch.event_one("t2e1y")
|
|
t2.dispatch.event_two("t2e2y")
|
|
|
|
eq_(m1.mock_calls, [call("t1e1x"), call("t1e2x"), call("t2e1x")])
|
|
|
|
@testing.requires.predictable_gc
|
|
def test_listener_collection_removed_cleanup(self):
|
|
from sqlalchemy.event import registry
|
|
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
|
|
t1 = Target()
|
|
|
|
event.listen(t1, "event_one", m1)
|
|
|
|
key = (id(t1), "event_one", id(m1))
|
|
|
|
assert key in registry._key_to_collection
|
|
collection_ref = list(registry._key_to_collection[key])[0]
|
|
assert collection_ref in registry._collection_to_key
|
|
|
|
t1.dispatch.event_one("t1")
|
|
|
|
del t1
|
|
|
|
gc_collect()
|
|
|
|
assert key not in registry._key_to_collection
|
|
assert collection_ref not in registry._collection_to_key
|
|
|
|
def test_remove_not_listened(self):
|
|
Target = self._fixture()
|
|
|
|
m1 = Mock()
|
|
|
|
t1 = Target()
|
|
|
|
event.listen(t1, "event_one", m1, propagate=True)
|
|
event.listen(t1, "event_three", m1)
|
|
|
|
event.remove(t1, "event_one", m1)
|
|
assert_raises_message(
|
|
exc.InvalidRequestError,
|
|
r"No listeners found for event <.*Target.*> / "
|
|
r"'event_two' / <Mock.*> ",
|
|
event.remove,
|
|
t1,
|
|
"event_two",
|
|
m1,
|
|
)
|
|
|
|
event.remove(t1, "event_three", m1)
|
|
|
|
def test_no_remove_in_event(self):
|
|
Target = self._fixture()
|
|
|
|
t1 = Target()
|
|
|
|
def evt():
|
|
event.remove(t1, "event_one", evt)
|
|
|
|
event.listen(t1, "event_one", evt)
|
|
|
|
assert_raises_message(
|
|
Exception, "deque mutated during iteration", t1.dispatch.event_one
|
|
)
|
|
|
|
def test_no_add_in_event(self):
|
|
Target = self._fixture()
|
|
|
|
t1 = Target()
|
|
|
|
m1 = Mock()
|
|
|
|
def evt():
|
|
event.listen(t1, "event_one", m1)
|
|
|
|
event.listen(t1, "event_one", evt)
|
|
|
|
assert_raises_message(
|
|
Exception, "deque mutated during iteration", t1.dispatch.event_one
|
|
)
|
|
|
|
def test_remove_plain_named(self):
|
|
Target = self._fixture()
|
|
|
|
listen_one = Mock()
|
|
t1 = Target()
|
|
event.listen(t1, "event_one", listen_one, named=True)
|
|
t1.dispatch.event_one("t1")
|
|
|
|
eq_(listen_one.mock_calls, [call(x="t1")])
|
|
event.remove(t1, "event_one", listen_one)
|
|
t1.dispatch.event_one("t2")
|
|
|
|
eq_(listen_one.mock_calls, [call(x="t1")])
|
|
|
|
def test_remove_wrapped_named(self):
|
|
Target = self._wrapped_fixture()
|
|
|
|
listen_one = Mock()
|
|
t1 = Target()
|
|
event.listen(t1, "event_one", listen_one, named=True)
|
|
t1.dispatch.event_one("t1")
|
|
|
|
eq_(listen_one.mock_calls, [call(x="adapted t1")])
|
|
event.remove(t1, "event_one", listen_one)
|
|
t1.dispatch.event_one("t2")
|
|
|
|
eq_(listen_one.mock_calls, [call(x="adapted t1")])
|
|
|
|
def test_double_event_nonwrapped(self):
|
|
Target = self._fixture()
|
|
|
|
listen_one = Mock()
|
|
t1 = Target()
|
|
event.listen(t1, "event_one", listen_one)
|
|
event.listen(t1, "event_one", listen_one)
|
|
|
|
t1.dispatch.event_one("t1")
|
|
|
|
# doubles are eliminated
|
|
eq_(listen_one.mock_calls, [call("t1")])
|
|
|
|
# only one remove needed
|
|
event.remove(t1, "event_one", listen_one)
|
|
t1.dispatch.event_one("t2")
|
|
|
|
eq_(listen_one.mock_calls, [call("t1")])
|
|
|
|
def test_double_event_wrapped(self):
|
|
# this is issue #3199
|
|
Target = self._wrapped_fixture()
|
|
|
|
listen_one = Mock()
|
|
t1 = Target()
|
|
|
|
event.listen(t1, "event_one", listen_one)
|
|
event.listen(t1, "event_one", listen_one)
|
|
|
|
t1.dispatch.event_one("t1")
|
|
|
|
# doubles are eliminated
|
|
eq_(listen_one.mock_calls, [call("adapted t1")])
|
|
|
|
# only one remove needed
|
|
event.remove(t1, "event_one", listen_one)
|
|
t1.dispatch.event_one("t2")
|
|
|
|
eq_(listen_one.mock_calls, [call("adapted t1")])
|