about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py')
-rw-r--r--.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py1143
1 files changed, 1143 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py b/.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py
new file mode 100644
index 00000000..d4bbf920
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py
@@ -0,0 +1,1143 @@
+# orm/state.py
+# Copyright (C) 2005-2025 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
+
+"""Defines instrumentation of instances.
+
+This module is usually not directly visible to user applications, but
+defines a large part of the ORM's interactivity.
+
+"""
+
+from __future__ import annotations
+
+from typing import Any
+from typing import Callable
+from typing import Dict
+from typing import Generic
+from typing import Iterable
+from typing import Optional
+from typing import Set
+from typing import Tuple
+from typing import TYPE_CHECKING
+from typing import Union
+import weakref
+
+from . import base
+from . import exc as orm_exc
+from . import interfaces
+from ._typing import _O
+from ._typing import is_collection_impl
+from .base import ATTR_WAS_SET
+from .base import INIT_OK
+from .base import LoaderCallableStatus
+from .base import NEVER_SET
+from .base import NO_VALUE
+from .base import PASSIVE_NO_INITIALIZE
+from .base import PASSIVE_NO_RESULT
+from .base import PASSIVE_OFF
+from .base import SQL_OK
+from .path_registry import PathRegistry
+from .. import exc as sa_exc
+from .. import inspection
+from .. import util
+from ..util.typing import Literal
+from ..util.typing import Protocol
+
+if TYPE_CHECKING:
+    from ._typing import _IdentityKeyType
+    from ._typing import _InstanceDict
+    from ._typing import _LoaderCallable
+    from .attributes import AttributeImpl
+    from .attributes import History
+    from .base import PassiveFlag
+    from .collections import _AdaptedCollectionProtocol
+    from .identity import IdentityMap
+    from .instrumentation import ClassManager
+    from .interfaces import ORMOption
+    from .mapper import Mapper
+    from .session import Session
+    from ..engine import Row
+    from ..ext.asyncio.session import async_session as _async_provider
+    from ..ext.asyncio.session import AsyncSession
+
+if TYPE_CHECKING:
+    _sessions: weakref.WeakValueDictionary[int, Session]
+else:
+    # late-populated by session.py
+    _sessions = None
+
+
+if not TYPE_CHECKING:
+    # optionally late-provided by sqlalchemy.ext.asyncio.session
+
+    _async_provider = None  # noqa
+
+
+class _InstanceDictProto(Protocol):
+    def __call__(self) -> Optional[IdentityMap]: ...
+
+
+class _InstallLoaderCallableProto(Protocol[_O]):
+    """used at result loading time to install a _LoaderCallable callable
+    upon a specific InstanceState, which will be used to populate an
+    attribute when that attribute is accessed.
+
+    Concrete examples are per-instance deferred column loaders and
+    relationship lazy loaders.
+
+    """
+
+    def __call__(
+        self, state: InstanceState[_O], dict_: _InstanceDict, row: Row[Any]
+    ) -> None: ...
+
+
+@inspection._self_inspects
+class InstanceState(interfaces.InspectionAttrInfo, Generic[_O]):
+    """Tracks state information at the instance level.
+
+    The :class:`.InstanceState` is a key object used by the
+    SQLAlchemy ORM in order to track the state of an object;
+    it is created the moment an object is instantiated, typically
+    as a result of :term:`instrumentation` which SQLAlchemy applies
+    to the ``__init__()`` method of the class.
+
+    :class:`.InstanceState` is also a semi-public object,
+    available for runtime inspection as to the state of a
+    mapped instance, including information such as its current
+    status within a particular :class:`.Session` and details
+    about data on individual attributes.  The public API
+    in order to acquire a :class:`.InstanceState` object
+    is to use the :func:`_sa.inspect` system::
+
+        >>> from sqlalchemy import inspect
+        >>> insp = inspect(some_mapped_object)
+        >>> insp.attrs.nickname.history
+        History(added=['new nickname'], unchanged=(), deleted=['nickname'])
+
+    .. seealso::
+
+        :ref:`orm_mapper_inspection_instancestate`
+
+    """
+
+    __slots__ = (
+        "__dict__",
+        "__weakref__",
+        "class_",
+        "manager",
+        "obj",
+        "committed_state",
+        "expired_attributes",
+    )
+
+    manager: ClassManager[_O]
+    session_id: Optional[int] = None
+    key: Optional[_IdentityKeyType[_O]] = None
+    runid: Optional[int] = None
+    load_options: Tuple[ORMOption, ...] = ()
+    load_path: PathRegistry = PathRegistry.root
+    insert_order: Optional[int] = None
+    _strong_obj: Optional[object] = None
+    obj: weakref.ref[_O]
+
+    committed_state: Dict[str, Any]
+
+    modified: bool = False
+    """When ``True`` the object was modified."""
+    expired: bool = False
+    """When ``True`` the object is :term:`expired`.
+
+    .. seealso::
+
+        :ref:`session_expire`
+    """
+    _deleted: bool = False
+    _load_pending: bool = False
+    _orphaned_outside_of_session: bool = False
+    is_instance: bool = True
+    identity_token: object = None
+    _last_known_values: Optional[Dict[str, Any]] = None
+
+    _instance_dict: _InstanceDictProto
+    """A weak reference, or in the default case a plain callable, that
+    returns a reference to the current :class:`.IdentityMap`, if any.
+
+    """
+    if not TYPE_CHECKING:
+
+        def _instance_dict(self):
+            """default 'weak reference' for _instance_dict"""
+            return None
+
+    expired_attributes: Set[str]
+    """The set of keys which are 'expired' to be loaded by
+    the manager's deferred scalar loader, assuming no pending
+    changes.
+
+    See also the ``unmodified`` collection which is intersected
+    against this set when a refresh operation occurs.
+    """
+
+    callables: Dict[str, Callable[[InstanceState[_O], PassiveFlag], Any]]
+    """A namespace where a per-state loader callable can be associated.
+
+    In SQLAlchemy 1.0, this is only used for lazy loaders / deferred
+    loaders that were set up via query option.
+
+    Previously, callables was used also to indicate expired attributes
+    by storing a link to the InstanceState itself in this dictionary.
+    This role is now handled by the expired_attributes set.
+
+    """
+
+    if not TYPE_CHECKING:
+        callables = util.EMPTY_DICT
+
+    def __init__(self, obj: _O, manager: ClassManager[_O]):
+        self.class_ = obj.__class__
+        self.manager = manager
+        self.obj = weakref.ref(obj, self._cleanup)
+        self.committed_state = {}
+        self.expired_attributes = set()
+
+    @util.memoized_property
+    def attrs(self) -> util.ReadOnlyProperties[AttributeState]:
+        """Return a namespace representing each attribute on
+        the mapped object, including its current value
+        and history.
+
+        The returned object is an instance of :class:`.AttributeState`.
+        This object allows inspection of the current data
+        within an attribute as well as attribute history
+        since the last flush.
+
+        """
+        return util.ReadOnlyProperties(
+            {key: AttributeState(self, key) for key in self.manager}
+        )
+
+    @property
+    def transient(self) -> bool:
+        """Return ``True`` if the object is :term:`transient`.
+
+        .. seealso::
+
+            :ref:`session_object_states`
+
+        """
+        return self.key is None and not self._attached
+
+    @property
+    def pending(self) -> bool:
+        """Return ``True`` if the object is :term:`pending`.
+
+        .. seealso::
+
+            :ref:`session_object_states`
+
+        """
+        return self.key is None and self._attached
+
+    @property
+    def deleted(self) -> bool:
+        """Return ``True`` if the object is :term:`deleted`.
+
+        An object that is in the deleted state is guaranteed to
+        not be within the :attr:`.Session.identity_map` of its parent
+        :class:`.Session`; however if the session's transaction is rolled
+        back, the object will be restored to the persistent state and
+        the identity map.
+
+        .. note::
+
+            The :attr:`.InstanceState.deleted` attribute refers to a specific
+            state of the object that occurs between the "persistent" and
+            "detached" states; once the object is :term:`detached`, the
+            :attr:`.InstanceState.deleted` attribute **no longer returns
+            True**; in order to detect that a state was deleted, regardless
+            of whether or not the object is associated with a
+            :class:`.Session`, use the :attr:`.InstanceState.was_deleted`
+            accessor.
+
+        .. versionadded: 1.1
+
+        .. seealso::
+
+            :ref:`session_object_states`
+
+        """
+        return self.key is not None and self._attached and self._deleted
+
+    @property
+    def was_deleted(self) -> bool:
+        """Return True if this object is or was previously in the
+        "deleted" state and has not been reverted to persistent.
+
+        This flag returns True once the object was deleted in flush.
+        When the object is expunged from the session either explicitly
+        or via transaction commit and enters the "detached" state,
+        this flag will continue to report True.
+
+        .. seealso::
+
+            :attr:`.InstanceState.deleted` - refers to the "deleted" state
+
+            :func:`.orm.util.was_deleted` - standalone function
+
+            :ref:`session_object_states`
+
+        """
+        return self._deleted
+
+    @property
+    def persistent(self) -> bool:
+        """Return ``True`` if the object is :term:`persistent`.
+
+        An object that is in the persistent state is guaranteed to
+        be within the :attr:`.Session.identity_map` of its parent
+        :class:`.Session`.
+
+        .. seealso::
+
+            :ref:`session_object_states`
+
+        """
+        return self.key is not None and self._attached and not self._deleted
+
+    @property
+    def detached(self) -> bool:
+        """Return ``True`` if the object is :term:`detached`.
+
+        .. seealso::
+
+            :ref:`session_object_states`
+
+        """
+        return self.key is not None and not self._attached
+
+    @util.non_memoized_property
+    @util.preload_module("sqlalchemy.orm.session")
+    def _attached(self) -> bool:
+        return (
+            self.session_id is not None
+            and self.session_id in util.preloaded.orm_session._sessions
+        )
+
+    def _track_last_known_value(self, key: str) -> None:
+        """Track the last known value of a particular key after expiration
+        operations.
+
+        .. versionadded:: 1.3
+
+        """
+
+        lkv = self._last_known_values
+        if lkv is None:
+            self._last_known_values = lkv = {}
+        if key not in lkv:
+            lkv[key] = NO_VALUE
+
+    @property
+    def session(self) -> Optional[Session]:
+        """Return the owning :class:`.Session` for this instance,
+        or ``None`` if none available.
+
+        Note that the result here can in some cases be *different*
+        from that of ``obj in session``; an object that's been deleted
+        will report as not ``in session``, however if the transaction is
+        still in progress, this attribute will still refer to that session.
+        Only when the transaction is completed does the object become
+        fully detached under normal circumstances.
+
+        .. seealso::
+
+            :attr:`_orm.InstanceState.async_session`
+
+        """
+        if self.session_id:
+            try:
+                return _sessions[self.session_id]
+            except KeyError:
+                pass
+        return None
+
+    @property
+    def async_session(self) -> Optional[AsyncSession]:
+        """Return the owning :class:`_asyncio.AsyncSession` for this instance,
+        or ``None`` if none available.
+
+        This attribute is only non-None when the :mod:`sqlalchemy.ext.asyncio`
+        API is in use for this ORM object. The returned
+        :class:`_asyncio.AsyncSession` object will be a proxy for the
+        :class:`_orm.Session` object that would be returned from the
+        :attr:`_orm.InstanceState.session` attribute for this
+        :class:`_orm.InstanceState`.
+
+        .. versionadded:: 1.4.18
+
+        .. seealso::
+
+            :ref:`asyncio_toplevel`
+
+        """
+        if _async_provider is None:
+            return None
+
+        sess = self.session
+        if sess is not None:
+            return _async_provider(sess)
+        else:
+            return None
+
+    @property
+    def object(self) -> Optional[_O]:
+        """Return the mapped object represented by this
+        :class:`.InstanceState`.
+
+        Returns None if the object has been garbage collected
+
+        """
+        return self.obj()
+
+    @property
+    def identity(self) -> Optional[Tuple[Any, ...]]:
+        """Return the mapped identity of the mapped object.
+        This is the primary key identity as persisted by the ORM
+        which can always be passed directly to
+        :meth:`_query.Query.get`.
+
+        Returns ``None`` if the object has no primary key identity.
+
+        .. note::
+            An object which is :term:`transient` or :term:`pending`
+            does **not** have a mapped identity until it is flushed,
+            even if its attributes include primary key values.
+
+        """
+        if self.key is None:
+            return None
+        else:
+            return self.key[1]
+
+    @property
+    def identity_key(self) -> Optional[_IdentityKeyType[_O]]:
+        """Return the identity key for the mapped object.
+
+        This is the key used to locate the object within
+        the :attr:`.Session.identity_map` mapping.   It contains
+        the identity as returned by :attr:`.identity` within it.
+
+
+        """
+        return self.key
+
+    @util.memoized_property
+    def parents(self) -> Dict[int, Union[Literal[False], InstanceState[Any]]]:
+        return {}
+
+    @util.memoized_property
+    def _pending_mutations(self) -> Dict[str, PendingCollection]:
+        return {}
+
+    @util.memoized_property
+    def _empty_collections(self) -> Dict[str, _AdaptedCollectionProtocol]:
+        return {}
+
+    @util.memoized_property
+    def mapper(self) -> Mapper[_O]:
+        """Return the :class:`_orm.Mapper` used for this mapped object."""
+        return self.manager.mapper
+
+    @property
+    def has_identity(self) -> bool:
+        """Return ``True`` if this object has an identity key.
+
+        This should always have the same value as the
+        expression ``state.persistent`` or ``state.detached``.
+
+        """
+        return bool(self.key)
+
+    @classmethod
+    def _detach_states(
+        self,
+        states: Iterable[InstanceState[_O]],
+        session: Session,
+        to_transient: bool = False,
+    ) -> None:
+        persistent_to_detached = (
+            session.dispatch.persistent_to_detached or None
+        )
+        deleted_to_detached = session.dispatch.deleted_to_detached or None
+        pending_to_transient = session.dispatch.pending_to_transient or None
+        persistent_to_transient = (
+            session.dispatch.persistent_to_transient or None
+        )
+
+        for state in states:
+            deleted = state._deleted
+            pending = state.key is None
+            persistent = not pending and not deleted
+
+            state.session_id = None
+
+            if to_transient and state.key:
+                del state.key
+            if persistent:
+                if to_transient:
+                    if persistent_to_transient is not None:
+                        persistent_to_transient(session, state)
+                elif persistent_to_detached is not None:
+                    persistent_to_detached(session, state)
+            elif deleted and deleted_to_detached is not None:
+                deleted_to_detached(session, state)
+            elif pending and pending_to_transient is not None:
+                pending_to_transient(session, state)
+
+            state._strong_obj = None
+
+    def _detach(self, session: Optional[Session] = None) -> None:
+        if session:
+            InstanceState._detach_states([self], session)
+        else:
+            self.session_id = self._strong_obj = None
+
+    def _dispose(self) -> None:
+        # used by the test suite, apparently
+        self._detach()
+
+    def _cleanup(self, ref: weakref.ref[_O]) -> None:
+        """Weakref callback cleanup.
+
+        This callable cleans out the state when it is being garbage
+        collected.
+
+        this _cleanup **assumes** that there are no strong refs to us!
+        Will not work otherwise!
+
+        """
+
+        # Python builtins become undefined during interpreter shutdown.
+        # Guard against exceptions during this phase, as the method cannot
+        # proceed in any case if builtins have been undefined.
+        if dict is None:
+            return
+
+        instance_dict = self._instance_dict()
+        if instance_dict is not None:
+            instance_dict._fast_discard(self)
+            del self._instance_dict
+
+            # we can't possibly be in instance_dict._modified
+            # b.c. this is weakref cleanup only, that set
+            # is strong referencing!
+            # assert self not in instance_dict._modified
+
+        self.session_id = self._strong_obj = None
+
+    @property
+    def dict(self) -> _InstanceDict:
+        """Return the instance dict used by the object.
+
+        Under normal circumstances, this is always synonymous
+        with the ``__dict__`` attribute of the mapped object,
+        unless an alternative instrumentation system has been
+        configured.
+
+        In the case that the actual object has been garbage
+        collected, this accessor returns a blank dictionary.
+
+        """
+        o = self.obj()
+        if o is not None:
+            return base.instance_dict(o)
+        else:
+            return {}
+
+    def _initialize_instance(*mixed: Any, **kwargs: Any) -> None:
+        self, instance, args = mixed[0], mixed[1], mixed[2:]  # noqa
+        manager = self.manager
+
+        manager.dispatch.init(self, args, kwargs)
+
+        try:
+            manager.original_init(*mixed[1:], **kwargs)
+        except:
+            with util.safe_reraise():
+                manager.dispatch.init_failure(self, args, kwargs)
+
+    def get_history(self, key: str, passive: PassiveFlag) -> History:
+        return self.manager[key].impl.get_history(self, self.dict, passive)
+
+    def get_impl(self, key: str) -> AttributeImpl:
+        return self.manager[key].impl
+
+    def _get_pending_mutation(self, key: str) -> PendingCollection:
+        if key not in self._pending_mutations:
+            self._pending_mutations[key] = PendingCollection()
+        return self._pending_mutations[key]
+
+    def __getstate__(self) -> Dict[str, Any]:
+        state_dict: Dict[str, Any] = {
+            "instance": self.obj(),
+            "class_": self.class_,
+            "committed_state": self.committed_state,
+            "expired_attributes": self.expired_attributes,
+        }
+        state_dict.update(
+            (k, self.__dict__[k])
+            for k in (
+                "_pending_mutations",
+                "modified",
+                "expired",
+                "callables",
+                "key",
+                "parents",
+                "load_options",
+                "class_",
+                "expired_attributes",
+                "info",
+            )
+            if k in self.__dict__
+        )
+        if self.load_path:
+            state_dict["load_path"] = self.load_path.serialize()
+
+        state_dict["manager"] = self.manager._serialize(self, state_dict)
+
+        return state_dict
+
+    def __setstate__(self, state_dict: Dict[str, Any]) -> None:
+        inst = state_dict["instance"]
+        if inst is not None:
+            self.obj = weakref.ref(inst, self._cleanup)
+            self.class_ = inst.__class__
+        else:
+            self.obj = lambda: None  # type: ignore
+            self.class_ = state_dict["class_"]
+
+        self.committed_state = state_dict.get("committed_state", {})
+        self._pending_mutations = state_dict.get("_pending_mutations", {})
+        self.parents = state_dict.get("parents", {})
+        self.modified = state_dict.get("modified", False)
+        self.expired = state_dict.get("expired", False)
+        if "info" in state_dict:
+            self.info.update(state_dict["info"])
+        if "callables" in state_dict:
+            self.callables = state_dict["callables"]
+
+            self.expired_attributes = state_dict["expired_attributes"]
+        else:
+            if "expired_attributes" in state_dict:
+                self.expired_attributes = state_dict["expired_attributes"]
+            else:
+                self.expired_attributes = set()
+
+        self.__dict__.update(
+            [
+                (k, state_dict[k])
+                for k in ("key", "load_options")
+                if k in state_dict
+            ]
+        )
+        if self.key:
+            self.identity_token = self.key[2]
+
+        if "load_path" in state_dict:
+            self.load_path = PathRegistry.deserialize(state_dict["load_path"])
+
+        state_dict["manager"](self, inst, state_dict)
+
+    def _reset(self, dict_: _InstanceDict, key: str) -> None:
+        """Remove the given attribute and any
+        callables associated with it."""
+
+        old = dict_.pop(key, None)
+        manager_impl = self.manager[key].impl
+        if old is not None and is_collection_impl(manager_impl):
+            manager_impl._invalidate_collection(old)
+        self.expired_attributes.discard(key)
+        if self.callables:
+            self.callables.pop(key, None)
+
+    def _copy_callables(self, from_: InstanceState[Any]) -> None:
+        if "callables" in from_.__dict__:
+            self.callables = dict(from_.callables)
+
+    @classmethod
+    def _instance_level_callable_processor(
+        cls, manager: ClassManager[_O], fn: _LoaderCallable, key: Any
+    ) -> _InstallLoaderCallableProto[_O]:
+        impl = manager[key].impl
+        if is_collection_impl(impl):
+            fixed_impl = impl
+
+            def _set_callable(
+                state: InstanceState[_O], dict_: _InstanceDict, row: Row[Any]
+            ) -> None:
+                if "callables" not in state.__dict__:
+                    state.callables = {}
+                old = dict_.pop(key, None)
+                if old is not None:
+                    fixed_impl._invalidate_collection(old)
+                state.callables[key] = fn
+
+        else:
+
+            def _set_callable(
+                state: InstanceState[_O], dict_: _InstanceDict, row: Row[Any]
+            ) -> None:
+                if "callables" not in state.__dict__:
+                    state.callables = {}
+                state.callables[key] = fn
+
+        return _set_callable
+
+    def _expire(
+        self, dict_: _InstanceDict, modified_set: Set[InstanceState[Any]]
+    ) -> None:
+        self.expired = True
+        if self.modified:
+            modified_set.discard(self)
+            self.committed_state.clear()
+            self.modified = False
+
+        self._strong_obj = None
+
+        if "_pending_mutations" in self.__dict__:
+            del self.__dict__["_pending_mutations"]
+
+        if "parents" in self.__dict__:
+            del self.__dict__["parents"]
+
+        self.expired_attributes.update(
+            [impl.key for impl in self.manager._loader_impls]
+        )
+
+        if self.callables:
+            # the per state loader callables we can remove here are
+            # LoadDeferredColumns, which undefers a column at the instance
+            # level that is mapped with deferred, and LoadLazyAttribute,
+            # which lazy loads a relationship at the instance level that
+            # is mapped with "noload" or perhaps "immediateload".
+            # Before 1.4, only column-based
+            # attributes could be considered to be "expired", so here they
+            # were the only ones "unexpired", which means to make them deferred
+            # again.   For the moment, as of 1.4 we also apply the same
+            # treatment relationships now, that is, an instance level lazy
+            # loader is reset in the same way as a column loader.
+            for k in self.expired_attributes.intersection(self.callables):
+                del self.callables[k]
+
+        for k in self.manager._collection_impl_keys.intersection(dict_):
+            collection = dict_.pop(k)
+            collection._sa_adapter.invalidated = True
+
+        if self._last_known_values:
+            self._last_known_values.update(
+                {k: dict_[k] for k in self._last_known_values if k in dict_}
+            )
+
+        for key in self.manager._all_key_set.intersection(dict_):
+            del dict_[key]
+
+        self.manager.dispatch.expire(self, None)
+
+    def _expire_attributes(
+        self,
+        dict_: _InstanceDict,
+        attribute_names: Iterable[str],
+        no_loader: bool = False,
+    ) -> None:
+        pending = self.__dict__.get("_pending_mutations", None)
+
+        callables = self.callables
+
+        for key in attribute_names:
+            impl = self.manager[key].impl
+            if impl.accepts_scalar_loader:
+                if no_loader and (impl.callable_ or key in callables):
+                    continue
+
+                self.expired_attributes.add(key)
+                if callables and key in callables:
+                    del callables[key]
+            old = dict_.pop(key, NO_VALUE)
+            if is_collection_impl(impl) and old is not NO_VALUE:
+                impl._invalidate_collection(old)
+
+            lkv = self._last_known_values
+            if lkv is not None and key in lkv and old is not NO_VALUE:
+                lkv[key] = old
+
+            self.committed_state.pop(key, None)
+            if pending:
+                pending.pop(key, None)
+
+        self.manager.dispatch.expire(self, attribute_names)
+
+    def _load_expired(
+        self, state: InstanceState[_O], passive: PassiveFlag
+    ) -> LoaderCallableStatus:
+        """__call__ allows the InstanceState to act as a deferred
+        callable for loading expired attributes, which is also
+        serializable (picklable).
+
+        """
+
+        if not passive & SQL_OK:
+            return PASSIVE_NO_RESULT
+
+        toload = self.expired_attributes.intersection(self.unmodified)
+        toload = toload.difference(
+            attr
+            for attr in toload
+            if not self.manager[attr].impl.load_on_unexpire
+        )
+
+        self.manager.expired_attribute_loader(self, toload, passive)
+
+        # if the loader failed, or this
+        # instance state didn't have an identity,
+        # the attributes still might be in the callables
+        # dict.  ensure they are removed.
+        self.expired_attributes.clear()
+
+        return ATTR_WAS_SET
+
+    @property
+    def unmodified(self) -> Set[str]:
+        """Return the set of keys which have no uncommitted changes"""
+
+        return set(self.manager).difference(self.committed_state)
+
+    def unmodified_intersection(self, keys: Iterable[str]) -> Set[str]:
+        """Return self.unmodified.intersection(keys)."""
+
+        return (
+            set(keys)
+            .intersection(self.manager)
+            .difference(self.committed_state)
+        )
+
+    @property
+    def unloaded(self) -> Set[str]:
+        """Return the set of keys which do not have a loaded value.
+
+        This includes expired attributes and any other attribute that was never
+        populated or modified.
+
+        """
+        return (
+            set(self.manager)
+            .difference(self.committed_state)
+            .difference(self.dict)
+        )
+
+    @property
+    @util.deprecated(
+        "2.0",
+        "The :attr:`.InstanceState.unloaded_expirable` attribute is "
+        "deprecated.  Please use :attr:`.InstanceState.unloaded`.",
+    )
+    def unloaded_expirable(self) -> Set[str]:
+        """Synonymous with :attr:`.InstanceState.unloaded`.
+
+        This attribute was added as an implementation-specific detail at some
+        point and should be considered to be private.
+
+        """
+        return self.unloaded
+
+    @property
+    def _unloaded_non_object(self) -> Set[str]:
+        return self.unloaded.intersection(
+            attr
+            for attr in self.manager
+            if self.manager[attr].impl.accepts_scalar_loader
+        )
+
+    def _modified_event(
+        self,
+        dict_: _InstanceDict,
+        attr: Optional[AttributeImpl],
+        previous: Any,
+        collection: bool = False,
+        is_userland: bool = False,
+    ) -> None:
+        if attr:
+            if not attr.send_modified_events:
+                return
+            if is_userland and attr.key not in dict_:
+                raise sa_exc.InvalidRequestError(
+                    "Can't flag attribute '%s' modified; it's not present in "
+                    "the object state" % attr.key
+                )
+            if attr.key not in self.committed_state or is_userland:
+                if collection:
+                    if TYPE_CHECKING:
+                        assert is_collection_impl(attr)
+                    if previous is NEVER_SET:
+                        if attr.key in dict_:
+                            previous = dict_[attr.key]
+
+                    if previous not in (None, NO_VALUE, NEVER_SET):
+                        previous = attr.copy(previous)
+                self.committed_state[attr.key] = previous
+
+            lkv = self._last_known_values
+            if lkv is not None and attr.key in lkv:
+                lkv[attr.key] = NO_VALUE
+
+        # assert self._strong_obj is None or self.modified
+
+        if (self.session_id and self._strong_obj is None) or not self.modified:
+            self.modified = True
+            instance_dict = self._instance_dict()
+            if instance_dict:
+                has_modified = bool(instance_dict._modified)
+                instance_dict._modified.add(self)
+            else:
+                has_modified = False
+
+            # only create _strong_obj link if attached
+            # to a session
+
+            inst = self.obj()
+            if self.session_id:
+                self._strong_obj = inst
+
+                # if identity map already had modified objects,
+                # assume autobegin already occurred, else check
+                # for autobegin
+                if not has_modified:
+                    # inline of autobegin, to ensure session transaction
+                    # snapshot is established
+                    try:
+                        session = _sessions[self.session_id]
+                    except KeyError:
+                        pass
+                    else:
+                        if session._transaction is None:
+                            session._autobegin_t()
+
+            if inst is None and attr:
+                raise orm_exc.ObjectDereferencedError(
+                    "Can't emit change event for attribute '%s' - "
+                    "parent object of type %s has been garbage "
+                    "collected."
+                    % (self.manager[attr.key], base.state_class_str(self))
+                )
+
+    def _commit(self, dict_: _InstanceDict, keys: Iterable[str]) -> None:
+        """Commit attributes.
+
+        This is used by a partial-attribute load operation to mark committed
+        those attributes which were refreshed from the database.
+
+        Attributes marked as "expired" can potentially remain "expired" after
+        this step if a value was not populated in state.dict.
+
+        """
+        for key in keys:
+            self.committed_state.pop(key, None)
+
+        self.expired = False
+
+        self.expired_attributes.difference_update(
+            set(keys).intersection(dict_)
+        )
+
+        # the per-keys commit removes object-level callables,
+        # while that of commit_all does not.  it's not clear
+        # if this behavior has a clear rationale, however tests do
+        # ensure this is what it does.
+        if self.callables:
+            for key in (
+                set(self.callables).intersection(keys).intersection(dict_)
+            ):
+                del self.callables[key]
+
+    def _commit_all(
+        self, dict_: _InstanceDict, instance_dict: Optional[IdentityMap] = None
+    ) -> None:
+        """commit all attributes unconditionally.
+
+        This is used after a flush() or a full load/refresh
+        to remove all pending state from the instance.
+
+         - all attributes are marked as "committed"
+         - the "strong dirty reference" is removed
+         - the "modified" flag is set to False
+         - any "expired" markers for scalar attributes loaded are removed.
+         - lazy load callables for objects / collections *stay*
+
+        Attributes marked as "expired" can potentially remain
+        "expired" after this step if a value was not populated in state.dict.
+
+        """
+        self._commit_all_states([(self, dict_)], instance_dict)
+
+    @classmethod
+    def _commit_all_states(
+        self,
+        iter_: Iterable[Tuple[InstanceState[Any], _InstanceDict]],
+        instance_dict: Optional[IdentityMap] = None,
+    ) -> None:
+        """Mass / highly inlined version of commit_all()."""
+
+        for state, dict_ in iter_:
+            state_dict = state.__dict__
+
+            state.committed_state.clear()
+
+            if "_pending_mutations" in state_dict:
+                del state_dict["_pending_mutations"]
+
+            state.expired_attributes.difference_update(dict_)
+
+            if instance_dict and state.modified:
+                instance_dict._modified.discard(state)
+
+            state.modified = state.expired = False
+            state._strong_obj = None
+
+
+class AttributeState:
+    """Provide an inspection interface corresponding
+    to a particular attribute on a particular mapped object.
+
+    The :class:`.AttributeState` object is accessed
+    via the :attr:`.InstanceState.attrs` collection
+    of a particular :class:`.InstanceState`::
+
+        from sqlalchemy import inspect
+
+        insp = inspect(some_mapped_object)
+        attr_state = insp.attrs.some_attribute
+
+    """
+
+    __slots__ = ("state", "key")
+
+    state: InstanceState[Any]
+    key: str
+
+    def __init__(self, state: InstanceState[Any], key: str):
+        self.state = state
+        self.key = key
+
+    @property
+    def loaded_value(self) -> Any:
+        """The current value of this attribute as loaded from the database.
+
+        If the value has not been loaded, or is otherwise not present
+        in the object's dictionary, returns NO_VALUE.
+
+        """
+        return self.state.dict.get(self.key, NO_VALUE)
+
+    @property
+    def value(self) -> Any:
+        """Return the value of this attribute.
+
+        This operation is equivalent to accessing the object's
+        attribute directly or via ``getattr()``, and will fire
+        off any pending loader callables if needed.
+
+        """
+        return self.state.manager[self.key].__get__(
+            self.state.obj(), self.state.class_
+        )
+
+    @property
+    def history(self) -> History:
+        """Return the current **pre-flush** change history for
+        this attribute, via the :class:`.History` interface.
+
+        This method will **not** emit loader callables if the value of the
+        attribute is unloaded.
+
+        .. note::
+
+            The attribute history system tracks changes on a **per flush
+            basis**. Each time the :class:`.Session` is flushed, the history
+            of each attribute is reset to empty.   The :class:`.Session` by
+            default autoflushes each time a :class:`_query.Query` is invoked.
+            For
+            options on how to control this, see :ref:`session_flushing`.
+
+
+        .. seealso::
+
+            :meth:`.AttributeState.load_history` - retrieve history
+            using loader callables if the value is not locally present.
+
+            :func:`.attributes.get_history` - underlying function
+
+        """
+        return self.state.get_history(self.key, PASSIVE_NO_INITIALIZE)
+
+    def load_history(self) -> History:
+        """Return the current **pre-flush** change history for
+        this attribute, via the :class:`.History` interface.
+
+        This method **will** emit loader callables if the value of the
+        attribute is unloaded.
+
+        .. note::
+
+            The attribute history system tracks changes on a **per flush
+            basis**. Each time the :class:`.Session` is flushed, the history
+            of each attribute is reset to empty.   The :class:`.Session` by
+            default autoflushes each time a :class:`_query.Query` is invoked.
+            For
+            options on how to control this, see :ref:`session_flushing`.
+
+        .. seealso::
+
+            :attr:`.AttributeState.history`
+
+            :func:`.attributes.get_history` - underlying function
+
+        """
+        return self.state.get_history(self.key, PASSIVE_OFF ^ INIT_OK)
+
+
+class PendingCollection:
+    """A writable placeholder for an unloaded collection.
+
+    Stores items appended to and removed from a collection that has not yet
+    been loaded. When the collection is loaded, the changes stored in
+    PendingCollection are applied to it to produce the final result.
+
+    """
+
+    __slots__ = ("deleted_items", "added_items")
+
+    deleted_items: util.IdentitySet
+    added_items: util.OrderedIdentitySet
+
+    def __init__(self) -> None:
+        self.deleted_items = util.IdentitySet()
+        self.added_items = util.OrderedIdentitySet()
+
+    def merge_with_history(self, history: History) -> History:
+        return history._merge(self.added_items, self.deleted_items)
+
+    def append(self, value: Any) -> None:
+        if value in self.deleted_items:
+            self.deleted_items.remove(value)
+        else:
+            self.added_items.add(value)
+
+    def remove(self, value: Any) -> None:
+        if value in self.added_items:
+            self.added_items.remove(value)
+        else:
+            self.deleted_items.add(value)