identity.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. # orm/identity.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. import weakref
  8. from . import attributes
  9. from .. import util
  10. from .. import exc as sa_exc
  11. from . import util as orm_util
  12. class IdentityMap(object):
  13. def __init__(self):
  14. self._dict = {}
  15. self._modified = set()
  16. self._wr = weakref.ref(self)
  17. def keys(self):
  18. return self._dict.keys()
  19. def replace(self, state):
  20. raise NotImplementedError()
  21. def add(self, state):
  22. raise NotImplementedError()
  23. def _add_unpresent(self, state, key):
  24. """optional inlined form of add() which can assume item isn't present
  25. in the map"""
  26. self.add(state)
  27. def update(self, dict):
  28. raise NotImplementedError("IdentityMap uses add() to insert data")
  29. def clear(self):
  30. raise NotImplementedError("IdentityMap uses remove() to remove data")
  31. def _manage_incoming_state(self, state):
  32. state._instance_dict = self._wr
  33. if state.modified:
  34. self._modified.add(state)
  35. def _manage_removed_state(self, state):
  36. del state._instance_dict
  37. if state.modified:
  38. self._modified.discard(state)
  39. def _dirty_states(self):
  40. return self._modified
  41. def check_modified(self):
  42. """return True if any InstanceStates present have been marked
  43. as 'modified'.
  44. """
  45. return bool(self._modified)
  46. def has_key(self, key):
  47. return key in self
  48. def popitem(self):
  49. raise NotImplementedError("IdentityMap uses remove() to remove data")
  50. def pop(self, key, *args):
  51. raise NotImplementedError("IdentityMap uses remove() to remove data")
  52. def setdefault(self, key, default=None):
  53. raise NotImplementedError("IdentityMap uses add() to insert data")
  54. def __len__(self):
  55. return len(self._dict)
  56. def copy(self):
  57. raise NotImplementedError()
  58. def __setitem__(self, key, value):
  59. raise NotImplementedError("IdentityMap uses add() to insert data")
  60. def __delitem__(self, key):
  61. raise NotImplementedError("IdentityMap uses remove() to remove data")
  62. class WeakInstanceDict(IdentityMap):
  63. def __getitem__(self, key):
  64. state = self._dict[key]
  65. o = state.obj()
  66. if o is None:
  67. raise KeyError(key)
  68. return o
  69. def __contains__(self, key):
  70. try:
  71. if key in self._dict:
  72. state = self._dict[key]
  73. o = state.obj()
  74. else:
  75. return False
  76. except KeyError:
  77. return False
  78. else:
  79. return o is not None
  80. def contains_state(self, state):
  81. return state.key in self._dict and self._dict[state.key] is state
  82. def replace(self, state):
  83. if state.key in self._dict:
  84. existing = self._dict[state.key]
  85. if existing is not state:
  86. self._manage_removed_state(existing)
  87. else:
  88. return
  89. self._dict[state.key] = state
  90. self._manage_incoming_state(state)
  91. def add(self, state):
  92. key = state.key
  93. # inline of self.__contains__
  94. if key in self._dict:
  95. try:
  96. existing_state = self._dict[key]
  97. if existing_state is not state:
  98. o = existing_state.obj()
  99. if o is not None:
  100. raise sa_exc.InvalidRequestError(
  101. "Can't attach instance "
  102. "%s; another instance with key %s is already "
  103. "present in this session." % (
  104. orm_util.state_str(state), state.key))
  105. else:
  106. return False
  107. except KeyError:
  108. pass
  109. self._dict[key] = state
  110. self._manage_incoming_state(state)
  111. return True
  112. def _add_unpresent(self, state, key):
  113. # inlined form of add() called by loading.py
  114. self._dict[key] = state
  115. state._instance_dict = self._wr
  116. def get(self, key, default=None):
  117. if key not in self._dict:
  118. return default
  119. state = self._dict[key]
  120. o = state.obj()
  121. if o is None:
  122. return default
  123. return o
  124. def items(self):
  125. values = self.all_states()
  126. result = []
  127. for state in values:
  128. value = state.obj()
  129. if value is not None:
  130. result.append((state.key, value))
  131. return result
  132. def values(self):
  133. values = self.all_states()
  134. result = []
  135. for state in values:
  136. value = state.obj()
  137. if value is not None:
  138. result.append(value)
  139. return result
  140. def __iter__(self):
  141. return iter(self.keys())
  142. if util.py2k:
  143. def iteritems(self):
  144. return iter(self.items())
  145. def itervalues(self):
  146. return iter(self.values())
  147. def all_states(self):
  148. if util.py2k:
  149. return self._dict.values()
  150. else:
  151. return list(self._dict.values())
  152. def _fast_discard(self, state):
  153. self._dict.pop(state.key, None)
  154. def discard(self, state):
  155. st = self._dict.pop(state.key, None)
  156. if st:
  157. assert st is state
  158. self._manage_removed_state(state)
  159. def safe_discard(self, state):
  160. if state.key in self._dict:
  161. st = self._dict[state.key]
  162. if st is state:
  163. self._dict.pop(state.key, None)
  164. self._manage_removed_state(state)
  165. def prune(self):
  166. return 0
  167. class StrongInstanceDict(IdentityMap):
  168. """A 'strong-referencing' version of the identity map.
  169. .. deprecated 1.1::
  170. The strong
  171. reference identity map is legacy. See the
  172. recipe at :ref:`session_referencing_behavior` for
  173. an event-based approach to maintaining strong identity
  174. references.
  175. """
  176. if util.py2k:
  177. def itervalues(self):
  178. return self._dict.itervalues()
  179. def iteritems(self):
  180. return self._dict.iteritems()
  181. def __iter__(self):
  182. return iter(self.dict_)
  183. def __getitem__(self, key):
  184. return self._dict[key]
  185. def __contains__(self, key):
  186. return key in self._dict
  187. def get(self, key, default=None):
  188. return self._dict.get(key, default)
  189. def values(self):
  190. return self._dict.values()
  191. def items(self):
  192. return self._dict.items()
  193. def all_states(self):
  194. return [attributes.instance_state(o) for o in self.values()]
  195. def contains_state(self, state):
  196. return (
  197. state.key in self and
  198. attributes.instance_state(self[state.key]) is state)
  199. def replace(self, state):
  200. if state.key in self._dict:
  201. existing = self._dict[state.key]
  202. existing = attributes.instance_state(existing)
  203. if existing is not state:
  204. self._manage_removed_state(existing)
  205. else:
  206. return
  207. self._dict[state.key] = state.obj()
  208. self._manage_incoming_state(state)
  209. def add(self, state):
  210. if state.key in self:
  211. if attributes.instance_state(self._dict[state.key]) is not state:
  212. raise sa_exc.InvalidRequestError(
  213. "Can't attach instance "
  214. "%s; another instance with key %s is already "
  215. "present in this session." % (
  216. orm_util.state_str(state), state.key))
  217. return False
  218. else:
  219. self._dict[state.key] = state.obj()
  220. self._manage_incoming_state(state)
  221. return True
  222. def _add_unpresent(self, state, key):
  223. # inlined form of add() called by loading.py
  224. self._dict[key] = state.obj()
  225. state._instance_dict = self._wr
  226. def _fast_discard(self, state):
  227. self._dict.pop(state.key, None)
  228. def discard(self, state):
  229. obj = self._dict.pop(state.key, None)
  230. if obj is not None:
  231. self._manage_removed_state(state)
  232. st = attributes.instance_state(obj)
  233. assert st is state
  234. def safe_discard(self, state):
  235. if state.key in self._dict:
  236. obj = self._dict[state.key]
  237. st = attributes.instance_state(obj)
  238. if st is state:
  239. self._dict.pop(state.key, None)
  240. self._manage_removed_state(state)
  241. def prune(self):
  242. """prune unreferenced, non-dirty states."""
  243. ref_count = len(self)
  244. dirty = [s.obj() for s in self.all_states() if s.modified]
  245. # work around http://bugs.python.org/issue6149
  246. keepers = weakref.WeakValueDictionary()
  247. keepers.update(self)
  248. self._dict.clear()
  249. self._dict.update(keepers)
  250. self.modified = bool(dirty)
  251. return ref_count - len(self)