Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/event/registry.py : 65%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# event/registry.py
2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
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`
9arguments.
11By "managed registration", we mean that event listening functions and
12other objects can be added to various collections in such a way that their
13membership in all those collections can be revoked at once, based on
14an equivalent :class:`._EventKey`.
16"""
18from __future__ import absolute_import
20import collections
21import types
22import weakref
24from .. import exc
25from .. import util
28_key_to_collection = collections.defaultdict(dict)
29"""
30Given an original listen() argument, can locate all
31listener collections and the listener fn contained
33(target, identifier, fn) -> {
34 ref(listenercollection) -> ref(listener_fn)
35 ref(listenercollection) -> ref(listener_fn)
36 ref(listenercollection) -> ref(listener_fn)
37 }
38"""
40_collection_to_key = collections.defaultdict(dict)
41"""
42Given a _ListenerCollection or _ClsLevelListener, can locate
43all the original listen() arguments and the listener fn contained
45ref(listenercollection) -> {
46 ref(listener_fn) -> (target, identifier, fn),
47 ref(listener_fn) -> (target, identifier, fn),
48 ref(listener_fn) -> (target, identifier, fn),
49 }
50"""
53def _collection_gced(ref):
54 # defaultdict, so can't get a KeyError
55 if not _collection_to_key or ref not in _collection_to_key:
56 return
57 listener_to_key = _collection_to_key.pop(ref)
58 for key in listener_to_key.values():
59 if key in _key_to_collection:
60 # defaultdict, so can't get a KeyError
61 dispatch_reg = _key_to_collection[key]
62 dispatch_reg.pop(ref)
63 if not dispatch_reg:
64 _key_to_collection.pop(key)
67def _stored_in_collection(event_key, owner):
68 key = event_key._key
70 dispatch_reg = _key_to_collection[key]
72 owner_ref = owner.ref
73 listen_ref = weakref.ref(event_key._listen_fn)
75 if owner_ref in dispatch_reg:
76 return False
78 dispatch_reg[owner_ref] = listen_ref
80 listener_to_key = _collection_to_key[owner_ref]
81 listener_to_key[listen_ref] = key
83 return True
86def _removed_from_collection(event_key, owner):
87 key = event_key._key
89 dispatch_reg = _key_to_collection[key]
91 listen_ref = weakref.ref(event_key._listen_fn)
93 owner_ref = owner.ref
94 dispatch_reg.pop(owner_ref, None)
95 if not dispatch_reg:
96 del _key_to_collection[key]
98 if owner_ref in _collection_to_key:
99 listener_to_key = _collection_to_key[owner_ref]
100 listener_to_key.pop(listen_ref)
103def _stored_in_collection_multi(newowner, oldowner, elements):
104 if not elements:
105 return
107 oldowner = oldowner.ref
108 newowner = newowner.ref
110 old_listener_to_key = _collection_to_key[oldowner]
111 new_listener_to_key = _collection_to_key[newowner]
113 for listen_fn in elements:
114 listen_ref = weakref.ref(listen_fn)
115 key = old_listener_to_key[listen_ref]
116 dispatch_reg = _key_to_collection[key]
117 if newowner in dispatch_reg:
118 assert dispatch_reg[newowner] == listen_ref
119 else:
120 dispatch_reg[newowner] = listen_ref
122 new_listener_to_key[listen_ref] = key
125def _clear(owner, elements):
126 if not elements:
127 return
129 owner = owner.ref
130 listener_to_key = _collection_to_key[owner]
131 for listen_fn in elements:
132 listen_ref = weakref.ref(listen_fn)
133 key = listener_to_key[listen_ref]
134 dispatch_reg = _key_to_collection[key]
135 dispatch_reg.pop(owner, None)
137 if not dispatch_reg:
138 del _key_to_collection[key]
141class _EventKey(object):
142 """Represent :func:`.listen` arguments.
143 """
145 __slots__ = (
146 "target",
147 "identifier",
148 "fn",
149 "fn_key",
150 "fn_wrap",
151 "dispatch_target",
152 )
154 def __init__(self, target, identifier, fn, dispatch_target, _fn_wrap=None):
155 self.target = target
156 self.identifier = identifier
157 self.fn = fn
158 if isinstance(fn, types.MethodType):
159 self.fn_key = id(fn.__func__), id(fn.__self__)
160 else:
161 self.fn_key = id(fn)
162 self.fn_wrap = _fn_wrap
163 self.dispatch_target = dispatch_target
165 @property
166 def _key(self):
167 return (id(self.target), self.identifier, self.fn_key)
169 def with_wrapper(self, fn_wrap):
170 if fn_wrap is self._listen_fn:
171 return self
172 else:
173 return _EventKey(
174 self.target,
175 self.identifier,
176 self.fn,
177 self.dispatch_target,
178 _fn_wrap=fn_wrap,
179 )
181 def with_dispatch_target(self, dispatch_target):
182 if dispatch_target is self.dispatch_target:
183 return self
184 else:
185 return _EventKey(
186 self.target,
187 self.identifier,
188 self.fn,
189 dispatch_target,
190 _fn_wrap=self.fn_wrap,
191 )
193 def listen(self, *args, **kw):
194 once = kw.pop("once", False)
195 once_unless_exception = kw.pop("_once_unless_exception", False)
196 named = kw.pop("named", False)
198 target, identifier, fn = (
199 self.dispatch_target,
200 self.identifier,
201 self._listen_fn,
202 )
204 dispatch_collection = getattr(target.dispatch, identifier)
206 adjusted_fn = dispatch_collection._adjust_fn_spec(fn, named)
208 self = self.with_wrapper(adjusted_fn)
210 stub_function = getattr(
211 self.dispatch_target.dispatch._events, self.identifier
212 )
213 if hasattr(stub_function, "_sa_warn"):
214 stub_function._sa_warn()
216 if once or once_unless_exception:
217 self.with_wrapper(
218 util.only_once(
219 self._listen_fn, retry_on_exception=once_unless_exception
220 )
221 ).listen(*args, **kw)
222 else:
223 self.dispatch_target.dispatch._listen(self, *args, **kw)
225 def remove(self):
226 key = self._key
228 if key not in _key_to_collection:
229 raise exc.InvalidRequestError(
230 "No listeners found for event %s / %r / %s "
231 % (self.target, self.identifier, self.fn)
232 )
233 dispatch_reg = _key_to_collection.pop(key)
235 for collection_ref, listener_ref in dispatch_reg.items():
236 collection = collection_ref()
237 listener_fn = listener_ref()
238 if collection is not None and listener_fn is not None:
239 collection.remove(self.with_wrapper(listener_fn))
241 def contains(self):
242 """Return True if this event key is registered to listen.
243 """
244 return self._key in _key_to_collection
246 def base_listen(
247 self, propagate=False, insert=False, named=False, retval=None
248 ):
250 target, identifier = self.dispatch_target, self.identifier
252 dispatch_collection = getattr(target.dispatch, identifier)
254 if insert:
255 dispatch_collection.for_modify(target.dispatch).insert(
256 self, propagate
257 )
258 else:
259 dispatch_collection.for_modify(target.dispatch).append(
260 self, propagate
261 )
263 @property
264 def _listen_fn(self):
265 return self.fn_wrap or self.fn
267 def append_to_list(self, owner, list_):
268 if _stored_in_collection(self, owner):
269 list_.append(self._listen_fn)
270 return True
271 else:
272 return False
274 def remove_from_list(self, owner, list_):
275 _removed_from_collection(self, owner)
276 list_.remove(self._listen_fn)
278 def prepend_to_list(self, owner, list_):
279 if _stored_in_collection(self, owner):
280 list_.appendleft(self._listen_fn)
281 return True
282 else:
283 return False