Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/orm/identity.py : 43%

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# orm/identity.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
8import weakref
10from . import attributes
11from . import util as orm_util
12from .. import exc as sa_exc
13from .. import util
16class IdentityMap(object):
17 def __init__(self):
18 self._dict = {}
19 self._modified = set()
20 self._wr = weakref.ref(self)
22 def keys(self):
23 return self._dict.keys()
25 def replace(self, state):
26 raise NotImplementedError()
28 def add(self, state):
29 raise NotImplementedError()
31 def _add_unpresent(self, state, key):
32 """optional inlined form of add() which can assume item isn't present
33 in the map"""
34 self.add(state)
36 def update(self, dict_):
37 raise NotImplementedError("IdentityMap uses add() to insert data")
39 def clear(self):
40 raise NotImplementedError("IdentityMap uses remove() to remove data")
42 def _manage_incoming_state(self, state):
43 state._instance_dict = self._wr
45 if state.modified:
46 self._modified.add(state)
48 def _manage_removed_state(self, state):
49 del state._instance_dict
50 if state.modified:
51 self._modified.discard(state)
53 def _dirty_states(self):
54 return self._modified
56 def check_modified(self):
57 """return True if any InstanceStates present have been marked
58 as 'modified'.
60 """
61 return bool(self._modified)
63 def has_key(self, key):
64 return key in self
66 def popitem(self):
67 raise NotImplementedError("IdentityMap uses remove() to remove data")
69 def pop(self, key, *args):
70 raise NotImplementedError("IdentityMap uses remove() to remove data")
72 def setdefault(self, key, default=None):
73 raise NotImplementedError("IdentityMap uses add() to insert data")
75 def __len__(self):
76 return len(self._dict)
78 def copy(self):
79 raise NotImplementedError()
81 def __setitem__(self, key, value):
82 raise NotImplementedError("IdentityMap uses add() to insert data")
84 def __delitem__(self, key):
85 raise NotImplementedError("IdentityMap uses remove() to remove data")
88class WeakInstanceDict(IdentityMap):
89 def __getitem__(self, key):
90 state = self._dict[key]
91 o = state.obj()
92 if o is None:
93 raise KeyError(key)
94 return o
96 def __contains__(self, key):
97 try:
98 if key in self._dict:
99 state = self._dict[key]
100 o = state.obj()
101 else:
102 return False
103 except KeyError:
104 return False
105 else:
106 return o is not None
108 def contains_state(self, state):
109 if state.key in self._dict:
110 try:
111 return self._dict[state.key] is state
112 except KeyError:
113 return False
114 else:
115 return False
117 def replace(self, state):
118 if state.key in self._dict:
119 try:
120 existing = self._dict[state.key]
121 except KeyError:
122 # catch gc removed the key after we just checked for it
123 pass
124 else:
125 if existing is not state:
126 self._manage_removed_state(existing)
127 else:
128 return None
129 else:
130 existing = None
132 self._dict[state.key] = state
133 self._manage_incoming_state(state)
134 return existing
136 def add(self, state):
137 key = state.key
138 # inline of self.__contains__
139 if key in self._dict:
140 try:
141 existing_state = self._dict[key]
142 except KeyError:
143 # catch gc removed the key after we just checked for it
144 pass
145 else:
146 if existing_state is not state:
147 o = existing_state.obj()
148 if o is not None:
149 raise sa_exc.InvalidRequestError(
150 "Can't attach instance "
151 "%s; another instance with key %s is already "
152 "present in this session."
153 % (orm_util.state_str(state), state.key)
154 )
155 else:
156 return False
157 self._dict[key] = state
158 self._manage_incoming_state(state)
159 return True
161 def _add_unpresent(self, state, key):
162 # inlined form of add() called by loading.py
163 self._dict[key] = state
164 state._instance_dict = self._wr
166 def get(self, key, default=None):
167 if key not in self._dict:
168 return default
169 try:
170 state = self._dict[key]
171 except KeyError:
172 # catch gc removed the key after we just checked for it
173 return default
174 else:
175 o = state.obj()
176 if o is None:
177 return default
178 return o
180 def items(self):
181 values = self.all_states()
182 result = []
183 for state in values:
184 value = state.obj()
185 if value is not None:
186 result.append((state.key, value))
187 return result
189 def values(self):
190 values = self.all_states()
191 result = []
192 for state in values:
193 value = state.obj()
194 if value is not None:
195 result.append(value)
197 return result
199 def __iter__(self):
200 return iter(self.keys())
202 if util.py2k:
204 def iteritems(self):
205 return iter(self.items())
207 def itervalues(self):
208 return iter(self.values())
210 def all_states(self):
211 if util.py2k:
212 return self._dict.values()
213 else:
214 return list(self._dict.values())
216 def _fast_discard(self, state):
217 # used by InstanceState for state being
218 # GC'ed, inlines _managed_removed_state
219 try:
220 st = self._dict[state.key]
221 except KeyError:
222 # catch gc removed the key after we just checked for it
223 pass
224 else:
225 if st is state:
226 self._dict.pop(state.key, None)
228 def discard(self, state):
229 self.safe_discard(state)
231 def safe_discard(self, state):
232 if state.key in self._dict:
233 try:
234 st = self._dict[state.key]
235 except KeyError:
236 # catch gc removed the key after we just checked for it
237 pass
238 else:
239 if st is state:
240 self._dict.pop(state.key, None)
241 self._manage_removed_state(state)
243 def prune(self):
244 return 0
247class StrongInstanceDict(IdentityMap):
248 """A 'strong-referencing' version of the identity map.
250 .. deprecated 1.1::
251 The strong
252 reference identity map is legacy. See the
253 recipe at :ref:`session_referencing_behavior` for
254 an event-based approach to maintaining strong identity
255 references.
258 """
260 if util.py2k:
262 def itervalues(self):
263 return self._dict.itervalues()
265 def iteritems(self):
266 return self._dict.iteritems()
268 def __iter__(self):
269 return iter(self.dict_)
271 def __getitem__(self, key):
272 return self._dict[key]
274 def __contains__(self, key):
275 return key in self._dict
277 def get(self, key, default=None):
278 return self._dict.get(key, default)
280 def values(self):
281 return self._dict.values()
283 def items(self):
284 return self._dict.items()
286 def all_states(self):
287 return [attributes.instance_state(o) for o in self.values()]
289 def contains_state(self, state):
290 return (
291 state.key in self
292 and attributes.instance_state(self[state.key]) is state
293 )
295 def replace(self, state):
296 if state.key in self._dict:
297 existing = self._dict[state.key]
298 existing = attributes.instance_state(existing)
299 if existing is not state:
300 self._manage_removed_state(existing)
301 else:
302 return
303 else:
304 existing = None
306 self._dict[state.key] = state.obj()
307 self._manage_incoming_state(state)
308 return existing
310 def add(self, state):
311 if state.key in self:
312 if attributes.instance_state(self._dict[state.key]) is not state:
313 raise sa_exc.InvalidRequestError(
314 "Can't attach instance "
315 "%s; another instance with key %s is already "
316 "present in this session."
317 % (orm_util.state_str(state), state.key)
318 )
319 return False
320 else:
321 self._dict[state.key] = state.obj()
322 self._manage_incoming_state(state)
323 return True
325 def _add_unpresent(self, state, key):
326 # inlined form of add() called by loading.py
327 self._dict[key] = state.obj()
328 state._instance_dict = self._wr
330 def _fast_discard(self, state):
331 # used by InstanceState for state being
332 # GC'ed, inlines _managed_removed_state
333 try:
334 obj = self._dict[state.key]
335 except KeyError:
336 # catch gc removed the key after we just checked for it
337 pass
338 else:
339 if attributes.instance_state(obj) is state:
340 self._dict.pop(state.key, None)
342 def discard(self, state):
343 self.safe_discard(state)
345 def safe_discard(self, state):
346 if state.key in self._dict:
347 obj = self._dict[state.key]
348 st = attributes.instance_state(obj)
349 if st is state:
350 self._dict.pop(state.key, None)
351 self._manage_removed_state(state)
353 def prune(self):
354 """prune unreferenced, non-dirty states."""
356 ref_count = len(self)
357 dirty = [s.obj() for s in self.all_states() if s.modified]
359 # work around http://bugs.python.org/issue6149
360 keepers = weakref.WeakValueDictionary()
361 keepers.update(self)
363 self._dict.clear()
364 self._dict.update(keepers)
365 self.modified = bool(dirty)
366 return ref_count - len(self)