- Fixed bug that affected many classes of event, particularly

ORM events but also engine events, where the usual logic of
"de duplicating" a redundant call to :func:`.event.listen`
with the same arguments would fail, for those events where the
listener function is wrapped.  An assertion would be hit within
registry.py.  This assertion has now been integrated into the
deduplication check, with the added bonus of a simpler means
of checking deduplication across the board.
fixes #3199
This commit is contained in:
Mike Bayer
2014-09-18 15:24:40 -04:00
parent f82f6d55dc
commit 9ae4db27b9
4 changed files with 90 additions and 15 deletions
+14
View File
@@ -13,6 +13,20 @@
.. changelog::
:version: 0.9.8
.. change::
:tags: bug, orm
:versions: 1.0.0
:tickets: 3199
Fixed bug that affected many classes of event, particularly
ORM events but also engine events, where the usual logic of
"de duplicating" a redundant call to :func:`.event.listen`
with the same arguments would fail, for those events where the
listener function is wrapped. An assertion would be hit within
registry.py. This assertion has now been integrated into the
deduplication check, with the added bonus of a simpler means
of checking deduplication across the board.
.. change::
:tags: bug, mssql
:versions: 1.0.0
+2 -4
View File
@@ -319,14 +319,12 @@ class _ListenerCollection(RefCollection, _CompoundListener):
registry._stored_in_collection_multi(self, other, to_associate)
def insert(self, event_key, propagate):
if event_key._listen_fn not in self.listeners:
event_key.prepend_to_list(self, self.listeners)
if event_key.prepend_to_list(self, self.listeners):
if propagate:
self.propagate.add(event_key._listen_fn)
def append(self, event_key, propagate):
if event_key._listen_fn not in self.listeners:
event_key.append_to_list(self, self.listeners)
if event_key.append_to_list(self, self.listeners):
if propagate:
self.propagate.add(event_key._listen_fn)
+15 -11
View File
@@ -71,13 +71,15 @@ def _stored_in_collection(event_key, owner):
listen_ref = weakref.ref(event_key._listen_fn)
if owner_ref in dispatch_reg:
assert dispatch_reg[owner_ref] == listen_ref
else:
dispatch_reg[owner_ref] = listen_ref
return False
dispatch_reg[owner_ref] = listen_ref
listener_to_key = _collection_to_key[owner_ref]
listener_to_key[listen_ref] = key
return True
def _removed_from_collection(event_key, owner):
key = event_key._key
@@ -229,18 +231,20 @@ class _EventKey(object):
def _listen_fn(self):
return self.fn_wrap or self.fn
def append_value_to_list(self, owner, list_, value):
_stored_in_collection(self, owner)
list_.append(value)
def append_to_list(self, owner, list_):
_stored_in_collection(self, owner)
list_.append(self._listen_fn)
if _stored_in_collection(self, owner):
list_.append(self._listen_fn)
return True
else:
return False
def remove_from_list(self, owner, list_):
_removed_from_collection(self, owner)
list_.remove(self._listen_fn)
def prepend_to_list(self, owner, list_):
_stored_in_collection(self, owner)
list_.appendleft(self._listen_fn)
if _stored_in_collection(self, owner):
list_.appendleft(self._listen_fn)
return True
else:
return False
+59
View File
@@ -996,6 +996,25 @@ class RemovalTest(fixtures.TestBase):
dispatch = event.dispatcher(TargetEvents)
return Target
def _wrapped_fixture(self):
class TargetEvents(event.Events):
@classmethod
def _listen(cls, event_key):
fn = event_key.fn
def adapt(value):
fn("adapted " + value)
event_key = event_key.with_wrapper(adapt)
event_key.base_listen()
def event_one(self, value):
pass
class Target(object):
dispatch = event.dispatcher(TargetEvents)
return Target
def test_clslevel(self):
Target = self._fixture()
@@ -1194,3 +1213,43 @@ class RemovalTest(fixtures.TestBase):
"deque mutated during iteration",
t1.dispatch.event_one
)
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")])