You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					255 lines
				
				7.1 KiB
			
		
		
			
		
	
	
					255 lines
				
				7.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# orm/identity.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import weakref
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from . import util as orm_util
							 | 
						||
| 
								 | 
							
								from .. import exc as sa_exc
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class IdentityMap(object):
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        self._dict = {}
							 | 
						||
| 
								 | 
							
								        self._modified = set()
							 | 
						||
| 
								 | 
							
								        self._wr = weakref.ref(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _kill(self):
							 | 
						||
| 
								 | 
							
								        self._add_unpresent = _killed
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def keys(self):
							 | 
						||
| 
								 | 
							
								        return self._dict.keys()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def replace(self, state):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def add(self, state):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _add_unpresent(self, state, key):
							 | 
						||
| 
								 | 
							
								        """optional inlined form of add() which can assume item isn't present
							 | 
						||
| 
								 | 
							
								        in the map"""
							 | 
						||
| 
								 | 
							
								        self.add(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def update(self, dict_):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError("IdentityMap uses add() to insert data")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def clear(self):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError("IdentityMap uses remove() to remove data")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _manage_incoming_state(self, state):
							 | 
						||
| 
								 | 
							
								        state._instance_dict = self._wr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if state.modified:
							 | 
						||
| 
								 | 
							
								            self._modified.add(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _manage_removed_state(self, state):
							 | 
						||
| 
								 | 
							
								        del state._instance_dict
							 | 
						||
| 
								 | 
							
								        if state.modified:
							 | 
						||
| 
								 | 
							
								            self._modified.discard(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _dirty_states(self):
							 | 
						||
| 
								 | 
							
								        return self._modified
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def check_modified(self):
							 | 
						||
| 
								 | 
							
								        """return True if any InstanceStates present have been marked
							 | 
						||
| 
								 | 
							
								        as 'modified'.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return bool(self._modified)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_key(self, key):
							 | 
						||
| 
								 | 
							
								        return key in self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __len__(self):
							 | 
						||
| 
								 | 
							
								        return len(self._dict)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class WeakInstanceDict(IdentityMap):
							 | 
						||
| 
								 | 
							
								    def __getitem__(self, key):
							 | 
						||
| 
								 | 
							
								        state = self._dict[key]
							 | 
						||
| 
								 | 
							
								        o = state.obj()
							 | 
						||
| 
								 | 
							
								        if o is None:
							 | 
						||
| 
								 | 
							
								            raise KeyError(key)
							 | 
						||
| 
								 | 
							
								        return o
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __contains__(self, key):
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            if key in self._dict:
							 | 
						||
| 
								 | 
							
								                state = self._dict[key]
							 | 
						||
| 
								 | 
							
								                o = state.obj()
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return o is not None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def contains_state(self, state):
							 | 
						||
| 
								 | 
							
								        if state.key in self._dict:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                return self._dict[state.key] is state
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def replace(self, state):
							 | 
						||
| 
								 | 
							
								        if state.key in self._dict:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                existing = self._dict[state.key]
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                # catch gc removed the key after we just checked for it
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if existing is not state:
							 | 
						||
| 
								 | 
							
								                    self._manage_removed_state(existing)
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            existing = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._dict[state.key] = state
							 | 
						||
| 
								 | 
							
								        self._manage_incoming_state(state)
							 | 
						||
| 
								 | 
							
								        return existing
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def add(self, state):
							 | 
						||
| 
								 | 
							
								        key = state.key
							 | 
						||
| 
								 | 
							
								        # inline of self.__contains__
							 | 
						||
| 
								 | 
							
								        if key in self._dict:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                existing_state = self._dict[key]
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                # catch gc removed the key after we just checked for it
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                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
							 | 
						||
| 
								 | 
							
								        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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get(self, key, default=None):
							 | 
						||
| 
								 | 
							
								        if key not in self._dict:
							 | 
						||
| 
								 | 
							
								            return default
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            state = self._dict[key]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            # catch gc removed the key after we just checked for it
							 | 
						||
| 
								 | 
							
								            return default
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            o = state.obj()
							 | 
						||
| 
								 | 
							
								            if o is None:
							 | 
						||
| 
								 | 
							
								                return default
							 | 
						||
| 
								 | 
							
								            return o
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def items(self):
							 | 
						||
| 
								 | 
							
								        values = self.all_states()
							 | 
						||
| 
								 | 
							
								        result = []
							 | 
						||
| 
								 | 
							
								        for state in values:
							 | 
						||
| 
								 | 
							
								            value = state.obj()
							 | 
						||
| 
								 | 
							
								            if value is not None:
							 | 
						||
| 
								 | 
							
								                result.append((state.key, value))
							 | 
						||
| 
								 | 
							
								        return result
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def values(self):
							 | 
						||
| 
								 | 
							
								        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())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def all_states(self):
							 | 
						||
| 
								 | 
							
								        if util.py2k:
							 | 
						||
| 
								 | 
							
								            return self._dict.values()
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return list(self._dict.values())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _fast_discard(self, state):
							 | 
						||
| 
								 | 
							
								        # used by InstanceState for state being
							 | 
						||
| 
								 | 
							
								        # GC'ed, inlines _managed_removed_state
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            st = self._dict[state.key]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            # catch gc removed the key after we just checked for it
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if st is state:
							 | 
						||
| 
								 | 
							
								                self._dict.pop(state.key, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def discard(self, state):
							 | 
						||
| 
								 | 
							
								        self.safe_discard(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def safe_discard(self, state):
							 | 
						||
| 
								 | 
							
								        if state.key in self._dict:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                st = self._dict[state.key]
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                # catch gc removed the key after we just checked for it
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if st is state:
							 | 
						||
| 
								 | 
							
								                    self._dict.pop(state.key, None)
							 | 
						||
| 
								 | 
							
								                    self._manage_removed_state(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _killed(state, key):
							 | 
						||
| 
								 | 
							
								    # external function to avoid creating cycles when assigned to
							 | 
						||
| 
								 | 
							
								    # the IdentityMap
							 | 
						||
| 
								 | 
							
								    raise sa_exc.InvalidRequestError(
							 | 
						||
| 
								 | 
							
								        "Object %s cannot be converted to 'persistent' state, as this "
							 | 
						||
| 
								 | 
							
								        "identity map is no longer valid.  Has the owning Session "
							 | 
						||
| 
								 | 
							
								        "been closed?" % orm_util.state_str(state),
							 | 
						||
| 
								 | 
							
								        code="lkrp",
							 | 
						||
| 
								 | 
							
								    )
							 |