registry.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. # event/registry.py
  2. # Copyright (C) 2005-2017 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
  7. """Provides managed registration services on behalf of :func:`.listen`
  8. arguments.
  9. By "managed registration", we mean that event listening functions and
  10. other objects can be added to various collections in such a way that their
  11. membership in all those collections can be revoked at once, based on
  12. an equivalent :class:`._EventKey`.
  13. """
  14. from __future__ import absolute_import
  15. import weakref
  16. import collections
  17. import types
  18. from .. import exc, util
  19. _key_to_collection = collections.defaultdict(dict)
  20. """
  21. Given an original listen() argument, can locate all
  22. listener collections and the listener fn contained
  23. (target, identifier, fn) -> {
  24. ref(listenercollection) -> ref(listener_fn)
  25. ref(listenercollection) -> ref(listener_fn)
  26. ref(listenercollection) -> ref(listener_fn)
  27. }
  28. """
  29. _collection_to_key = collections.defaultdict(dict)
  30. """
  31. Given a _ListenerCollection or _ClsLevelListener, can locate
  32. all the original listen() arguments and the listener fn contained
  33. ref(listenercollection) -> {
  34. ref(listener_fn) -> (target, identifier, fn),
  35. ref(listener_fn) -> (target, identifier, fn),
  36. ref(listener_fn) -> (target, identifier, fn),
  37. }
  38. """
  39. def _collection_gced(ref):
  40. # defaultdict, so can't get a KeyError
  41. if not _collection_to_key or ref not in _collection_to_key:
  42. return
  43. listener_to_key = _collection_to_key.pop(ref)
  44. for key in listener_to_key.values():
  45. if key in _key_to_collection:
  46. # defaultdict, so can't get a KeyError
  47. dispatch_reg = _key_to_collection[key]
  48. dispatch_reg.pop(ref)
  49. if not dispatch_reg:
  50. _key_to_collection.pop(key)
  51. def _stored_in_collection(event_key, owner):
  52. key = event_key._key
  53. dispatch_reg = _key_to_collection[key]
  54. owner_ref = owner.ref
  55. listen_ref = weakref.ref(event_key._listen_fn)
  56. if owner_ref in dispatch_reg:
  57. return False
  58. dispatch_reg[owner_ref] = listen_ref
  59. listener_to_key = _collection_to_key[owner_ref]
  60. listener_to_key[listen_ref] = key
  61. return True
  62. def _removed_from_collection(event_key, owner):
  63. key = event_key._key
  64. dispatch_reg = _key_to_collection[key]
  65. listen_ref = weakref.ref(event_key._listen_fn)
  66. owner_ref = owner.ref
  67. dispatch_reg.pop(owner_ref, None)
  68. if not dispatch_reg:
  69. del _key_to_collection[key]
  70. if owner_ref in _collection_to_key:
  71. listener_to_key = _collection_to_key[owner_ref]
  72. listener_to_key.pop(listen_ref)
  73. def _stored_in_collection_multi(newowner, oldowner, elements):
  74. if not elements:
  75. return
  76. oldowner = oldowner.ref
  77. newowner = newowner.ref
  78. old_listener_to_key = _collection_to_key[oldowner]
  79. new_listener_to_key = _collection_to_key[newowner]
  80. for listen_fn in elements:
  81. listen_ref = weakref.ref(listen_fn)
  82. key = old_listener_to_key[listen_ref]
  83. dispatch_reg = _key_to_collection[key]
  84. if newowner in dispatch_reg:
  85. assert dispatch_reg[newowner] == listen_ref
  86. else:
  87. dispatch_reg[newowner] = listen_ref
  88. new_listener_to_key[listen_ref] = key
  89. def _clear(owner, elements):
  90. if not elements:
  91. return
  92. owner = owner.ref
  93. listener_to_key = _collection_to_key[owner]
  94. for listen_fn in elements:
  95. listen_ref = weakref.ref(listen_fn)
  96. key = listener_to_key[listen_ref]
  97. dispatch_reg = _key_to_collection[key]
  98. dispatch_reg.pop(owner, None)
  99. if not dispatch_reg:
  100. del _key_to_collection[key]
  101. class _EventKey(object):
  102. """Represent :func:`.listen` arguments.
  103. """
  104. __slots__ = (
  105. 'target', 'identifier', 'fn', 'fn_key', 'fn_wrap', 'dispatch_target'
  106. )
  107. def __init__(self, target, identifier,
  108. fn, dispatch_target, _fn_wrap=None):
  109. self.target = target
  110. self.identifier = identifier
  111. self.fn = fn
  112. if isinstance(fn, types.MethodType):
  113. self.fn_key = id(fn.__func__), id(fn.__self__)
  114. else:
  115. self.fn_key = id(fn)
  116. self.fn_wrap = _fn_wrap
  117. self.dispatch_target = dispatch_target
  118. @property
  119. def _key(self):
  120. return (id(self.target), self.identifier, self.fn_key)
  121. def with_wrapper(self, fn_wrap):
  122. if fn_wrap is self._listen_fn:
  123. return self
  124. else:
  125. return _EventKey(
  126. self.target,
  127. self.identifier,
  128. self.fn,
  129. self.dispatch_target,
  130. _fn_wrap=fn_wrap
  131. )
  132. def with_dispatch_target(self, dispatch_target):
  133. if dispatch_target is self.dispatch_target:
  134. return self
  135. else:
  136. return _EventKey(
  137. self.target,
  138. self.identifier,
  139. self.fn,
  140. dispatch_target,
  141. _fn_wrap=self.fn_wrap
  142. )
  143. def listen(self, *args, **kw):
  144. once = kw.pop("once", False)
  145. named = kw.pop("named", False)
  146. target, identifier, fn = \
  147. self.dispatch_target, self.identifier, self._listen_fn
  148. dispatch_collection = getattr(target.dispatch, identifier)
  149. adjusted_fn = dispatch_collection._adjust_fn_spec(fn, named)
  150. self = self.with_wrapper(adjusted_fn)
  151. if once:
  152. self.with_wrapper(
  153. util.only_once(self._listen_fn)).listen(*args, **kw)
  154. else:
  155. self.dispatch_target.dispatch._listen(self, *args, **kw)
  156. def remove(self):
  157. key = self._key
  158. if key not in _key_to_collection:
  159. raise exc.InvalidRequestError(
  160. "No listeners found for event %s / %r / %s " %
  161. (self.target, self.identifier, self.fn)
  162. )
  163. dispatch_reg = _key_to_collection.pop(key)
  164. for collection_ref, listener_ref in dispatch_reg.items():
  165. collection = collection_ref()
  166. listener_fn = listener_ref()
  167. if collection is not None and listener_fn is not None:
  168. collection.remove(self.with_wrapper(listener_fn))
  169. def contains(self):
  170. """Return True if this event key is registered to listen.
  171. """
  172. return self._key in _key_to_collection
  173. def base_listen(self, propagate=False, insert=False,
  174. named=False):
  175. target, identifier, fn = \
  176. self.dispatch_target, self.identifier, self._listen_fn
  177. dispatch_collection = getattr(target.dispatch, identifier)
  178. if insert:
  179. dispatch_collection.\
  180. for_modify(target.dispatch).insert(self, propagate)
  181. else:
  182. dispatch_collection.\
  183. for_modify(target.dispatch).append(self, propagate)
  184. @property
  185. def _listen_fn(self):
  186. return self.fn_wrap or self.fn
  187. def append_to_list(self, owner, list_):
  188. if _stored_in_collection(self, owner):
  189. list_.append(self._listen_fn)
  190. return True
  191. else:
  192. return False
  193. def remove_from_list(self, owner, list_):
  194. _removed_from_collection(self, owner)
  195. list_.remove(self._listen_fn)
  196. def prepend_to_list(self, owner, list_):
  197. if _stored_in_collection(self, owner):
  198. list_.appendleft(self._listen_fn)
  199. return True
  200. else:
  201. return False