dibbler/sqlalchemy/orm/identity.py

322 lines
8.9 KiB
Python
Raw Normal View History

2017-04-15 18:27:12 +02:00
# orm/identity.py
# Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
# <see AUTHORS file>
2010-05-07 19:33:49 +02:00
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
import weakref
2017-04-15 18:27:12 +02:00
from . import attributes
from .. import util
from .. import exc as sa_exc
from . import util as orm_util
2010-05-07 19:33:49 +02:00
2017-04-15 18:27:12 +02:00
class IdentityMap(object):
2010-05-07 19:33:49 +02:00
def __init__(self):
2017-04-15 18:27:12 +02:00
self._dict = {}
2010-05-07 19:33:49 +02:00
self._modified = set()
self._wr = weakref.ref(self)
2017-04-15 18:27:12 +02:00
def keys(self):
return self._dict.keys()
2010-05-07 19:33:49 +02:00
def replace(self, state):
raise NotImplementedError()
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def add(self, state):
raise NotImplementedError()
2017-04-15 18:27:12 +02:00
def _add_unpresent(self, state, key):
"""optional inlined form of add() which can assume item isn't present
in the map"""
self.add(state)
2010-05-07 19:33:49 +02:00
def update(self, dict):
raise NotImplementedError("IdentityMap uses add() to insert data")
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def clear(self):
raise NotImplementedError("IdentityMap uses remove() to remove data")
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def _manage_incoming_state(self, state):
state._instance_dict = self._wr
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
if state.modified:
2017-04-15 18:27:12 +02:00
self._modified.add(state)
2010-05-07 19:33:49 +02:00
def _manage_removed_state(self, state):
del state._instance_dict
2017-04-15 18:27:12 +02:00
if state.modified:
self._modified.discard(state)
2010-05-07 19:33:49 +02:00
def _dirty_states(self):
2017-04-15 18:27:12 +02:00
return self._modified
2010-05-07 19:33:49 +02:00
def check_modified(self):
2017-04-15 18:27:12 +02:00
"""return True if any InstanceStates present have been marked
as 'modified'.
"""
return bool(self._modified)
2010-05-07 19:33:49 +02:00
def has_key(self, key):
return key in self
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def popitem(self):
raise NotImplementedError("IdentityMap uses remove() to remove data")
def pop(self, key, *args):
raise NotImplementedError("IdentityMap uses remove() to remove data")
def setdefault(self, key, default=None):
raise NotImplementedError("IdentityMap uses add() to insert data")
2017-04-15 18:27:12 +02:00
def __len__(self):
return len(self._dict)
2010-05-07 19:33:49 +02:00
def copy(self):
raise NotImplementedError()
def __setitem__(self, key, value):
raise NotImplementedError("IdentityMap uses add() to insert data")
def __delitem__(self, key):
raise NotImplementedError("IdentityMap uses remove() to remove data")
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
class WeakInstanceDict(IdentityMap):
def __getitem__(self, key):
2017-04-15 18:27:12 +02:00
state = self._dict[key]
2010-05-07 19:33:49 +02:00
o = state.obj()
if o is None:
2017-04-15 18:27:12 +02:00
raise KeyError(key)
2010-05-07 19:33:49 +02:00
return o
def __contains__(self, key):
try:
2017-04-15 18:27:12 +02:00
if key in self._dict:
state = self._dict[key]
2010-05-07 19:33:49 +02:00
o = state.obj()
else:
return False
except KeyError:
return False
else:
return o is not None
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def contains_state(self, state):
2017-04-15 18:27:12 +02:00
return state.key in self._dict and self._dict[state.key] is state
2010-05-07 19:33:49 +02:00
def replace(self, state):
2017-04-15 18:27:12 +02:00
if state.key in self._dict:
existing = self._dict[state.key]
2010-05-07 19:33:49 +02:00
if existing is not state:
self._manage_removed_state(existing)
else:
return
2017-04-15 18:27:12 +02:00
self._dict[state.key] = state
2010-05-07 19:33:49 +02:00
self._manage_incoming_state(state)
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def add(self, state):
2017-04-15 18:27:12 +02:00
key = state.key
# inline of self.__contains__
if key in self._dict:
try:
existing_state = self._dict[key]
if existing_state is not state:
o = existing_state.obj()
if o is not None:
raise sa_exc.InvalidRequestError(
"Can't attach instance "
"%s; another instance with key %s is already "
"present in this session." % (
orm_util.state_str(state), state.key))
else:
return False
except KeyError:
pass
self._dict[key] = state
self._manage_incoming_state(state)
return True
def _add_unpresent(self, state, key):
# inlined form of add() called by loading.py
self._dict[key] = state
state._instance_dict = self._wr
2010-05-07 19:33:49 +02:00
def get(self, key, default=None):
2017-04-15 18:27:12 +02:00
if key not in self._dict:
2010-05-07 19:33:49 +02:00
return default
2017-04-15 18:27:12 +02:00
state = self._dict[key]
2010-05-07 19:33:49 +02:00
o = state.obj()
if o is None:
return default
return o
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
def items(self):
2017-04-15 18:27:12 +02:00
values = self.all_states()
result = []
for state in values:
2010-05-07 19:33:49 +02:00
value = state.obj()
if value is not None:
2017-04-15 18:27:12 +02:00
result.append((state.key, value))
return result
2010-05-07 19:33:49 +02:00
def values(self):
2017-04-15 18:27:12 +02:00
values = self.all_states()
result = []
for state in values:
value = state.obj()
if value is not None:
result.append(value)
return result
def __iter__(self):
return iter(self.keys())
if util.py2k:
def iteritems(self):
return iter(self.items())
def itervalues(self):
return iter(self.values())
2010-05-07 19:33:49 +02:00
def all_states(self):
2017-04-15 18:27:12 +02:00
if util.py2k:
return self._dict.values()
else:
return list(self._dict.values())
def _fast_discard(self, state):
self._dict.pop(state.key, None)
def discard(self, state):
st = self._dict.pop(state.key, None)
if st:
assert st is state
self._manage_removed_state(state)
def safe_discard(self, state):
if state.key in self._dict:
st = self._dict[state.key]
if st is state:
self._dict.pop(state.key, None)
self._manage_removed_state(state)
2010-05-07 19:33:49 +02:00
def prune(self):
return 0
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
class StrongInstanceDict(IdentityMap):
2017-04-15 18:27:12 +02:00
"""A 'strong-referencing' version of the identity map.
.. deprecated 1.1::
The strong
reference identity map is legacy. See the
recipe at :ref:`session_referencing_behavior` for
an event-based approach to maintaining strong identity
references.
"""
if util.py2k:
def itervalues(self):
return self._dict.itervalues()
def iteritems(self):
return self._dict.iteritems()
def __iter__(self):
return iter(self.dict_)
def __getitem__(self, key):
return self._dict[key]
def __contains__(self, key):
return key in self._dict
def get(self, key, default=None):
return self._dict.get(key, default)
def values(self):
return self._dict.values()
def items(self):
return self._dict.items()
2010-05-07 19:33:49 +02:00
def all_states(self):
2017-04-15 18:27:12 +02:00
return [attributes.instance_state(o) for o in self.values()]
2010-05-07 19:33:49 +02:00
def contains_state(self, state):
2017-04-15 18:27:12 +02:00
return (
state.key in self and
attributes.instance_state(self[state.key]) is state)
2010-05-07 19:33:49 +02:00
def replace(self, state):
2017-04-15 18:27:12 +02:00
if state.key in self._dict:
existing = self._dict[state.key]
2010-05-07 19:33:49 +02:00
existing = attributes.instance_state(existing)
if existing is not state:
self._manage_removed_state(existing)
else:
return
2017-04-15 18:27:12 +02:00
self._dict[state.key] = state.obj()
2010-05-07 19:33:49 +02:00
self._manage_incoming_state(state)
def add(self, state):
if state.key in self:
2017-04-15 18:27:12 +02:00
if attributes.instance_state(self._dict[state.key]) is not state:
raise sa_exc.InvalidRequestError(
"Can't attach instance "
"%s; another instance with key %s is already "
"present in this session." % (
orm_util.state_str(state), state.key))
return False
2010-05-07 19:33:49 +02:00
else:
2017-04-15 18:27:12 +02:00
self._dict[state.key] = state.obj()
2010-05-07 19:33:49 +02:00
self._manage_incoming_state(state)
2017-04-15 18:27:12 +02:00
return True
def _add_unpresent(self, state, key):
# inlined form of add() called by loading.py
self._dict[key] = state.obj()
state._instance_dict = self._wr
def _fast_discard(self, state):
self._dict.pop(state.key, None)
2010-05-07 19:33:49 +02:00
def discard(self, state):
2017-04-15 18:27:12 +02:00
obj = self._dict.pop(state.key, None)
if obj is not None:
2010-05-07 19:33:49 +02:00
self._manage_removed_state(state)
2017-04-15 18:27:12 +02:00
st = attributes.instance_state(obj)
assert st is state
def safe_discard(self, state):
if state.key in self._dict:
obj = self._dict[state.key]
st = attributes.instance_state(obj)
if st is state:
self._dict.pop(state.key, None)
self._manage_removed_state(state)
2010-05-07 19:33:49 +02:00
def prune(self):
"""prune unreferenced, non-dirty states."""
2017-04-15 18:27:12 +02:00
2010-05-07 19:33:49 +02:00
ref_count = len(self)
dirty = [s.obj() for s in self.all_states() if s.modified]
# work around http://bugs.python.org/issue6149
keepers = weakref.WeakValueDictionary()
keepers.update(self)
2017-04-15 18:27:12 +02:00
self._dict.clear()
self._dict.update(keepers)
2010-05-07 19:33:49 +02:00
self.modified = bool(dirty)
return ref_count - len(self)