2
# Copyright (C) 2005-2016 the SQLAlchemy authors and contributors
5
# This module is part of SQLAlchemy and is released under
6
# the MIT License: http://www.opensource.org/licenses/mit-license.php
8
"""Provides managed registration services on behalf of :func:`.listen`
11
By "managed registration", we mean that event listening functions and
12
other objects can be added to various collections in such a way that their
13
membership in all those collections can be revoked at once, based on
14
an equivalent :class:`._EventKey`.
18
from __future__ import absolute_import
23
from .. import exc, util
26
_key_to_collection = collections.defaultdict(dict)
28
Given an original listen() argument, can locate all
29
listener collections and the listener fn contained
31
(target, identifier, fn) -> {
32
ref(listenercollection) -> ref(listener_fn)
33
ref(listenercollection) -> ref(listener_fn)
34
ref(listenercollection) -> ref(listener_fn)
38
_collection_to_key = collections.defaultdict(dict)
40
Given a _ListenerCollection or _ClsLevelListener, can locate
41
all the original listen() arguments and the listener fn contained
43
ref(listenercollection) -> {
44
ref(listener_fn) -> (target, identifier, fn),
45
ref(listener_fn) -> (target, identifier, fn),
46
ref(listener_fn) -> (target, identifier, fn),
51
def _collection_gced(ref):
52
# defaultdict, so can't get a KeyError
53
if not _collection_to_key or ref not in _collection_to_key:
55
listener_to_key = _collection_to_key.pop(ref)
56
for key in listener_to_key.values():
57
if key in _key_to_collection:
58
# defaultdict, so can't get a KeyError
59
dispatch_reg = _key_to_collection[key]
62
_key_to_collection.pop(key)
65
def _stored_in_collection(event_key, owner):
68
dispatch_reg = _key_to_collection[key]
71
listen_ref = weakref.ref(event_key._listen_fn)
73
if owner_ref in dispatch_reg:
76
dispatch_reg[owner_ref] = listen_ref
78
listener_to_key = _collection_to_key[owner_ref]
79
listener_to_key[listen_ref] = key
84
def _removed_from_collection(event_key, owner):
87
dispatch_reg = _key_to_collection[key]
89
listen_ref = weakref.ref(event_key._listen_fn)
92
dispatch_reg.pop(owner_ref, None)
94
del _key_to_collection[key]
96
if owner_ref in _collection_to_key:
97
listener_to_key = _collection_to_key[owner_ref]
98
listener_to_key.pop(listen_ref)
101
def _stored_in_collection_multi(newowner, oldowner, elements):
105
oldowner = oldowner.ref
106
newowner = newowner.ref
108
old_listener_to_key = _collection_to_key[oldowner]
109
new_listener_to_key = _collection_to_key[newowner]
111
for listen_fn in elements:
112
listen_ref = weakref.ref(listen_fn)
113
key = old_listener_to_key[listen_ref]
114
dispatch_reg = _key_to_collection[key]
115
if newowner in dispatch_reg:
116
assert dispatch_reg[newowner] == listen_ref
118
dispatch_reg[newowner] = listen_ref
120
new_listener_to_key[listen_ref] = key
123
def _clear(owner, elements):
128
listener_to_key = _collection_to_key[owner]
129
for listen_fn in elements:
130
listen_ref = weakref.ref(listen_fn)
131
key = listener_to_key[listen_ref]
132
dispatch_reg = _key_to_collection[key]
133
dispatch_reg.pop(owner, None)
136
del _key_to_collection[key]
139
class _EventKey(object):
140
"""Represent :func:`.listen` arguments.
144
'target', 'identifier', 'fn', 'fn_key', 'fn_wrap', 'dispatch_target'
147
def __init__(self, target, identifier,
148
fn, dispatch_target, _fn_wrap=None):
150
self.identifier = identifier
152
if isinstance(fn, types.MethodType):
153
self.fn_key = id(fn.__func__), id(fn.__self__)
156
self.fn_wrap = _fn_wrap
157
self.dispatch_target = dispatch_target
161
return (id(self.target), self.identifier, self.fn_key)
163
def with_wrapper(self, fn_wrap):
164
if fn_wrap is self._listen_fn:
171
self.dispatch_target,
175
def with_dispatch_target(self, dispatch_target):
176
if dispatch_target is self.dispatch_target:
184
_fn_wrap=self.fn_wrap
187
def listen(self, *args, **kw):
188
once = kw.pop("once", False)
189
named = kw.pop("named", False)
191
target, identifier, fn = \
192
self.dispatch_target, self.identifier, self._listen_fn
194
dispatch_collection = getattr(target.dispatch, identifier)
196
adjusted_fn = dispatch_collection._adjust_fn_spec(fn, named)
198
self = self.with_wrapper(adjusted_fn)
202
util.only_once(self._listen_fn)).listen(*args, **kw)
204
self.dispatch_target.dispatch._listen(self, *args, **kw)
209
if key not in _key_to_collection:
210
raise exc.InvalidRequestError(
211
"No listeners found for event %s / %r / %s " %
212
(self.target, self.identifier, self.fn)
214
dispatch_reg = _key_to_collection.pop(key)
216
for collection_ref, listener_ref in dispatch_reg.items():
217
collection = collection_ref()
218
listener_fn = listener_ref()
219
if collection is not None and listener_fn is not None:
220
collection.remove(self.with_wrapper(listener_fn))
223
"""Return True if this event key is registered to listen.
225
return self._key in _key_to_collection
227
def base_listen(self, propagate=False, insert=False,
230
target, identifier, fn = \
231
self.dispatch_target, self.identifier, self._listen_fn
233
dispatch_collection = getattr(target.dispatch, identifier)
236
dispatch_collection.\
237
for_modify(target.dispatch).insert(self, propagate)
239
dispatch_collection.\
240
for_modify(target.dispatch).append(self, propagate)
243
def _listen_fn(self):
244
return self.fn_wrap or self.fn
246
def append_to_list(self, owner, list_):
247
if _stored_in_collection(self, owner):
248
list_.append(self._listen_fn)
253
def remove_from_list(self, owner, list_):
254
_removed_from_collection(self, owner)
255
list_.remove(self._listen_fn)
257
def prepend_to_list(self, owner, list_):
258
if _stored_in_collection(self, owner):
259
list_.appendleft(self._listen_fn)