1# orm/session.py 
    2# Copyright (C) 2005-2025 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: https://www.opensource.org/licenses/mit-license.php 
    7 
    8"""Provides the Session class and related utilities.""" 
    9 
    10from __future__ import annotations 
    11 
    12import contextlib 
    13from enum import Enum 
    14import itertools 
    15import sys 
    16import typing 
    17from typing import Any 
    18from typing import Callable 
    19from typing import cast 
    20from typing import Dict 
    21from typing import Generic 
    22from typing import Iterable 
    23from typing import Iterator 
    24from typing import List 
    25from typing import Literal 
    26from typing import NoReturn 
    27from typing import Optional 
    28from typing import overload 
    29from typing import Protocol 
    30from typing import Sequence 
    31from typing import Set 
    32from typing import Tuple 
    33from typing import Type 
    34from typing import TYPE_CHECKING 
    35from typing import TypeVar 
    36from typing import Union 
    37import weakref 
    38 
    39from . import attributes 
    40from . import bulk_persistence 
    41from . import context 
    42from . import descriptor_props 
    43from . import exc 
    44from . import identity 
    45from . import loading 
    46from . import query 
    47from . import state as statelib 
    48from ._typing import _O 
    49from ._typing import insp_is_mapper 
    50from ._typing import is_composite_class 
    51from ._typing import is_orm_option 
    52from ._typing import is_user_defined_option 
    53from .base import _class_to_mapper 
    54from .base import _none_set 
    55from .base import _state_mapper 
    56from .base import instance_str 
    57from .base import LoaderCallableStatus 
    58from .base import object_mapper 
    59from .base import object_state 
    60from .base import PassiveFlag 
    61from .base import state_str 
    62from .context import _ORMCompileState 
    63from .context import FromStatement 
    64from .identity import IdentityMap 
    65from .query import Query 
    66from .state import InstanceState 
    67from .state_changes import _StateChange 
    68from .state_changes import _StateChangeState 
    69from .state_changes import _StateChangeStates 
    70from .unitofwork import UOWTransaction 
    71from .. import engine 
    72from .. import exc as sa_exc 
    73from .. import sql 
    74from .. import util 
    75from ..engine import Connection 
    76from ..engine import Engine 
    77from ..engine.util import TransactionalContext 
    78from ..event import dispatcher 
    79from ..event import EventTarget 
    80from ..inspection import inspect 
    81from ..inspection import Inspectable 
    82from ..sql import coercions 
    83from ..sql import dml 
    84from ..sql import roles 
    85from ..sql import Select 
    86from ..sql import TableClause 
    87from ..sql import visitors 
    88from ..sql.base import _NoArg 
    89from ..sql.base import CompileState 
    90from ..sql.schema import Table 
    91from ..sql.selectable import ForUpdateArg 
    92from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 
    93from ..util import deprecated_params 
    94from ..util import IdentitySet 
    95from ..util.typing import TupleAny 
    96from ..util.typing import TypeVarTuple 
    97from ..util.typing import Unpack 
    98 
    99 
    100if typing.TYPE_CHECKING: 
    101    from ._typing import _EntityType 
    102    from ._typing import _IdentityKeyType 
    103    from ._typing import _InstanceDict 
    104    from ._typing import OrmExecuteOptionsParameter 
    105    from .interfaces import ORMOption 
    106    from .interfaces import UserDefinedOption 
    107    from .mapper import Mapper 
    108    from .path_registry import PathRegistry 
    109    from .query import RowReturningQuery 
    110    from ..engine import Result 
    111    from ..engine import Row 
    112    from ..engine import RowMapping 
    113    from ..engine.base import Transaction 
    114    from ..engine.base import TwoPhaseTransaction 
    115    from ..engine.interfaces import _CoreAnyExecuteParams 
    116    from ..engine.interfaces import _CoreSingleExecuteParams 
    117    from ..engine.interfaces import _ExecuteOptions 
    118    from ..engine.interfaces import CoreExecuteOptionsParameter 
    119    from ..engine.result import ScalarResult 
    120    from ..event import _InstanceLevelDispatch 
    121    from ..sql._typing import _ColumnsClauseArgument 
    122    from ..sql._typing import _InfoType 
    123    from ..sql._typing import _T0 
    124    from ..sql._typing import _T1 
    125    from ..sql._typing import _T2 
    126    from ..sql._typing import _T3 
    127    from ..sql._typing import _T4 
    128    from ..sql._typing import _T5 
    129    from ..sql._typing import _T6 
    130    from ..sql._typing import _T7 
    131    from ..sql._typing import _TypedColumnClauseArgument as _TCCA 
    132    from ..sql.base import Executable 
    133    from ..sql.base import ExecutableOption 
    134    from ..sql.elements import ClauseElement 
    135    from ..sql.roles import TypedColumnsClauseRole 
    136    from ..sql.selectable import ForUpdateParameter 
    137    from ..sql.selectable import TypedReturnsRows 
    138 
    139_T = TypeVar("_T", bound=Any) 
    140_Ts = TypeVarTuple("_Ts") 
    141 
    142__all__ = [ 
    143    "Session", 
    144    "SessionTransaction", 
    145    "sessionmaker", 
    146    "ORMExecuteState", 
    147    "close_all_sessions", 
    148    "make_transient", 
    149    "make_transient_to_detached", 
    150    "object_session", 
    151] 
    152 
    153_sessions: weakref.WeakValueDictionary[int, Session] = ( 
    154    weakref.WeakValueDictionary() 
    155) 
    156"""Weak-referencing dictionary of :class:`.Session` objects. 
    157""" 
    158 
    159statelib._sessions = _sessions 
    160 
    161_PKIdentityArgument = Union[Any, Tuple[Any, ...]] 
    162 
    163_BindArguments = Dict[str, Any] 
    164 
    165_EntityBindKey = Union[Type[_O], "Mapper[_O]"] 
    166_SessionBindKey = Union[Type[Any], "Mapper[Any]", "TableClause", str] 
    167_SessionBind = Union["Engine", "Connection"] 
    168 
    169JoinTransactionMode = Literal[ 
    170    "conditional_savepoint", 
    171    "rollback_only", 
    172    "control_fully", 
    173    "create_savepoint", 
    174] 
    175 
    176 
    177class _ConnectionCallableProto(Protocol): 
    178    """a callable that returns a :class:`.Connection` given an instance. 
    179 
    180    This callable, when present on a :class:`.Session`, is called only from the 
    181    ORM's persistence mechanism (i.e. the unit of work flush process) to allow 
    182    for connection-per-instance schemes (i.e. horizontal sharding) to be used 
    183    as persistence time. 
    184 
    185    This callable is not present on a plain :class:`.Session`, however 
    186    is established when using the horizontal sharding extension. 
    187 
    188    """ 
    189 
    190    def __call__( 
    191        self, 
    192        mapper: Optional[Mapper[Any]] = None, 
    193        instance: Optional[object] = None, 
    194        **kw: Any, 
    195    ) -> Connection: ... 
    196 
    197 
    198def _state_session(state: InstanceState[Any]) -> Optional[Session]: 
    199    """Given an :class:`.InstanceState`, return the :class:`.Session` 
    200    associated, if any. 
    201    """ 
    202    return state.session 
    203 
    204 
    205class _SessionClassMethods: 
    206    """Class-level methods for :class:`.Session`, :class:`.sessionmaker`.""" 
    207 
    208    @classmethod 
    209    @util.preload_module("sqlalchemy.orm.util") 
    210    def identity_key( 
    211        cls, 
    212        class_: Optional[Type[Any]] = None, 
    213        ident: Union[Any, Tuple[Any, ...]] = None, 
    214        *, 
    215        instance: Optional[Any] = None, 
    216        row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]] = None, 
    217        identity_token: Optional[Any] = None, 
    218    ) -> _IdentityKeyType[Any]: 
    219        """Return an identity key. 
    220 
    221        This is an alias of :func:`.util.identity_key`. 
    222 
    223        """ 
    224        return util.preloaded.orm_util.identity_key( 
    225            class_, 
    226            ident, 
    227            instance=instance, 
    228            row=row, 
    229            identity_token=identity_token, 
    230        ) 
    231 
    232    @classmethod 
    233    def object_session(cls, instance: object) -> Optional[Session]: 
    234        """Return the :class:`.Session` to which an object belongs. 
    235 
    236        This is an alias of :func:`.object_session`. 
    237 
    238        """ 
    239 
    240        return object_session(instance) 
    241 
    242 
    243class SessionTransactionState(_StateChangeState): 
    244    ACTIVE = 1 
    245    PREPARED = 2 
    246    COMMITTED = 3 
    247    DEACTIVE = 4 
    248    CLOSED = 5 
    249    PROVISIONING_CONNECTION = 6 
    250 
    251 
    252# backwards compatibility 
    253ACTIVE, PREPARED, COMMITTED, DEACTIVE, CLOSED, PROVISIONING_CONNECTION = tuple( 
    254    SessionTransactionState 
    255) 
    256 
    257 
    258class ORMExecuteState(util.MemoizedSlots): 
    259    """Represents a call to the :meth:`_orm.Session.execute` method, as passed 
    260    to the :meth:`.SessionEvents.do_orm_execute` event hook. 
    261 
    262    .. versionadded:: 1.4 
    263 
    264    .. seealso:: 
    265 
    266        :ref:`session_execute_events` - top level documentation on how 
    267        to use :meth:`_orm.SessionEvents.do_orm_execute` 
    268 
    269    """ 
    270 
    271    __slots__ = ( 
    272        "session", 
    273        "statement", 
    274        "parameters", 
    275        "execution_options", 
    276        "local_execution_options", 
    277        "bind_arguments", 
    278        "identity_token", 
    279        "_compile_state_cls", 
    280        "_starting_event_idx", 
    281        "_events_todo", 
    282        "_update_execution_options", 
    283    ) 
    284 
    285    session: Session 
    286    """The :class:`_orm.Session` in use.""" 
    287 
    288    statement: Executable 
    289    """The SQL statement being invoked. 
    290 
    291    For an ORM selection as would 
    292    be retrieved from :class:`_orm.Query`, this is an instance of 
    293    :class:`_sql.select` that was generated from the ORM query. 
    294    """ 
    295 
    296    parameters: Optional[_CoreAnyExecuteParams] 
    297    """Dictionary of parameters that was passed to 
    298    :meth:`_orm.Session.execute`.""" 
    299 
    300    execution_options: _ExecuteOptions 
    301    """The complete dictionary of current execution options. 
    302 
    303    This is a merge of the statement level options with the 
    304    locally passed execution options. 
    305 
    306    .. seealso:: 
    307 
    308        :attr:`_orm.ORMExecuteState.local_execution_options` 
    309 
    310        :meth:`_sql.Executable.execution_options` 
    311 
    312        :ref:`orm_queryguide_execution_options` 
    313 
    314    """ 
    315 
    316    local_execution_options: _ExecuteOptions 
    317    """Dictionary view of the execution options passed to the 
    318    :meth:`.Session.execute` method. 
    319 
    320    This does not include options that may be associated with the statement 
    321    being invoked. 
    322 
    323    .. seealso:: 
    324 
    325        :attr:`_orm.ORMExecuteState.execution_options` 
    326 
    327    """ 
    328 
    329    bind_arguments: _BindArguments 
    330    """The dictionary passed as the 
    331    :paramref:`_orm.Session.execute.bind_arguments` dictionary. 
    332 
    333    This dictionary may be used by extensions to :class:`_orm.Session` to pass 
    334    arguments that will assist in determining amongst a set of database 
    335    connections which one should be used to invoke this statement. 
    336 
    337    """ 
    338 
    339    _compile_state_cls: Optional[Type[_ORMCompileState]] 
    340    _starting_event_idx: int 
    341    _events_todo: List[Any] 
    342    _update_execution_options: Optional[_ExecuteOptions] 
    343 
    344    def __init__( 
    345        self, 
    346        session: Session, 
    347        statement: Executable, 
    348        parameters: Optional[_CoreAnyExecuteParams], 
    349        execution_options: _ExecuteOptions, 
    350        bind_arguments: _BindArguments, 
    351        compile_state_cls: Optional[Type[_ORMCompileState]], 
    352        events_todo: List[_InstanceLevelDispatch[Session]], 
    353    ): 
    354        """Construct a new :class:`_orm.ORMExecuteState`. 
    355 
    356        this object is constructed internally. 
    357 
    358        """ 
    359        self.session = session 
    360        self.statement = statement 
    361        self.parameters = parameters 
    362        self.local_execution_options = execution_options 
    363        self.execution_options = statement._execution_options.union( 
    364            execution_options 
    365        ) 
    366        self.bind_arguments = bind_arguments 
    367        self._compile_state_cls = compile_state_cls 
    368        self._events_todo = list(events_todo) 
    369 
    370    def _remaining_events(self) -> List[_InstanceLevelDispatch[Session]]: 
    371        return self._events_todo[self._starting_event_idx + 1 :] 
    372 
    373    def invoke_statement( 
    374        self, 
    375        statement: Optional[Executable] = None, 
    376        params: Optional[_CoreAnyExecuteParams] = None, 
    377        execution_options: Optional[OrmExecuteOptionsParameter] = None, 
    378        bind_arguments: Optional[_BindArguments] = None, 
    379    ) -> Result[Unpack[TupleAny]]: 
    380        """Execute the statement represented by this 
    381        :class:`.ORMExecuteState`, without re-invoking events that have 
    382        already proceeded. 
    383 
    384        This method essentially performs a re-entrant execution of the current 
    385        statement for which the :meth:`.SessionEvents.do_orm_execute` event is 
    386        being currently invoked.    The use case for this is for event handlers 
    387        that want to override how the ultimate 
    388        :class:`_engine.Result` object is returned, such as for schemes that 
    389        retrieve results from an offline cache or which concatenate results 
    390        from multiple executions. 
    391 
    392        When the :class:`_engine.Result` object is returned by the actual 
    393        handler function within :meth:`_orm.SessionEvents.do_orm_execute` and 
    394        is propagated to the calling 
    395        :meth:`_orm.Session.execute` method, the remainder of the 
    396        :meth:`_orm.Session.execute` method is preempted and the 
    397        :class:`_engine.Result` object is returned to the caller of 
    398        :meth:`_orm.Session.execute` immediately. 
    399 
    400        :param statement: optional statement to be invoked, in place of the 
    401         statement currently represented by :attr:`.ORMExecuteState.statement`. 
    402 
    403        :param params: optional dictionary of parameters or list of parameters 
    404         which will be merged into the existing 
    405         :attr:`.ORMExecuteState.parameters` of this :class:`.ORMExecuteState`. 
    406 
    407         .. versionchanged:: 2.0 a list of parameter dictionaries is accepted 
    408            for executemany executions. 
    409 
    410        :param execution_options: optional dictionary of execution options 
    411         will be merged into the existing 
    412         :attr:`.ORMExecuteState.execution_options` of this 
    413         :class:`.ORMExecuteState`. 
    414 
    415        :param bind_arguments: optional dictionary of bind_arguments 
    416         which will be merged amongst the current 
    417         :attr:`.ORMExecuteState.bind_arguments` 
    418         of this :class:`.ORMExecuteState`. 
    419 
    420        :return: a :class:`_engine.Result` object with ORM-level results. 
    421 
    422        .. seealso:: 
    423 
    424            :ref:`do_orm_execute_re_executing` - background and examples on the 
    425            appropriate usage of :meth:`_orm.ORMExecuteState.invoke_statement`. 
    426 
    427 
    428        """ 
    429 
    430        if statement is None: 
    431            statement = self.statement 
    432 
    433        _bind_arguments = dict(self.bind_arguments) 
    434        if bind_arguments: 
    435            _bind_arguments.update(bind_arguments) 
    436        _bind_arguments["_sa_skip_events"] = True 
    437 
    438        _params: Optional[_CoreAnyExecuteParams] 
    439        if params: 
    440            if self.is_executemany: 
    441                _params = [] 
    442                exec_many_parameters = cast( 
    443                    "List[Dict[str, Any]]", self.parameters 
    444                ) 
    445                for _existing_params, _new_params in itertools.zip_longest( 
    446                    exec_many_parameters, 
    447                    cast("List[Dict[str, Any]]", params), 
    448                ): 
    449                    if _existing_params is None or _new_params is None: 
    450                        raise sa_exc.InvalidRequestError( 
    451                            f"Can't apply executemany parameters to " 
    452                            f"statement; number of parameter sets passed to " 
    453                            f"Session.execute() ({len(exec_many_parameters)}) " 
    454                            f"does not match number of parameter sets given " 
    455                            f"to ORMExecuteState.invoke_statement() " 
    456                            f"({len(params)})" 
    457                        ) 
    458                    _existing_params = dict(_existing_params) 
    459                    _existing_params.update(_new_params) 
    460                    _params.append(_existing_params) 
    461            else: 
    462                _params = dict(cast("Dict[str, Any]", self.parameters)) 
    463                _params.update(cast("Dict[str, Any]", params)) 
    464        else: 
    465            _params = self.parameters 
    466 
    467        _execution_options = self.local_execution_options 
    468        if execution_options: 
    469            _execution_options = _execution_options.union(execution_options) 
    470 
    471        return self.session._execute_internal( 
    472            statement, 
    473            _params, 
    474            execution_options=_execution_options, 
    475            bind_arguments=_bind_arguments, 
    476            _parent_execute_state=self, 
    477        ) 
    478 
    479    @property 
    480    def bind_mapper(self) -> Optional[Mapper[Any]]: 
    481        """Return the :class:`_orm.Mapper` that is the primary "bind" mapper. 
    482 
    483        For an :class:`_orm.ORMExecuteState` object invoking an ORM 
    484        statement, that is, the :attr:`_orm.ORMExecuteState.is_orm_statement` 
    485        attribute is ``True``, this attribute will return the 
    486        :class:`_orm.Mapper` that is considered to be the "primary" mapper 
    487        of the statement.   The term "bind mapper" refers to the fact that 
    488        a :class:`_orm.Session` object may be "bound" to multiple 
    489        :class:`_engine.Engine` objects keyed to mapped classes, and the 
    490        "bind mapper" determines which of those :class:`_engine.Engine` objects 
    491        would be selected. 
    492 
    493        For a statement that is invoked against a single mapped class, 
    494        :attr:`_orm.ORMExecuteState.bind_mapper` is intended to be a reliable 
    495        way of getting this mapper. 
    496 
    497        .. versionadded:: 1.4.0b2 
    498 
    499        .. seealso:: 
    500 
    501            :attr:`_orm.ORMExecuteState.all_mappers` 
    502 
    503 
    504        """ 
    505        mp: Optional[Mapper[Any]] = self.bind_arguments.get("mapper", None) 
    506        return mp 
    507 
    508    @property 
    509    def all_mappers(self) -> Sequence[Mapper[Any]]: 
    510        """Return a sequence of all :class:`_orm.Mapper` objects that are 
    511        involved at the top level of this statement. 
    512 
    513        By "top level" we mean those :class:`_orm.Mapper` objects that would 
    514        be represented in the result set rows for a :func:`_sql.select` 
    515        query, or for a :func:`_dml.update` or :func:`_dml.delete` query, 
    516        the mapper that is the main subject of the UPDATE or DELETE. 
    517 
    518        .. versionadded:: 1.4.0b2 
    519 
    520        .. seealso:: 
    521 
    522            :attr:`_orm.ORMExecuteState.bind_mapper` 
    523 
    524 
    525 
    526        """ 
    527        if not self.is_orm_statement: 
    528            return [] 
    529        elif isinstance(self.statement, (Select, FromStatement)): 
    530            result = [] 
    531            seen = set() 
    532            for d in self.statement.column_descriptions: 
    533                ent = d["entity"] 
    534                if ent: 
    535                    insp = inspect(ent, raiseerr=False) 
    536                    if insp and insp.mapper and insp.mapper not in seen: 
    537                        seen.add(insp.mapper) 
    538                        result.append(insp.mapper) 
    539            return result 
    540        elif self.statement.is_dml and self.bind_mapper: 
    541            return [self.bind_mapper] 
    542        else: 
    543            return [] 
    544 
    545    @property 
    546    def is_orm_statement(self) -> bool: 
    547        """return True if the operation is an ORM statement. 
    548 
    549        This indicates that the select(), insert(), update(), or delete() 
    550        being invoked contains ORM entities as subjects.   For a statement 
    551        that does not have ORM entities and instead refers only to 
    552        :class:`.Table` metadata, it is invoked as a Core SQL statement 
    553        and no ORM-level automation takes place. 
    554 
    555        """ 
    556        return self._compile_state_cls is not None 
    557 
    558    @property 
    559    def is_executemany(self) -> bool: 
    560        """return True if the parameters are a multi-element list of 
    561        dictionaries with more than one dictionary. 
    562 
    563        .. versionadded:: 2.0 
    564 
    565        """ 
    566        return isinstance(self.parameters, list) 
    567 
    568    @property 
    569    def is_select(self) -> bool: 
    570        """return True if this is a SELECT operation. 
    571 
    572        .. versionchanged:: 2.0.30 - the attribute is also True for a 
    573           :meth:`_sql.Select.from_statement` construct that is itself against 
    574           a :class:`_sql.Select` construct, such as 
    575           ``select(Entity).from_statement(select(..))`` 
    576 
    577        """ 
    578        return self.statement.is_select 
    579 
    580    @property 
    581    def is_from_statement(self) -> bool: 
    582        """return True if this operation is a 
    583        :meth:`_sql.Select.from_statement` operation. 
    584 
    585        This is independent from :attr:`_orm.ORMExecuteState.is_select`, as a 
    586        ``select().from_statement()`` construct can be used with 
    587        INSERT/UPDATE/DELETE RETURNING types of statements as well. 
    588        :attr:`_orm.ORMExecuteState.is_select` will only be set if the 
    589        :meth:`_sql.Select.from_statement` is itself against a 
    590        :class:`_sql.Select` construct. 
    591 
    592        .. versionadded:: 2.0.30 
    593 
    594        """ 
    595        return self.statement.is_from_statement 
    596 
    597    @property 
    598    def is_insert(self) -> bool: 
    599        """return True if this is an INSERT operation. 
    600 
    601        .. versionchanged:: 2.0.30 - the attribute is also True for a 
    602           :meth:`_sql.Select.from_statement` construct that is itself against 
    603           a :class:`_sql.Insert` construct, such as 
    604           ``select(Entity).from_statement(insert(..))`` 
    605 
    606        """ 
    607        return self.statement.is_dml and self.statement.is_insert 
    608 
    609    @property 
    610    def is_update(self) -> bool: 
    611        """return True if this is an UPDATE operation. 
    612 
    613        .. versionchanged:: 2.0.30 - the attribute is also True for a 
    614           :meth:`_sql.Select.from_statement` construct that is itself against 
    615           a :class:`_sql.Update` construct, such as 
    616           ``select(Entity).from_statement(update(..))`` 
    617 
    618        """ 
    619        return self.statement.is_dml and self.statement.is_update 
    620 
    621    @property 
    622    def is_delete(self) -> bool: 
    623        """return True if this is a DELETE operation. 
    624 
    625        .. versionchanged:: 2.0.30 - the attribute is also True for a 
    626           :meth:`_sql.Select.from_statement` construct that is itself against 
    627           a :class:`_sql.Delete` construct, such as 
    628           ``select(Entity).from_statement(delete(..))`` 
    629 
    630        """ 
    631        return self.statement.is_dml and self.statement.is_delete 
    632 
    633    @property 
    634    def _is_crud(self) -> bool: 
    635        return isinstance(self.statement, (dml.Update, dml.Delete)) 
    636 
    637    def update_execution_options(self, **opts: Any) -> None: 
    638        """Update the local execution options with new values.""" 
    639        self.local_execution_options = self.local_execution_options.union(opts) 
    640 
    641    def _orm_compile_options( 
    642        self, 
    643    ) -> Optional[ 
    644        Union[ 
    645            context._ORMCompileState.default_compile_options, 
    646            Type[context._ORMCompileState.default_compile_options], 
    647        ] 
    648    ]: 
    649        if not self.is_select: 
    650            return None 
    651        try: 
    652            opts = self.statement._compile_options 
    653        except AttributeError: 
    654            return None 
    655 
    656        if opts is not None and opts.isinstance( 
    657            context._ORMCompileState.default_compile_options 
    658        ): 
    659            return opts  # type: ignore 
    660        else: 
    661            return None 
    662 
    663    @property 
    664    def lazy_loaded_from(self) -> Optional[InstanceState[Any]]: 
    665        """An :class:`.InstanceState` that is using this statement execution 
    666        for a lazy load operation. 
    667 
    668        The primary rationale for this attribute is to support the horizontal 
    669        sharding extension, where it is available within specific query 
    670        execution time hooks created by this extension.   To that end, the 
    671        attribute is only intended to be meaningful at **query execution 
    672        time**, and importantly not any time prior to that, including query 
    673        compilation time. 
    674 
    675        """ 
    676        return self.load_options._lazy_loaded_from 
    677 
    678    @property 
    679    def loader_strategy_path(self) -> Optional[PathRegistry]: 
    680        """Return the :class:`.PathRegistry` for the current load path. 
    681 
    682        This object represents the "path" in a query along relationships 
    683        when a particular object or collection is being loaded. 
    684 
    685        """ 
    686        opts = self._orm_compile_options() 
    687        if opts is not None: 
    688            return opts._current_path 
    689        else: 
    690            return None 
    691 
    692    @property 
    693    def is_column_load(self) -> bool: 
    694        """Return True if the operation is refreshing column-oriented 
    695        attributes on an existing ORM object. 
    696 
    697        This occurs during operations such as :meth:`_orm.Session.refresh`, 
    698        as well as when an attribute deferred by :func:`_orm.defer` is 
    699        being loaded, or an attribute that was expired either directly 
    700        by :meth:`_orm.Session.expire` or via a commit operation is being 
    701        loaded. 
    702 
    703        Handlers will very likely not want to add any options to queries 
    704        when such an operation is occurring as the query should be a straight 
    705        primary key fetch which should not have any additional WHERE criteria, 
    706        and loader options travelling with the instance 
    707        will have already been added to the query. 
    708 
    709        .. versionadded:: 1.4.0b2 
    710 
    711        .. seealso:: 
    712 
    713            :attr:`_orm.ORMExecuteState.is_relationship_load` 
    714 
    715        """ 
    716        opts = self._orm_compile_options() 
    717        return opts is not None and opts._for_refresh_state 
    718 
    719    @property 
    720    def is_relationship_load(self) -> bool: 
    721        """Return True if this load is loading objects on behalf of a 
    722        relationship. 
    723 
    724        This means, the loader in effect is either a LazyLoader, 
    725        SelectInLoader, SubqueryLoader, or similar, and the entire 
    726        SELECT statement being emitted is on behalf of a relationship 
    727        load. 
    728 
    729        Handlers will very likely not want to add any options to queries 
    730        when such an operation is occurring, as loader options are already 
    731        capable of being propagated to relationship loaders and should 
    732        be already present. 
    733 
    734        .. seealso:: 
    735 
    736            :attr:`_orm.ORMExecuteState.is_column_load` 
    737 
    738        """ 
    739        opts = self._orm_compile_options() 
    740        if opts is None: 
    741            return False 
    742        path = self.loader_strategy_path 
    743        return path is not None and not path.is_root 
    744 
    745    @property 
    746    def load_options( 
    747        self, 
    748    ) -> Union[ 
    749        context.QueryContext.default_load_options, 
    750        Type[context.QueryContext.default_load_options], 
    751    ]: 
    752        """Return the load_options that will be used for this execution.""" 
    753 
    754        if not self.is_select: 
    755            raise sa_exc.InvalidRequestError( 
    756                "This ORM execution is not against a SELECT statement " 
    757                "so there are no load options." 
    758            ) 
    759 
    760        lo: Union[ 
    761            context.QueryContext.default_load_options, 
    762            Type[context.QueryContext.default_load_options], 
    763        ] = self.execution_options.get( 
    764            "_sa_orm_load_options", context.QueryContext.default_load_options 
    765        ) 
    766        return lo 
    767 
    768    @property 
    769    def update_delete_options( 
    770        self, 
    771    ) -> Union[ 
    772        bulk_persistence._BulkUDCompileState.default_update_options, 
    773        Type[bulk_persistence._BulkUDCompileState.default_update_options], 
    774    ]: 
    775        """Return the update_delete_options that will be used for this 
    776        execution.""" 
    777 
    778        if not self._is_crud: 
    779            raise sa_exc.InvalidRequestError( 
    780                "This ORM execution is not against an UPDATE or DELETE " 
    781                "statement so there are no update options." 
    782            ) 
    783        uo: Union[ 
    784            bulk_persistence._BulkUDCompileState.default_update_options, 
    785            Type[bulk_persistence._BulkUDCompileState.default_update_options], 
    786        ] = self.execution_options.get( 
    787            "_sa_orm_update_options", 
    788            bulk_persistence._BulkUDCompileState.default_update_options, 
    789        ) 
    790        return uo 
    791 
    792    @property 
    793    def _non_compile_orm_options(self) -> Sequence[ORMOption]: 
    794        return [ 
    795            opt 
    796            for opt in self.statement._with_options 
    797            if is_orm_option(opt) and not opt._is_compile_state 
    798        ] 
    799 
    800    @property 
    801    def user_defined_options(self) -> Sequence[UserDefinedOption]: 
    802        """The sequence of :class:`.UserDefinedOptions` that have been 
    803        associated with the statement being invoked. 
    804 
    805        """ 
    806        return [ 
    807            opt 
    808            for opt in self.statement._with_options 
    809            if is_user_defined_option(opt) 
    810        ] 
    811 
    812 
    813class SessionTransactionOrigin(Enum): 
    814    """indicates the origin of a :class:`.SessionTransaction`. 
    815 
    816    This enumeration is present on the 
    817    :attr:`.SessionTransaction.origin` attribute of any 
    818    :class:`.SessionTransaction` object. 
    819 
    820    .. versionadded:: 2.0 
    821 
    822    """ 
    823 
    824    AUTOBEGIN = 0 
    825    """transaction were started by autobegin""" 
    826 
    827    BEGIN = 1 
    828    """transaction were started by calling :meth:`_orm.Session.begin`""" 
    829 
    830    BEGIN_NESTED = 2 
    831    """tranaction were started by :meth:`_orm.Session.begin_nested`""" 
    832 
    833    SUBTRANSACTION = 3 
    834    """transaction is an internal "subtransaction" """ 
    835 
    836 
    837class SessionTransaction(_StateChange, TransactionalContext): 
    838    """A :class:`.Session`-level transaction. 
    839 
    840    :class:`.SessionTransaction` is produced from the 
    841    :meth:`_orm.Session.begin` 
    842    and :meth:`_orm.Session.begin_nested` methods.   It's largely an internal 
    843    object that in modern use provides a context manager for session 
    844    transactions. 
    845 
    846    Documentation on interacting with :class:`_orm.SessionTransaction` is 
    847    at: :ref:`unitofwork_transaction`. 
    848 
    849 
    850    .. versionchanged:: 1.4  The scoping and API methods to work with the 
    851       :class:`_orm.SessionTransaction` object directly have been simplified. 
    852 
    853    .. seealso:: 
    854 
    855        :ref:`unitofwork_transaction` 
    856 
    857        :meth:`.Session.begin` 
    858 
    859        :meth:`.Session.begin_nested` 
    860 
    861        :meth:`.Session.rollback` 
    862 
    863        :meth:`.Session.commit` 
    864 
    865        :meth:`.Session.in_transaction` 
    866 
    867        :meth:`.Session.in_nested_transaction` 
    868 
    869        :meth:`.Session.get_transaction` 
    870 
    871        :meth:`.Session.get_nested_transaction` 
    872 
    873 
    874    """ 
    875 
    876    _rollback_exception: Optional[BaseException] = None 
    877 
    878    _connections: Dict[ 
    879        Union[Engine, Connection], Tuple[Connection, Transaction, bool, bool] 
    880    ] 
    881    session: Session 
    882    _parent: Optional[SessionTransaction] 
    883 
    884    _state: SessionTransactionState 
    885 
    886    _new: weakref.WeakKeyDictionary[InstanceState[Any], object] 
    887    _deleted: weakref.WeakKeyDictionary[InstanceState[Any], object] 
    888    _dirty: weakref.WeakKeyDictionary[InstanceState[Any], object] 
    889    _key_switches: weakref.WeakKeyDictionary[ 
    890        InstanceState[Any], Tuple[Any, Any] 
    891    ] 
    892 
    893    origin: SessionTransactionOrigin 
    894    """Origin of this :class:`_orm.SessionTransaction`. 
    895 
    896    Refers to a :class:`.SessionTransactionOrigin` instance which is an 
    897    enumeration indicating the source event that led to constructing 
    898    this :class:`_orm.SessionTransaction`. 
    899 
    900    .. versionadded:: 2.0 
    901 
    902    """ 
    903 
    904    nested: bool = False 
    905    """Indicates if this is a nested, or SAVEPOINT, transaction. 
    906 
    907    When :attr:`.SessionTransaction.nested` is True, it is expected 
    908    that :attr:`.SessionTransaction.parent` will be present as well, 
    909    linking to the enclosing :class:`.SessionTransaction`. 
    910 
    911    .. seealso:: 
    912 
    913        :attr:`.SessionTransaction.origin` 
    914 
    915    """ 
    916 
    917    def __init__( 
    918        self, 
    919        session: Session, 
    920        origin: SessionTransactionOrigin, 
    921        parent: Optional[SessionTransaction] = None, 
    922    ): 
    923        TransactionalContext._trans_ctx_check(session) 
    924 
    925        self.session = session 
    926        self._connections = {} 
    927        self._parent = parent 
    928        self.nested = nested = origin is SessionTransactionOrigin.BEGIN_NESTED 
    929        self.origin = origin 
    930 
    931        if session._close_state is _SessionCloseState.CLOSED: 
    932            raise sa_exc.InvalidRequestError( 
    933                "This Session has been permanently closed and is unable " 
    934                "to handle any more transaction requests." 
    935            ) 
    936 
    937        if nested: 
    938            if not parent: 
    939                raise sa_exc.InvalidRequestError( 
    940                    "Can't start a SAVEPOINT transaction when no existing " 
    941                    "transaction is in progress" 
    942                ) 
    943 
    944            self._previous_nested_transaction = session._nested_transaction 
    945        elif origin is SessionTransactionOrigin.SUBTRANSACTION: 
    946            assert parent is not None 
    947        else: 
    948            assert parent is None 
    949 
    950        self._state = SessionTransactionState.ACTIVE 
    951 
    952        self._take_snapshot() 
    953 
    954        # make sure transaction is assigned before we call the 
    955        # dispatch 
    956        self.session._transaction = self 
    957 
    958        self.session.dispatch.after_transaction_create(self.session, self) 
    959 
    960    def _raise_for_prerequisite_state( 
    961        self, operation_name: str, state: _StateChangeState 
    962    ) -> NoReturn: 
    963        if state is SessionTransactionState.DEACTIVE: 
    964            if self._rollback_exception: 
    965                raise sa_exc.PendingRollbackError( 
    966                    "This Session's transaction has been rolled back " 
    967                    "due to a previous exception during flush." 
    968                    " To begin a new transaction with this Session, " 
    969                    "first issue Session.rollback()." 
    970                    f" Original exception was: {self._rollback_exception}", 
    971                    code="7s2a", 
    972                ) 
    973            else: 
    974                raise sa_exc.InvalidRequestError( 
    975                    "This session is in 'inactive' state, due to the " 
    976                    "SQL transaction being rolled back; no further SQL " 
    977                    "can be emitted within this transaction." 
    978                ) 
    979        elif state is SessionTransactionState.CLOSED: 
    980            raise sa_exc.ResourceClosedError("This transaction is closed") 
    981        elif state is SessionTransactionState.PROVISIONING_CONNECTION: 
    982            raise sa_exc.InvalidRequestError( 
    983                "This session is provisioning a new connection; concurrent " 
    984                "operations are not permitted", 
    985                code="isce", 
    986            ) 
    987        else: 
    988            raise sa_exc.InvalidRequestError( 
    989                f"This session is in '{state.name.lower()}' state; no " 
    990                "further SQL can be emitted within this transaction." 
    991            ) 
    992 
    993    @property 
    994    def parent(self) -> Optional[SessionTransaction]: 
    995        """The parent :class:`.SessionTransaction` of this 
    996        :class:`.SessionTransaction`. 
    997 
    998        If this attribute is ``None``, indicates this 
    999        :class:`.SessionTransaction` is at the top of the stack, and 
    1000        corresponds to a real "COMMIT"/"ROLLBACK" 
    1001        block.  If non-``None``, then this is either a "subtransaction" 
    1002        (an internal marker object used by the flush process) or a 
    1003        "nested" / SAVEPOINT transaction.  If the 
    1004        :attr:`.SessionTransaction.nested` attribute is ``True``, then 
    1005        this is a SAVEPOINT, and if ``False``, indicates this a subtransaction. 
    1006 
    1007        """ 
    1008        return self._parent 
    1009 
    1010    @property 
    1011    def is_active(self) -> bool: 
    1012        return ( 
    1013            self.session is not None 
    1014            and self._state is SessionTransactionState.ACTIVE 
    1015        ) 
    1016 
    1017    @property 
    1018    def _is_transaction_boundary(self) -> bool: 
    1019        return self.nested or not self._parent 
    1020 
    1021    @_StateChange.declare_states( 
    1022        (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 
    1023    ) 
    1024    def connection( 
    1025        self, 
    1026        bindkey: Optional[Mapper[Any]], 
    1027        execution_options: Optional[_ExecuteOptions] = None, 
    1028        **kwargs: Any, 
    1029    ) -> Connection: 
    1030        bind = self.session.get_bind(bindkey, **kwargs) 
    1031        return self._connection_for_bind(bind, execution_options) 
    1032 
    1033    @_StateChange.declare_states( 
    1034        (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 
    1035    ) 
    1036    def _begin(self, nested: bool = False) -> SessionTransaction: 
    1037        return SessionTransaction( 
    1038            self.session, 
    1039            ( 
    1040                SessionTransactionOrigin.BEGIN_NESTED 
    1041                if nested 
    1042                else SessionTransactionOrigin.SUBTRANSACTION 
    1043            ), 
    1044            self, 
    1045        ) 
    1046 
    1047    def _iterate_self_and_parents( 
    1048        self, upto: Optional[SessionTransaction] = None 
    1049    ) -> Iterable[SessionTransaction]: 
    1050        current = self 
    1051        result: Tuple[SessionTransaction, ...] = () 
    1052        while current: 
    1053            result += (current,) 
    1054            if current._parent is upto: 
    1055                break 
    1056            elif current._parent is None: 
    1057                raise sa_exc.InvalidRequestError( 
    1058                    "Transaction %s is not on the active transaction list" 
    1059                    % (upto) 
    1060                ) 
    1061            else: 
    1062                current = current._parent 
    1063 
    1064        return result 
    1065 
    1066    def _take_snapshot(self) -> None: 
    1067        if not self._is_transaction_boundary: 
    1068            parent = self._parent 
    1069            assert parent is not None 
    1070            self._new = parent._new 
    1071            self._deleted = parent._deleted 
    1072            self._dirty = parent._dirty 
    1073            self._key_switches = parent._key_switches 
    1074            return 
    1075 
    1076        is_begin = self.origin in ( 
    1077            SessionTransactionOrigin.BEGIN, 
    1078            SessionTransactionOrigin.AUTOBEGIN, 
    1079        ) 
    1080        if not is_begin and not self.session._flushing: 
    1081            self.session.flush() 
    1082 
    1083        self._new = weakref.WeakKeyDictionary() 
    1084        self._deleted = weakref.WeakKeyDictionary() 
    1085        self._dirty = weakref.WeakKeyDictionary() 
    1086        self._key_switches = weakref.WeakKeyDictionary() 
    1087 
    1088    def _restore_snapshot(self, dirty_only: bool = False) -> None: 
    1089        """Restore the restoration state taken before a transaction began. 
    1090 
    1091        Corresponds to a rollback. 
    1092 
    1093        """ 
    1094        assert self._is_transaction_boundary 
    1095 
    1096        to_expunge = set(self._new).union(self.session._new) 
    1097        self.session._expunge_states(to_expunge, to_transient=True) 
    1098 
    1099        for s, (oldkey, newkey) in self._key_switches.items(): 
    1100            # we probably can do this conditionally based on 
    1101            # if we expunged or not, but safe_discard does that anyway 
    1102            self.session.identity_map.safe_discard(s) 
    1103 
    1104            # restore the old key 
    1105            s.key = oldkey 
    1106 
    1107            # now restore the object, but only if we didn't expunge 
    1108            if s not in to_expunge: 
    1109                self.session.identity_map.replace(s) 
    1110 
    1111        for s in set(self._deleted).union(self.session._deleted): 
    1112            self.session._update_impl(s, revert_deletion=True) 
    1113 
    1114        assert not self.session._deleted 
    1115 
    1116        for s in self.session.identity_map.all_states(): 
    1117            if not dirty_only or s.modified or s in self._dirty: 
    1118                s._expire(s.dict, self.session.identity_map._modified) 
    1119 
    1120    def _remove_snapshot(self) -> None: 
    1121        """Remove the restoration state taken before a transaction began. 
    1122 
    1123        Corresponds to a commit. 
    1124 
    1125        """ 
    1126        assert self._is_transaction_boundary 
    1127 
    1128        if not self.nested and self.session.expire_on_commit: 
    1129            for s in self.session.identity_map.all_states(): 
    1130                s._expire(s.dict, self.session.identity_map._modified) 
    1131 
    1132            statelib.InstanceState._detach_states( 
    1133                list(self._deleted), self.session 
    1134            ) 
    1135            self._deleted.clear() 
    1136        elif self.nested: 
    1137            parent = self._parent 
    1138            assert parent is not None 
    1139            parent._new.update(self._new) 
    1140            parent._dirty.update(self._dirty) 
    1141            parent._deleted.update(self._deleted) 
    1142            parent._key_switches.update(self._key_switches) 
    1143 
    1144    @_StateChange.declare_states( 
    1145        (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 
    1146    ) 
    1147    def _connection_for_bind( 
    1148        self, 
    1149        bind: _SessionBind, 
    1150        execution_options: Optional[CoreExecuteOptionsParameter], 
    1151    ) -> Connection: 
    1152        if bind in self._connections: 
    1153            if execution_options: 
    1154                util.warn( 
    1155                    "Connection is already established for the " 
    1156                    "given bind; execution_options ignored" 
    1157                ) 
    1158            return self._connections[bind][0] 
    1159 
    1160        self._state = SessionTransactionState.PROVISIONING_CONNECTION 
    1161 
    1162        local_connect = False 
    1163        should_commit = True 
    1164 
    1165        try: 
    1166            if self._parent: 
    1167                conn = self._parent._connection_for_bind( 
    1168                    bind, execution_options 
    1169                ) 
    1170                if not self.nested: 
    1171                    return conn 
    1172            else: 
    1173                if isinstance(bind, engine.Connection): 
    1174                    conn = bind 
    1175                    if conn.engine in self._connections: 
    1176                        raise sa_exc.InvalidRequestError( 
    1177                            "Session already has a Connection associated " 
    1178                            "for the given Connection's Engine" 
    1179                        ) 
    1180                else: 
    1181                    conn = bind.connect() 
    1182                    local_connect = True 
    1183 
    1184            try: 
    1185                if execution_options: 
    1186                    conn = conn.execution_options(**execution_options) 
    1187 
    1188                transaction: Transaction 
    1189                if self.session.twophase and self._parent is None: 
    1190                    # TODO: shouldn't we only be here if not 
    1191                    # conn.in_transaction() ? 
    1192                    # if twophase is set and conn.in_transaction(), validate 
    1193                    # that it is in fact twophase. 
    1194                    transaction = conn.begin_twophase() 
    1195                elif self.nested: 
    1196                    transaction = conn.begin_nested() 
    1197                elif conn.in_transaction(): 
    1198 
    1199                    if local_connect: 
    1200                        _trans = conn.get_transaction() 
    1201                        assert _trans is not None 
    1202                        transaction = _trans 
    1203                    else: 
    1204                        join_transaction_mode = ( 
    1205                            self.session.join_transaction_mode 
    1206                        ) 
    1207 
    1208                        if join_transaction_mode == "conditional_savepoint": 
    1209                            if conn.in_nested_transaction(): 
    1210                                join_transaction_mode = "create_savepoint" 
    1211                            else: 
    1212                                join_transaction_mode = "rollback_only" 
    1213 
    1214                        if join_transaction_mode in ( 
    1215                            "control_fully", 
    1216                            "rollback_only", 
    1217                        ): 
    1218                            if conn.in_nested_transaction(): 
    1219                                transaction = ( 
    1220                                    conn._get_required_nested_transaction() 
    1221                                ) 
    1222                            else: 
    1223                                transaction = conn._get_required_transaction() 
    1224                            if join_transaction_mode == "rollback_only": 
    1225                                should_commit = False 
    1226                        elif join_transaction_mode == "create_savepoint": 
    1227                            transaction = conn.begin_nested() 
    1228                        else: 
    1229                            assert False, join_transaction_mode 
    1230                else: 
    1231                    transaction = conn.begin() 
    1232            except: 
    1233                # connection will not not be associated with this Session; 
    1234                # close it immediately so that it isn't closed under GC 
    1235                if local_connect: 
    1236                    conn.close() 
    1237                raise 
    1238            else: 
    1239                bind_is_connection = isinstance(bind, engine.Connection) 
    1240 
    1241                self._connections[conn] = self._connections[conn.engine] = ( 
    1242                    conn, 
    1243                    transaction, 
    1244                    should_commit, 
    1245                    not bind_is_connection, 
    1246                ) 
    1247                self.session.dispatch.after_begin(self.session, self, conn) 
    1248                return conn 
    1249        finally: 
    1250            self._state = SessionTransactionState.ACTIVE 
    1251 
    1252    def prepare(self) -> None: 
    1253        if self._parent is not None or not self.session.twophase: 
    1254            raise sa_exc.InvalidRequestError( 
    1255                "'twophase' mode not enabled, or not root transaction; " 
    1256                "can't prepare." 
    1257            ) 
    1258        self._prepare_impl() 
    1259 
    1260    @_StateChange.declare_states( 
    1261        (SessionTransactionState.ACTIVE,), SessionTransactionState.PREPARED 
    1262    ) 
    1263    def _prepare_impl(self) -> None: 
    1264        if self._parent is None or self.nested: 
    1265            self.session.dispatch.before_commit(self.session) 
    1266 
    1267        stx = self.session._transaction 
    1268        assert stx is not None 
    1269        if stx is not self: 
    1270            for subtransaction in stx._iterate_self_and_parents(upto=self): 
    1271                subtransaction.commit() 
    1272 
    1273        if not self.session._flushing: 
    1274            for _flush_guard in range(100): 
    1275                if self.session._is_clean(): 
    1276                    break 
    1277                self.session.flush() 
    1278            else: 
    1279                raise exc.FlushError( 
    1280                    "Over 100 subsequent flushes have occurred within " 
    1281                    "session.commit() - is an after_flush() hook " 
    1282                    "creating new objects?" 
    1283                ) 
    1284 
    1285        if self._parent is None and self.session.twophase: 
    1286            try: 
    1287                for t in set(self._connections.values()): 
    1288                    cast("TwoPhaseTransaction", t[1]).prepare() 
    1289            except: 
    1290                with util.safe_reraise(): 
    1291                    self.rollback() 
    1292 
    1293        self._state = SessionTransactionState.PREPARED 
    1294 
    1295    @_StateChange.declare_states( 
    1296        (SessionTransactionState.ACTIVE, SessionTransactionState.PREPARED), 
    1297        SessionTransactionState.CLOSED, 
    1298    ) 
    1299    def commit(self, _to_root: bool = False) -> None: 
    1300        if self._state is not SessionTransactionState.PREPARED: 
    1301            with self._expect_state(SessionTransactionState.PREPARED): 
    1302                self._prepare_impl() 
    1303 
    1304        if self._parent is None or self.nested: 
    1305            for conn, trans, should_commit, autoclose in set( 
    1306                self._connections.values() 
    1307            ): 
    1308                if should_commit: 
    1309                    trans.commit() 
    1310 
    1311            self._state = SessionTransactionState.COMMITTED 
    1312            self.session.dispatch.after_commit(self.session) 
    1313 
    1314            self._remove_snapshot() 
    1315 
    1316        with self._expect_state(SessionTransactionState.CLOSED): 
    1317            self.close() 
    1318 
    1319        if _to_root and self._parent: 
    1320            self._parent.commit(_to_root=True) 
    1321 
    1322    @_StateChange.declare_states( 
    1323        ( 
    1324            SessionTransactionState.ACTIVE, 
    1325            SessionTransactionState.DEACTIVE, 
    1326            SessionTransactionState.PREPARED, 
    1327        ), 
    1328        SessionTransactionState.CLOSED, 
    1329    ) 
    1330    def rollback( 
    1331        self, _capture_exception: bool = False, _to_root: bool = False 
    1332    ) -> None: 
    1333        stx = self.session._transaction 
    1334        assert stx is not None 
    1335        if stx is not self: 
    1336            for subtransaction in stx._iterate_self_and_parents(upto=self): 
    1337                subtransaction.close() 
    1338 
    1339        boundary = self 
    1340        rollback_err = None 
    1341        if self._state in ( 
    1342            SessionTransactionState.ACTIVE, 
    1343            SessionTransactionState.PREPARED, 
    1344        ): 
    1345            for transaction in self._iterate_self_and_parents(): 
    1346                if transaction._parent is None or transaction.nested: 
    1347                    try: 
    1348                        for t in set(transaction._connections.values()): 
    1349                            t[1].rollback() 
    1350 
    1351                        transaction._state = SessionTransactionState.DEACTIVE 
    1352                        self.session.dispatch.after_rollback(self.session) 
    1353                    except: 
    1354                        rollback_err = sys.exc_info() 
    1355                    finally: 
    1356                        transaction._state = SessionTransactionState.DEACTIVE 
    1357                        transaction._restore_snapshot( 
    1358                            dirty_only=transaction.nested 
    1359                        ) 
    1360                    boundary = transaction 
    1361                    break 
    1362                else: 
    1363                    transaction._state = SessionTransactionState.DEACTIVE 
    1364 
    1365        sess = self.session 
    1366 
    1367        if not rollback_err and not sess._is_clean(): 
    1368            # if items were added, deleted, or mutated 
    1369            # here, we need to re-restore the snapshot 
    1370            util.warn( 
    1371                "Session's state has been changed on " 
    1372                "a non-active transaction - this state " 
    1373                "will be discarded." 
    1374            ) 
    1375            boundary._restore_snapshot(dirty_only=boundary.nested) 
    1376 
    1377        with self._expect_state(SessionTransactionState.CLOSED): 
    1378            self.close() 
    1379 
    1380        if self._parent and _capture_exception: 
    1381            self._parent._rollback_exception = sys.exc_info()[1] 
    1382 
    1383        if rollback_err and rollback_err[1]: 
    1384            raise rollback_err[1].with_traceback(rollback_err[2]) 
    1385 
    1386        sess.dispatch.after_soft_rollback(sess, self) 
    1387 
    1388        if _to_root and self._parent: 
    1389            self._parent.rollback(_to_root=True) 
    1390 
    1391    @_StateChange.declare_states( 
    1392        _StateChangeStates.ANY, SessionTransactionState.CLOSED 
    1393    ) 
    1394    def close(self, invalidate: bool = False) -> None: 
    1395        if self.nested: 
    1396            self.session._nested_transaction = ( 
    1397                self._previous_nested_transaction 
    1398            ) 
    1399 
    1400        self.session._transaction = self._parent 
    1401 
    1402        for connection, transaction, should_commit, autoclose in set( 
    1403            self._connections.values() 
    1404        ): 
    1405            if invalidate and self._parent is None: 
    1406                connection.invalidate() 
    1407            if should_commit and transaction.is_active: 
    1408                transaction.close() 
    1409            if autoclose and self._parent is None: 
    1410                connection.close() 
    1411 
    1412        self._state = SessionTransactionState.CLOSED 
    1413        sess = self.session 
    1414 
    1415        # TODO: these two None sets were historically after the 
    1416        # event hook below, and in 2.0 I changed it this way for some reason, 
    1417        # and I remember there being a reason, but not what it was. 
    1418        # Why do we need to get rid of them at all?  test_memusage::CycleTest 
    1419        # passes with these commented out. 
    1420        # self.session = None  # type: ignore 
    1421        # self._connections = None  # type: ignore 
    1422 
    1423        sess.dispatch.after_transaction_end(sess, self) 
    1424 
    1425    def _get_subject(self) -> Session: 
    1426        return self.session 
    1427 
    1428    def _transaction_is_active(self) -> bool: 
    1429        return self._state is SessionTransactionState.ACTIVE 
    1430 
    1431    def _transaction_is_closed(self) -> bool: 
    1432        return self._state is SessionTransactionState.CLOSED 
    1433 
    1434    def _rollback_can_be_called(self) -> bool: 
    1435        return self._state not in (COMMITTED, CLOSED) 
    1436 
    1437 
    1438class _SessionCloseState(Enum): 
    1439    ACTIVE = 1 
    1440    CLOSED = 2 
    1441    CLOSE_IS_RESET = 3 
    1442 
    1443 
    1444class Session(_SessionClassMethods, EventTarget): 
    1445    """Manages persistence operations for ORM-mapped objects. 
    1446 
    1447    The :class:`_orm.Session` is **not safe for use in concurrent threads.**. 
    1448    See :ref:`session_faq_threadsafe` for background. 
    1449 
    1450    The Session's usage paradigm is described at :doc:`/orm/session`. 
    1451 
    1452 
    1453    """ 
    1454 
    1455    _is_asyncio = False 
    1456 
    1457    dispatch: dispatcher[Session] 
    1458 
    1459    identity_map: IdentityMap 
    1460    """A mapping of object identities to objects themselves. 
    1461 
    1462    Iterating through ``Session.identity_map.values()`` provides 
    1463    access to the full set of persistent objects (i.e., those 
    1464    that have row identity) currently in the session. 
    1465 
    1466    .. seealso:: 
    1467 
    1468        :func:`.identity_key` - helper function to produce the keys used 
    1469        in this dictionary. 
    1470 
    1471    """ 
    1472 
    1473    _new: Dict[InstanceState[Any], Any] 
    1474    _deleted: Dict[InstanceState[Any], Any] 
    1475    bind: Optional[Union[Engine, Connection]] 
    1476    __binds: Dict[_SessionBindKey, _SessionBind] 
    1477    _flushing: bool 
    1478    _warn_on_events: bool 
    1479    _transaction: Optional[SessionTransaction] 
    1480    _nested_transaction: Optional[SessionTransaction] 
    1481    hash_key: int 
    1482    autoflush: bool 
    1483    expire_on_commit: bool 
    1484    enable_baked_queries: bool 
    1485    twophase: bool 
    1486    join_transaction_mode: JoinTransactionMode 
    1487    execution_options: _ExecuteOptions = util.EMPTY_DICT 
    1488    _query_cls: Type[Query[Any]] 
    1489    _close_state: _SessionCloseState 
    1490 
    1491    def __init__( 
    1492        self, 
    1493        bind: Optional[_SessionBind] = None, 
    1494        *, 
    1495        autoflush: bool = True, 
    1496        future: Literal[True] = True, 
    1497        expire_on_commit: bool = True, 
    1498        autobegin: bool = True, 
    1499        twophase: bool = False, 
    1500        binds: Optional[Dict[_SessionBindKey, _SessionBind]] = None, 
    1501        enable_baked_queries: bool = True, 
    1502        info: Optional[_InfoType] = None, 
    1503        query_cls: Optional[Type[Query[Any]]] = None, 
    1504        autocommit: Literal[False] = False, 
    1505        join_transaction_mode: JoinTransactionMode = "conditional_savepoint", 
    1506        close_resets_only: Union[bool, _NoArg] = _NoArg.NO_ARG, 
    1507        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    1508    ): 
    1509        r"""Construct a new :class:`_orm.Session`. 
    1510 
    1511        See also the :class:`.sessionmaker` function which is used to 
    1512        generate a :class:`.Session`-producing callable with a given 
    1513        set of arguments. 
    1514 
    1515        :param autoflush: When ``True``, all query operations will issue a 
    1516           :meth:`~.Session.flush` call to this ``Session`` before proceeding. 
    1517           This is a convenience feature so that :meth:`~.Session.flush` need 
    1518           not be called repeatedly in order for database queries to retrieve 
    1519           results. 
    1520 
    1521           .. seealso:: 
    1522 
    1523               :ref:`session_flushing` - additional background on autoflush 
    1524 
    1525        :param autobegin: Automatically start transactions (i.e. equivalent to 
    1526           invoking :meth:`_orm.Session.begin`) when database access is 
    1527           requested by an operation.   Defaults to ``True``.    Set to 
    1528           ``False`` to prevent a :class:`_orm.Session` from implicitly 
    1529           beginning transactions after construction, as well as after any of 
    1530           the :meth:`_orm.Session.rollback`, :meth:`_orm.Session.commit`, 
    1531           or :meth:`_orm.Session.close` methods are called. 
    1532 
    1533           .. versionadded:: 2.0 
    1534 
    1535           .. seealso:: 
    1536 
    1537                :ref:`session_autobegin_disable` 
    1538 
    1539        :param bind: An optional :class:`_engine.Engine` or 
    1540           :class:`_engine.Connection` to 
    1541           which this ``Session`` should be bound. When specified, all SQL 
    1542           operations performed by this session will execute via this 
    1543           connectable. 
    1544 
    1545        :param binds: A dictionary which may specify any number of 
    1546           :class:`_engine.Engine` or :class:`_engine.Connection` 
    1547           objects as the source of 
    1548           connectivity for SQL operations on a per-entity basis.   The keys 
    1549           of the dictionary consist of any series of mapped classes, 
    1550           arbitrary Python classes that are bases for mapped classes, 
    1551           :class:`_schema.Table` objects and :class:`_orm.Mapper` objects. 
    1552           The 
    1553           values of the dictionary are then instances of 
    1554           :class:`_engine.Engine` 
    1555           or less commonly :class:`_engine.Connection` objects. 
    1556           Operations which 
    1557           proceed relative to a particular mapped class will consult this 
    1558           dictionary for the closest matching entity in order to determine 
    1559           which :class:`_engine.Engine` should be used for a particular SQL 
    1560           operation.    The complete heuristics for resolution are 
    1561           described at :meth:`.Session.get_bind`.  Usage looks like:: 
    1562 
    1563            Session = sessionmaker( 
    1564                binds={ 
    1565                    SomeMappedClass: create_engine("postgresql+psycopg2://engine1"), 
    1566                    SomeDeclarativeBase: create_engine( 
    1567                        "postgresql+psycopg2://engine2" 
    1568                    ), 
    1569                    some_mapper: create_engine("postgresql+psycopg2://engine3"), 
    1570                    some_table: create_engine("postgresql+psycopg2://engine4"), 
    1571                } 
    1572            ) 
    1573 
    1574           .. seealso:: 
    1575 
    1576                :ref:`session_partitioning` 
    1577 
    1578                :meth:`.Session.bind_mapper` 
    1579 
    1580                :meth:`.Session.bind_table` 
    1581 
    1582                :meth:`.Session.get_bind` 
    1583 
    1584 
    1585        :param \class_: Specify an alternate class other than 
    1586           ``sqlalchemy.orm.session.Session`` which should be used by the 
    1587           returned class. This is the only argument that is local to the 
    1588           :class:`.sessionmaker` function, and is not sent directly to the 
    1589           constructor for ``Session``. 
    1590 
    1591        :param enable_baked_queries: legacy; defaults to ``True``. 
    1592           A parameter consumed 
    1593           by the :mod:`sqlalchemy.ext.baked` extension to determine if 
    1594           "baked queries" should be cached, as is the normal operation 
    1595           of this extension.  When set to ``False``, caching as used by 
    1596           this particular extension is disabled. 
    1597 
    1598           .. versionchanged:: 1.4 The ``sqlalchemy.ext.baked`` extension is 
    1599              legacy and is not used by any of SQLAlchemy's internals. This 
    1600              flag therefore only affects applications that are making explicit 
    1601              use of this extension within their own code. 
    1602 
    1603        :param execution_options: optional dictionary of execution options 
    1604           that will be applied to all calls to :meth:`_orm.Session.execute`, 
    1605           :meth:`_orm.Session.scalars`, and similar.  Execution options 
    1606           present in statements as well as options passed to methods like 
    1607           :meth:`_orm.Session.execute` explicitly take precedence over 
    1608           the session-wide options. 
    1609 
    1610           .. versionadded:: 2.1 
    1611 
    1612        :param expire_on_commit:  Defaults to ``True``. When ``True``, all 
    1613           instances will be fully expired after each :meth:`~.commit`, 
    1614           so that all attribute/object access subsequent to a completed 
    1615           transaction will load from the most recent database state. 
    1616 
    1617            .. seealso:: 
    1618 
    1619                :ref:`session_committing` 
    1620 
    1621        :param future: Deprecated; this flag is always True. 
    1622 
    1623          .. seealso:: 
    1624 
    1625            :ref:`migration_20_toplevel` 
    1626 
    1627        :param info: optional dictionary of arbitrary data to be associated 
    1628           with this :class:`.Session`.  Is available via the 
    1629           :attr:`.Session.info` attribute.  Note the dictionary is copied at 
    1630           construction time so that modifications to the per- 
    1631           :class:`.Session` dictionary will be local to that 
    1632           :class:`.Session`. 
    1633 
    1634        :param query_cls:  Class which should be used to create new Query 
    1635          objects, as returned by the :meth:`~.Session.query` method. 
    1636          Defaults to :class:`_query.Query`. 
    1637 
    1638        :param twophase:  When ``True``, all transactions will be started as 
    1639            a "two phase" transaction, i.e. using the "two phase" semantics 
    1640            of the database in use along with an XID.  During a 
    1641            :meth:`~.commit`, after :meth:`~.flush` has been issued for all 
    1642            attached databases, the :meth:`~.TwoPhaseTransaction.prepare` 
    1643            method on each database's :class:`.TwoPhaseTransaction` will be 
    1644            called. This allows each database to roll back the entire 
    1645            transaction, before each transaction is committed. 
    1646 
    1647        :param autocommit: the "autocommit" keyword is present for backwards 
    1648            compatibility but must remain at its default value of ``False``. 
    1649 
    1650        :param join_transaction_mode: Describes the transactional behavior to 
    1651          take when a given bind is a :class:`_engine.Connection` that 
    1652          has already begun a transaction outside the scope of this 
    1653          :class:`_orm.Session`; in other words the 
    1654          :meth:`_engine.Connection.in_transaction()` method returns True. 
    1655 
    1656          The following behaviors only take effect when the :class:`_orm.Session` 
    1657          **actually makes use of the connection given**; that is, a method 
    1658          such as :meth:`_orm.Session.execute`, :meth:`_orm.Session.connection`, 
    1659          etc. are actually invoked: 
    1660 
    1661          * ``"conditional_savepoint"`` - this is the default.  if the given 
    1662            :class:`_engine.Connection` is begun within a transaction but 
    1663            does not have a SAVEPOINT, then ``"rollback_only"`` is used. 
    1664            If the :class:`_engine.Connection` is additionally within 
    1665            a SAVEPOINT, in other words 
    1666            :meth:`_engine.Connection.in_nested_transaction()` method returns 
    1667            True, then ``"create_savepoint"`` is used. 
    1668 
    1669            ``"conditional_savepoint"`` behavior attempts to make use of 
    1670            savepoints in order to keep the state of the existing transaction 
    1671            unchanged, but only if there is already a savepoint in progress; 
    1672            otherwise, it is not assumed that the backend in use has adequate 
    1673            support for SAVEPOINT, as availability of this feature varies. 
    1674            ``"conditional_savepoint"`` also seeks to establish approximate 
    1675            backwards compatibility with previous :class:`_orm.Session` 
    1676            behavior, for applications that are not setting a specific mode. It 
    1677            is recommended that one of the explicit settings be used. 
    1678 
    1679          * ``"create_savepoint"`` - the :class:`_orm.Session` will use 
    1680            :meth:`_engine.Connection.begin_nested()` in all cases to create 
    1681            its own transaction.  This transaction by its nature rides 
    1682            "on top" of any existing transaction that's opened on the given 
    1683            :class:`_engine.Connection`; if the underlying database and 
    1684            the driver in use has full, non-broken support for SAVEPOINT, the 
    1685            external transaction will remain unaffected throughout the 
    1686            lifespan of the :class:`_orm.Session`. 
    1687 
    1688            The ``"create_savepoint"`` mode is the most useful for integrating 
    1689            a :class:`_orm.Session` into a test suite where an externally 
    1690            initiated transaction should remain unaffected; however, it relies 
    1691            on proper SAVEPOINT support from the underlying driver and 
    1692            database. 
    1693 
    1694            .. tip:: When using SQLite, the SQLite driver included through 
    1695               Python 3.11 does not handle SAVEPOINTs correctly in all cases 
    1696               without workarounds. See the sections 
    1697               :ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable` 
    1698               for details on current workarounds. 
    1699 
    1700          * ``"control_fully"`` - the :class:`_orm.Session` will take 
    1701            control of the given transaction as its own; 
    1702            :meth:`_orm.Session.commit` will call ``.commit()`` on the 
    1703            transaction, :meth:`_orm.Session.rollback` will call 
    1704            ``.rollback()`` on the transaction, :meth:`_orm.Session.close` will 
    1705            call ``.rollback`` on the transaction. 
    1706 
    1707            .. tip:: This mode of use is equivalent to how SQLAlchemy 1.4 would 
    1708               handle a :class:`_engine.Connection` given with an existing 
    1709               SAVEPOINT (i.e. :meth:`_engine.Connection.begin_nested`); the 
    1710               :class:`_orm.Session` would take full control of the existing 
    1711               SAVEPOINT. 
    1712 
    1713          * ``"rollback_only"`` - the :class:`_orm.Session` will take control 
    1714            of the given transaction for ``.rollback()`` calls only; 
    1715            ``.commit()`` calls will not be propagated to the given 
    1716            transaction.  ``.close()`` calls will have no effect on the 
    1717            given transaction. 
    1718 
    1719            .. tip:: This mode of use is equivalent to how SQLAlchemy 1.4 would 
    1720               handle a :class:`_engine.Connection` given with an existing 
    1721               regular database transaction (i.e. 
    1722               :meth:`_engine.Connection.begin`); the :class:`_orm.Session` 
    1723               would propagate :meth:`_orm.Session.rollback` calls to the 
    1724               underlying transaction, but not :meth:`_orm.Session.commit` or 
    1725               :meth:`_orm.Session.close` calls. 
    1726 
    1727          .. versionadded:: 2.0.0rc1 
    1728 
    1729        :param close_resets_only: Defaults to ``True``. Determines if 
    1730          the session should reset itself after calling ``.close()`` 
    1731          or should pass in a no longer usable state, disabling re-use. 
    1732 
    1733          .. versionadded:: 2.0.22 added flag ``close_resets_only``. 
    1734            A future SQLAlchemy version may change the default value of 
    1735            this flag to ``False``. 
    1736 
    1737          .. seealso:: 
    1738 
    1739            :ref:`session_closing` - Detail on the semantics of 
    1740            :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 
    1741 
    1742        """  # noqa 
    1743 
    1744        # considering allowing the "autocommit" keyword to still be accepted 
    1745        # as long as it's False, so that external test suites, oslo.db etc 
    1746        # continue to function as the argument appears to be passed in lots 
    1747        # of cases including in our own test suite 
    1748        if autocommit: 
    1749            raise sa_exc.ArgumentError( 
    1750                "autocommit=True is no longer supported" 
    1751            ) 
    1752        self.identity_map = identity._WeakInstanceDict() 
    1753 
    1754        if not future: 
    1755            raise sa_exc.ArgumentError( 
    1756                "The 'future' parameter passed to " 
    1757                "Session() may only be set to True." 
    1758            ) 
    1759 
    1760        self._new = {}  # InstanceState->object, strong refs object 
    1761        self._deleted = {}  # same 
    1762        self.bind = bind 
    1763        self.__binds = {} 
    1764        self._flushing = False 
    1765        self._warn_on_events = False 
    1766        self._transaction = None 
    1767        self._nested_transaction = None 
    1768        self.hash_key = _new_sessionid() 
    1769        self.autobegin = autobegin 
    1770        self.autoflush = autoflush 
    1771        self.expire_on_commit = expire_on_commit 
    1772        self.enable_baked_queries = enable_baked_queries 
    1773        if execution_options: 
    1774            self.execution_options = self.execution_options.union( 
    1775                execution_options 
    1776            ) 
    1777 
    1778        # the idea is that at some point NO_ARG will warn that in the future 
    1779        # the default will switch to close_resets_only=False. 
    1780        if close_resets_only in (True, _NoArg.NO_ARG): 
    1781            self._close_state = _SessionCloseState.CLOSE_IS_RESET 
    1782        else: 
    1783            self._close_state = _SessionCloseState.ACTIVE 
    1784        if ( 
    1785            join_transaction_mode 
    1786            and join_transaction_mode 
    1787            not in JoinTransactionMode.__args__  # type: ignore 
    1788        ): 
    1789            raise sa_exc.ArgumentError( 
    1790                f"invalid selection for join_transaction_mode: " 
    1791                f'"{join_transaction_mode}"' 
    1792            ) 
    1793        self.join_transaction_mode = join_transaction_mode 
    1794 
    1795        self.twophase = twophase 
    1796        self._query_cls = query_cls if query_cls else query.Query 
    1797        if info: 
    1798            self.info.update(info) 
    1799 
    1800        if binds is not None: 
    1801            for key, bind in binds.items(): 
    1802                self._add_bind(key, bind) 
    1803 
    1804        _sessions[self.hash_key] = self 
    1805 
    1806    # used by sqlalchemy.engine.util.TransactionalContext 
    1807    _trans_context_manager: Optional[TransactionalContext] = None 
    1808 
    1809    connection_callable: Optional[_ConnectionCallableProto] = None 
    1810 
    1811    def __enter__(self: _S) -> _S: 
    1812        return self 
    1813 
    1814    def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 
    1815        self.close() 
    1816 
    1817    @contextlib.contextmanager 
    1818    def _maker_context_manager(self: _S) -> Iterator[_S]: 
    1819        with self: 
    1820            with self.begin(): 
    1821                yield self 
    1822 
    1823    def in_transaction(self) -> bool: 
    1824        """Return True if this :class:`_orm.Session` has begun a transaction. 
    1825 
    1826        .. versionadded:: 1.4 
    1827 
    1828        .. seealso:: 
    1829 
    1830            :attr:`_orm.Session.is_active` 
    1831 
    1832 
    1833        """ 
    1834        return self._transaction is not None 
    1835 
    1836    def in_nested_transaction(self) -> bool: 
    1837        """Return True if this :class:`_orm.Session` has begun a nested 
    1838        transaction, e.g. SAVEPOINT. 
    1839 
    1840        .. versionadded:: 1.4 
    1841 
    1842        """ 
    1843        return self._nested_transaction is not None 
    1844 
    1845    def get_transaction(self) -> Optional[SessionTransaction]: 
    1846        """Return the current root transaction in progress, if any. 
    1847 
    1848        .. versionadded:: 1.4 
    1849 
    1850        """ 
    1851        trans = self._transaction 
    1852        while trans is not None and trans._parent is not None: 
    1853            trans = trans._parent 
    1854        return trans 
    1855 
    1856    def get_nested_transaction(self) -> Optional[SessionTransaction]: 
    1857        """Return the current nested transaction in progress, if any. 
    1858 
    1859        .. versionadded:: 1.4 
    1860 
    1861        """ 
    1862 
    1863        return self._nested_transaction 
    1864 
    1865    @util.memoized_property 
    1866    def info(self) -> _InfoType: 
    1867        """A user-modifiable dictionary. 
    1868 
    1869        The initial value of this dictionary can be populated using the 
    1870        ``info`` argument to the :class:`.Session` constructor or 
    1871        :class:`.sessionmaker` constructor or factory methods.  The dictionary 
    1872        here is always local to this :class:`.Session` and can be modified 
    1873        independently of all other :class:`.Session` objects. 
    1874 
    1875        """ 
    1876        return {} 
    1877 
    1878    def _autobegin_t(self, begin: bool = False) -> SessionTransaction: 
    1879        if self._transaction is None: 
    1880            if not begin and not self.autobegin: 
    1881                raise sa_exc.InvalidRequestError( 
    1882                    "Autobegin is disabled on this Session; please call " 
    1883                    "session.begin() to start a new transaction" 
    1884                ) 
    1885            trans = SessionTransaction( 
    1886                self, 
    1887                ( 
    1888                    SessionTransactionOrigin.BEGIN 
    1889                    if begin 
    1890                    else SessionTransactionOrigin.AUTOBEGIN 
    1891                ), 
    1892            ) 
    1893            assert self._transaction is trans 
    1894            return trans 
    1895 
    1896        return self._transaction 
    1897 
    1898    def begin(self, nested: bool = False) -> SessionTransaction: 
    1899        """Begin a transaction, or nested transaction, 
    1900        on this :class:`.Session`, if one is not already begun. 
    1901 
    1902        The :class:`_orm.Session` object features **autobegin** behavior, 
    1903        so that normally it is not necessary to call the 
    1904        :meth:`_orm.Session.begin` 
    1905        method explicitly. However, it may be used in order to control 
    1906        the scope of when the transactional state is begun. 
    1907 
    1908        When used to begin the outermost transaction, an error is raised 
    1909        if this :class:`.Session` is already inside of a transaction. 
    1910 
    1911        :param nested: if True, begins a SAVEPOINT transaction and is 
    1912         equivalent to calling :meth:`~.Session.begin_nested`. For 
    1913         documentation on SAVEPOINT transactions, please see 
    1914         :ref:`session_begin_nested`. 
    1915 
    1916        :return: the :class:`.SessionTransaction` object.  Note that 
    1917         :class:`.SessionTransaction` 
    1918         acts as a Python context manager, allowing :meth:`.Session.begin` 
    1919         to be used in a "with" block.  See :ref:`session_explicit_begin` for 
    1920         an example. 
    1921 
    1922        .. seealso:: 
    1923 
    1924            :ref:`session_autobegin` 
    1925 
    1926            :ref:`unitofwork_transaction` 
    1927 
    1928            :meth:`.Session.begin_nested` 
    1929 
    1930 
    1931        """ 
    1932 
    1933        trans = self._transaction 
    1934        if trans is None: 
    1935            trans = self._autobegin_t(begin=True) 
    1936 
    1937            if not nested: 
    1938                return trans 
    1939 
    1940        assert trans is not None 
    1941 
    1942        if nested: 
    1943            trans = trans._begin(nested=nested) 
    1944            assert self._transaction is trans 
    1945            self._nested_transaction = trans 
    1946        else: 
    1947            raise sa_exc.InvalidRequestError( 
    1948                "A transaction is already begun on this Session." 
    1949            ) 
    1950 
    1951        return trans  # needed for __enter__/__exit__ hook 
    1952 
    1953    def begin_nested(self) -> SessionTransaction: 
    1954        """Begin a "nested" transaction on this Session, e.g. SAVEPOINT. 
    1955 
    1956        The target database(s) and associated drivers must support SQL 
    1957        SAVEPOINT for this method to function correctly. 
    1958 
    1959        For documentation on SAVEPOINT 
    1960        transactions, please see :ref:`session_begin_nested`. 
    1961 
    1962        :return: the :class:`.SessionTransaction` object.  Note that 
    1963         :class:`.SessionTransaction` acts as a context manager, allowing 
    1964         :meth:`.Session.begin_nested` to be used in a "with" block. 
    1965         See :ref:`session_begin_nested` for a usage example. 
    1966 
    1967        .. seealso:: 
    1968 
    1969            :ref:`session_begin_nested` 
    1970 
    1971            :ref:`pysqlite_serializable` - special workarounds required 
    1972            with the SQLite driver in order for SAVEPOINT to work 
    1973            correctly. For asyncio use cases, see the section 
    1974            :ref:`aiosqlite_serializable`. 
    1975 
    1976        """ 
    1977        return self.begin(nested=True) 
    1978 
    1979    def rollback(self) -> None: 
    1980        """Rollback the current transaction in progress. 
    1981 
    1982        If no transaction is in progress, this method is a pass-through. 
    1983 
    1984        The method always rolls back 
    1985        the topmost database transaction, discarding any nested 
    1986        transactions that may be in progress. 
    1987 
    1988        .. seealso:: 
    1989 
    1990            :ref:`session_rollback` 
    1991 
    1992            :ref:`unitofwork_transaction` 
    1993 
    1994        """ 
    1995        if self._transaction is None: 
    1996            pass 
    1997        else: 
    1998            self._transaction.rollback(_to_root=True) 
    1999 
    2000    def commit(self) -> None: 
    2001        """Flush pending changes and commit the current transaction. 
    2002 
    2003        When the COMMIT operation is complete, all objects are fully 
    2004        :term:`expired`, erasing their internal contents, which will be 
    2005        automatically re-loaded when the objects are next accessed. In the 
    2006        interim, these objects are in an expired state and will not function if 
    2007        they are :term:`detached` from the :class:`.Session`. Additionally, 
    2008        this re-load operation is not supported when using asyncio-oriented 
    2009        APIs. The :paramref:`.Session.expire_on_commit` parameter may be used 
    2010        to disable this behavior. 
    2011 
    2012        When there is no transaction in place for the :class:`.Session`, 
    2013        indicating that no operations were invoked on this :class:`.Session` 
    2014        since the previous call to :meth:`.Session.commit`, the method will 
    2015        begin and commit an internal-only "logical" transaction, that does not 
    2016        normally affect the database unless pending flush changes were 
    2017        detected, but will still invoke event handlers and object expiration 
    2018        rules. 
    2019 
    2020        The outermost database transaction is committed unconditionally, 
    2021        automatically releasing any SAVEPOINTs in effect. 
    2022 
    2023        .. seealso:: 
    2024 
    2025            :ref:`session_committing` 
    2026 
    2027            :ref:`unitofwork_transaction` 
    2028 
    2029            :ref:`asyncio_orm_avoid_lazyloads` 
    2030 
    2031        """ 
    2032        trans = self._transaction 
    2033        if trans is None: 
    2034            trans = self._autobegin_t() 
    2035 
    2036        trans.commit(_to_root=True) 
    2037 
    2038    def prepare(self) -> None: 
    2039        """Prepare the current transaction in progress for two phase commit. 
    2040 
    2041        If no transaction is in progress, this method raises an 
    2042        :exc:`~sqlalchemy.exc.InvalidRequestError`. 
    2043 
    2044        Only root transactions of two phase sessions can be prepared. If the 
    2045        current transaction is not such, an 
    2046        :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. 
    2047 
    2048        """ 
    2049        trans = self._transaction 
    2050        if trans is None: 
    2051            trans = self._autobegin_t() 
    2052 
    2053        trans.prepare() 
    2054 
    2055    def connection( 
    2056        self, 
    2057        bind_arguments: Optional[_BindArguments] = None, 
    2058        execution_options: Optional[CoreExecuteOptionsParameter] = None, 
    2059    ) -> Connection: 
    2060        r"""Return a :class:`_engine.Connection` object corresponding to this 
    2061        :class:`.Session` object's transactional state. 
    2062 
    2063        Either the :class:`_engine.Connection` corresponding to the current 
    2064        transaction is returned, or if no transaction is in progress, a new 
    2065        one is begun and the :class:`_engine.Connection` 
    2066        returned (note that no 
    2067        transactional state is established with the DBAPI until the first 
    2068        SQL statement is emitted). 
    2069 
    2070        Ambiguity in multi-bind or unbound :class:`.Session` objects can be 
    2071        resolved through any of the optional keyword arguments.   This 
    2072        ultimately makes usage of the :meth:`.get_bind` method for resolution. 
    2073 
    2074        :param bind_arguments: dictionary of bind arguments.  May include 
    2075         "mapper", "bind", "clause", other custom arguments that are passed 
    2076         to :meth:`.Session.get_bind`. 
    2077 
    2078        :param execution_options: a dictionary of execution options that will 
    2079         be passed to :meth:`_engine.Connection.execution_options`, **when the 
    2080         connection is first procured only**.   If the connection is already 
    2081         present within the :class:`.Session`, a warning is emitted and 
    2082         the arguments are ignored. 
    2083 
    2084         .. seealso:: 
    2085 
    2086            :ref:`session_transaction_isolation` 
    2087 
    2088        """ 
    2089 
    2090        if bind_arguments: 
    2091            bind = bind_arguments.pop("bind", None) 
    2092 
    2093            if bind is None: 
    2094                bind = self.get_bind(**bind_arguments) 
    2095        else: 
    2096            bind = self.get_bind() 
    2097 
    2098        return self._connection_for_bind( 
    2099            bind, 
    2100            execution_options=execution_options, 
    2101        ) 
    2102 
    2103    def _connection_for_bind( 
    2104        self, 
    2105        engine: _SessionBind, 
    2106        execution_options: Optional[CoreExecuteOptionsParameter] = None, 
    2107        **kw: Any, 
    2108    ) -> Connection: 
    2109        TransactionalContext._trans_ctx_check(self) 
    2110 
    2111        trans = self._transaction 
    2112        if trans is None: 
    2113            trans = self._autobegin_t() 
    2114        return trans._connection_for_bind(engine, execution_options) 
    2115 
    2116    @overload 
    2117    def _execute_internal( 
    2118        self, 
    2119        statement: Executable, 
    2120        params: Optional[_CoreSingleExecuteParams] = None, 
    2121        *, 
    2122        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2123        bind_arguments: Optional[_BindArguments] = None, 
    2124        _parent_execute_state: Optional[Any] = None, 
    2125        _add_event: Optional[Any] = None, 
    2126        _scalar_result: Literal[True] = ..., 
    2127    ) -> Any: ... 
    2128 
    2129    @overload 
    2130    def _execute_internal( 
    2131        self, 
    2132        statement: Executable, 
    2133        params: Optional[_CoreAnyExecuteParams] = None, 
    2134        *, 
    2135        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2136        bind_arguments: Optional[_BindArguments] = None, 
    2137        _parent_execute_state: Optional[Any] = None, 
    2138        _add_event: Optional[Any] = None, 
    2139        _scalar_result: bool = ..., 
    2140    ) -> Result[Unpack[TupleAny]]: ... 
    2141 
    2142    def _execute_internal( 
    2143        self, 
    2144        statement: Executable, 
    2145        params: Optional[_CoreAnyExecuteParams] = None, 
    2146        *, 
    2147        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2148        bind_arguments: Optional[_BindArguments] = None, 
    2149        _parent_execute_state: Optional[Any] = None, 
    2150        _add_event: Optional[Any] = None, 
    2151        _scalar_result: bool = False, 
    2152    ) -> Any: 
    2153        statement = coercions.expect(roles.StatementRole, statement) 
    2154 
    2155        if not bind_arguments: 
    2156            bind_arguments = {} 
    2157        else: 
    2158            bind_arguments = dict(bind_arguments) 
    2159 
    2160        if ( 
    2161            statement._propagate_attrs.get("compile_state_plugin", None) 
    2162            == "orm" 
    2163        ): 
    2164            compile_state_cls = CompileState._get_plugin_class_for_plugin( 
    2165                statement, "orm" 
    2166            ) 
    2167            if TYPE_CHECKING: 
    2168                assert isinstance( 
    2169                    compile_state_cls, context._AbstractORMCompileState 
    2170                ) 
    2171        else: 
    2172            compile_state_cls = None 
    2173            bind_arguments.setdefault("clause", statement) 
    2174 
    2175        combined_execution_options: util.immutabledict[str, Any] = ( 
    2176            util.coerce_to_immutabledict(execution_options) 
    2177        ) 
    2178        if self.execution_options: 
    2179            # merge given execution options with session-wide execution 
    2180            # options.  if the statement also has execution_options, 
    2181            # maintain priority of session.execution_options -> 
    2182            # statement.execution_options -> method passed execution_options 
    2183            # by omitting from the base execution options those keys that 
    2184            # will come from the statement 
    2185            if statement._execution_options: 
    2186                combined_execution_options = util.immutabledict( 
    2187                    { 
    2188                        k: v 
    2189                        for k, v in self.execution_options.items() 
    2190                        if k not in statement._execution_options 
    2191                    } 
    2192                ).union(combined_execution_options) 
    2193            else: 
    2194                combined_execution_options = self.execution_options.union( 
    2195                    combined_execution_options 
    2196                ) 
    2197 
    2198        if _parent_execute_state: 
    2199            events_todo = _parent_execute_state._remaining_events() 
    2200        else: 
    2201            events_todo = self.dispatch.do_orm_execute 
    2202            if _add_event: 
    2203                events_todo = list(events_todo) + [_add_event] 
    2204 
    2205        if events_todo: 
    2206            if compile_state_cls is not None: 
    2207                # for event handlers, do the orm_pre_session_exec 
    2208                # pass ahead of the event handlers, so that things like 
    2209                # .load_options, .update_delete_options etc. are populated. 
    2210                # is_pre_event=True allows the hook to hold off on things 
    2211                # it doesn't want to do twice, including autoflush as well 
    2212                # as "pre fetch" for DML, etc. 
    2213                ( 
    2214                    statement, 
    2215                    combined_execution_options, 
    2216                ) = compile_state_cls.orm_pre_session_exec( 
    2217                    self, 
    2218                    statement, 
    2219                    params, 
    2220                    combined_execution_options, 
    2221                    bind_arguments, 
    2222                    True, 
    2223                ) 
    2224 
    2225            orm_exec_state = ORMExecuteState( 
    2226                self, 
    2227                statement, 
    2228                params, 
    2229                combined_execution_options, 
    2230                bind_arguments, 
    2231                compile_state_cls, 
    2232                events_todo, 
    2233            ) 
    2234            for idx, fn in enumerate(events_todo): 
    2235                orm_exec_state._starting_event_idx = idx 
    2236                fn_result: Optional[Result[Unpack[TupleAny]]] = fn( 
    2237                    orm_exec_state 
    2238                ) 
    2239                if fn_result: 
    2240                    if _scalar_result: 
    2241                        return fn_result.scalar() 
    2242                    else: 
    2243                        return fn_result 
    2244 
    2245            statement = orm_exec_state.statement 
    2246            combined_execution_options = orm_exec_state.local_execution_options 
    2247 
    2248        if compile_state_cls is not None: 
    2249            # now run orm_pre_session_exec() "for real".   if there were 
    2250            # event hooks, this will re-run the steps that interpret 
    2251            # new execution_options into load_options / update_delete_options, 
    2252            # which we assume the event hook might have updated. 
    2253            # autoflush will also be invoked in this step if enabled. 
    2254            ( 
    2255                statement, 
    2256                combined_execution_options, 
    2257            ) = compile_state_cls.orm_pre_session_exec( 
    2258                self, 
    2259                statement, 
    2260                params, 
    2261                combined_execution_options, 
    2262                bind_arguments, 
    2263                False, 
    2264            ) 
    2265        else: 
    2266            # Issue #9809: unconditionally autoflush for Core statements 
    2267            self._autoflush() 
    2268 
    2269        bind = self.get_bind(**bind_arguments) 
    2270 
    2271        conn = self._connection_for_bind(bind) 
    2272 
    2273        if _scalar_result and not compile_state_cls: 
    2274            if TYPE_CHECKING: 
    2275                params = cast(_CoreSingleExecuteParams, params) 
    2276            return conn.scalar( 
    2277                statement, 
    2278                params or {}, 
    2279                execution_options=combined_execution_options, 
    2280            ) 
    2281 
    2282        if compile_state_cls: 
    2283            result: Result[Unpack[TupleAny]] = ( 
    2284                compile_state_cls.orm_execute_statement( 
    2285                    self, 
    2286                    statement, 
    2287                    params or {}, 
    2288                    combined_execution_options, 
    2289                    bind_arguments, 
    2290                    conn, 
    2291                ) 
    2292            ) 
    2293        else: 
    2294            result = conn.execute( 
    2295                statement, params, execution_options=combined_execution_options 
    2296            ) 
    2297 
    2298        if _scalar_result: 
    2299            return result.scalar() 
    2300        else: 
    2301            return result 
    2302 
    2303    @overload 
    2304    def execute( 
    2305        self, 
    2306        statement: TypedReturnsRows[Unpack[_Ts]], 
    2307        params: Optional[_CoreAnyExecuteParams] = None, 
    2308        *, 
    2309        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2310        bind_arguments: Optional[_BindArguments] = None, 
    2311        _parent_execute_state: Optional[Any] = None, 
    2312        _add_event: Optional[Any] = None, 
    2313    ) -> Result[Unpack[_Ts]]: ... 
    2314 
    2315    @overload 
    2316    def execute( 
    2317        self, 
    2318        statement: Executable, 
    2319        params: Optional[_CoreAnyExecuteParams] = None, 
    2320        *, 
    2321        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2322        bind_arguments: Optional[_BindArguments] = None, 
    2323        _parent_execute_state: Optional[Any] = None, 
    2324        _add_event: Optional[Any] = None, 
    2325    ) -> Result[Unpack[TupleAny]]: ... 
    2326 
    2327    def execute( 
    2328        self, 
    2329        statement: Executable, 
    2330        params: Optional[_CoreAnyExecuteParams] = None, 
    2331        *, 
    2332        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2333        bind_arguments: Optional[_BindArguments] = None, 
    2334        _parent_execute_state: Optional[Any] = None, 
    2335        _add_event: Optional[Any] = None, 
    2336    ) -> Result[Unpack[TupleAny]]: 
    2337        r"""Execute a SQL expression construct. 
    2338 
    2339        Returns a :class:`_engine.Result` object representing 
    2340        results of the statement execution. 
    2341 
    2342        E.g.:: 
    2343 
    2344            from sqlalchemy import select 
    2345 
    2346            result = session.execute(select(User).where(User.id == 5)) 
    2347 
    2348        The API contract of :meth:`_orm.Session.execute` is similar to that 
    2349        of :meth:`_engine.Connection.execute`, the :term:`2.0 style` version 
    2350        of :class:`_engine.Connection`. 
    2351 
    2352        .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is 
    2353           now the primary point of ORM statement execution when using 
    2354           :term:`2.0 style` ORM usage. 
    2355 
    2356        :param statement: 
    2357            An executable statement (i.e. an :class:`.Executable` expression 
    2358            such as :func:`_expression.select`). 
    2359 
    2360        :param params: 
    2361            Optional dictionary, or list of dictionaries, containing 
    2362            bound parameter values.   If a single dictionary, single-row 
    2363            execution occurs; if a list of dictionaries, an 
    2364            "executemany" will be invoked.  The keys in each dictionary 
    2365            must correspond to parameter names present in the statement. 
    2366 
    2367        :param execution_options: optional dictionary of execution options, 
    2368         which will be associated with the statement execution.  This 
    2369         dictionary can provide a subset of the options that are accepted 
    2370         by :meth:`_engine.Connection.execution_options`, and may also 
    2371         provide additional options understood only in an ORM context. 
    2372 
    2373         The execution_options are passed along to methods like 
    2374         :meth:`.Connection.execute` on :class:`.Connection` giving the 
    2375         highest priority to execution_options that are passed to this 
    2376         method explicitly, then the options that are present on the 
    2377         statement object if any, and finally those options present 
    2378         session-wide. 
    2379 
    2380         .. seealso:: 
    2381 
    2382            :ref:`orm_queryguide_execution_options` - ORM-specific execution 
    2383            options 
    2384 
    2385        :param bind_arguments: dictionary of additional arguments to determine 
    2386         the bind.  May include "mapper", "bind", or other custom arguments. 
    2387         Contents of this dictionary are passed to the 
    2388         :meth:`.Session.get_bind` method. 
    2389 
    2390        :return: a :class:`_engine.Result` object. 
    2391 
    2392 
    2393        """ 
    2394        return self._execute_internal( 
    2395            statement, 
    2396            params, 
    2397            execution_options=execution_options, 
    2398            bind_arguments=bind_arguments, 
    2399            _parent_execute_state=_parent_execute_state, 
    2400            _add_event=_add_event, 
    2401        ) 
    2402 
    2403    @overload 
    2404    def scalar( 
    2405        self, 
    2406        statement: TypedReturnsRows[_T], 
    2407        params: Optional[_CoreSingleExecuteParams] = None, 
    2408        *, 
    2409        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2410        bind_arguments: Optional[_BindArguments] = None, 
    2411        **kw: Any, 
    2412    ) -> Optional[_T]: ... 
    2413 
    2414    @overload 
    2415    def scalar( 
    2416        self, 
    2417        statement: Executable, 
    2418        params: Optional[_CoreSingleExecuteParams] = None, 
    2419        *, 
    2420        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2421        bind_arguments: Optional[_BindArguments] = None, 
    2422        **kw: Any, 
    2423    ) -> Any: ... 
    2424 
    2425    def scalar( 
    2426        self, 
    2427        statement: Executable, 
    2428        params: Optional[_CoreSingleExecuteParams] = None, 
    2429        *, 
    2430        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2431        bind_arguments: Optional[_BindArguments] = None, 
    2432        **kw: Any, 
    2433    ) -> Any: 
    2434        """Execute a statement and return a scalar result. 
    2435 
    2436        Usage and parameters are the same as that of 
    2437        :meth:`_orm.Session.execute`; the return result is a scalar Python 
    2438        value. 
    2439 
    2440        """ 
    2441 
    2442        return self._execute_internal( 
    2443            statement, 
    2444            params, 
    2445            execution_options=execution_options, 
    2446            bind_arguments=bind_arguments, 
    2447            _scalar_result=True, 
    2448            **kw, 
    2449        ) 
    2450 
    2451    @overload 
    2452    def scalars( 
    2453        self, 
    2454        statement: TypedReturnsRows[_T], 
    2455        params: Optional[_CoreAnyExecuteParams] = None, 
    2456        *, 
    2457        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2458        bind_arguments: Optional[_BindArguments] = None, 
    2459        **kw: Any, 
    2460    ) -> ScalarResult[_T]: ... 
    2461 
    2462    @overload 
    2463    def scalars( 
    2464        self, 
    2465        statement: Executable, 
    2466        params: Optional[_CoreAnyExecuteParams] = None, 
    2467        *, 
    2468        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2469        bind_arguments: Optional[_BindArguments] = None, 
    2470        **kw: Any, 
    2471    ) -> ScalarResult[Any]: ... 
    2472 
    2473    def scalars( 
    2474        self, 
    2475        statement: Executable, 
    2476        params: Optional[_CoreAnyExecuteParams] = None, 
    2477        *, 
    2478        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    2479        bind_arguments: Optional[_BindArguments] = None, 
    2480        **kw: Any, 
    2481    ) -> ScalarResult[Any]: 
    2482        """Execute a statement and return the results as scalars. 
    2483 
    2484        Usage and parameters are the same as that of 
    2485        :meth:`_orm.Session.execute`; the return result is a 
    2486        :class:`_result.ScalarResult` filtering object which 
    2487        will return single elements rather than :class:`_row.Row` objects. 
    2488 
    2489        :return:  a :class:`_result.ScalarResult` object 
    2490 
    2491        .. versionadded:: 1.4.24 Added :meth:`_orm.Session.scalars` 
    2492 
    2493        .. versionadded:: 1.4.26 Added :meth:`_orm.scoped_session.scalars` 
    2494 
    2495        .. seealso:: 
    2496 
    2497            :ref:`orm_queryguide_select_orm_entities` - contrasts the behavior 
    2498            of :meth:`_orm.Session.execute` to :meth:`_orm.Session.scalars` 
    2499 
    2500        """ 
    2501 
    2502        return self._execute_internal( 
    2503            statement, 
    2504            params=params, 
    2505            execution_options=execution_options, 
    2506            bind_arguments=bind_arguments, 
    2507            _scalar_result=False,  # mypy appreciates this 
    2508            **kw, 
    2509        ).scalars() 
    2510 
    2511    def close(self) -> None: 
    2512        """Close out the transactional resources and ORM objects used by this 
    2513        :class:`_orm.Session`. 
    2514 
    2515        This expunges all ORM objects associated with this 
    2516        :class:`_orm.Session`, ends any transaction in progress and 
    2517        :term:`releases` any :class:`_engine.Connection` objects which this 
    2518        :class:`_orm.Session` itself has checked out from associated 
    2519        :class:`_engine.Engine` objects. The operation then leaves the 
    2520        :class:`_orm.Session` in a state which it may be used again. 
    2521 
    2522        .. tip:: 
    2523 
    2524            In the default running mode the :meth:`_orm.Session.close` 
    2525            method **does not prevent the Session from being used again**. 
    2526            The :class:`_orm.Session` itself does not actually have a 
    2527            distinct "closed" state; it merely means 
    2528            the :class:`_orm.Session` will release all database connections 
    2529            and ORM objects. 
    2530 
    2531            Setting the parameter :paramref:`_orm.Session.close_resets_only` 
    2532            to ``False`` will instead make the ``close`` final, meaning that 
    2533            any further action on the session will be forbidden. 
    2534 
    2535        .. versionchanged:: 1.4  The :meth:`.Session.close` method does not 
    2536           immediately create a new :class:`.SessionTransaction` object; 
    2537           instead, the new :class:`.SessionTransaction` is created only if 
    2538           the :class:`.Session` is used again for a database operation. 
    2539 
    2540        .. seealso:: 
    2541 
    2542            :ref:`session_closing` - detail on the semantics of 
    2543            :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 
    2544 
    2545            :meth:`_orm.Session.reset` - a similar method that behaves like 
    2546            ``close()`` with  the parameter 
    2547            :paramref:`_orm.Session.close_resets_only` set to ``True``. 
    2548 
    2549        """ 
    2550        self._close_impl(invalidate=False) 
    2551 
    2552    def reset(self) -> None: 
    2553        """Close out the transactional resources and ORM objects used by this 
    2554        :class:`_orm.Session`, resetting the session to its initial state. 
    2555 
    2556        This method provides for same "reset-only" behavior that the 
    2557        :meth:`_orm.Session.close` method has provided historically, where the 
    2558        state of the :class:`_orm.Session` is reset as though the object were 
    2559        brand new, and ready to be used again. 
    2560        This method may then be useful for :class:`_orm.Session` objects 
    2561        which set :paramref:`_orm.Session.close_resets_only` to ``False``, 
    2562        so that "reset only" behavior is still available. 
    2563 
    2564        .. versionadded:: 2.0.22 
    2565 
    2566        .. seealso:: 
    2567 
    2568            :ref:`session_closing` - detail on the semantics of 
    2569            :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 
    2570 
    2571            :meth:`_orm.Session.close` - a similar method will additionally 
    2572            prevent re-use of the Session when the parameter 
    2573            :paramref:`_orm.Session.close_resets_only` is set to ``False``. 
    2574        """ 
    2575        self._close_impl(invalidate=False, is_reset=True) 
    2576 
    2577    def invalidate(self) -> None: 
    2578        """Close this Session, using connection invalidation. 
    2579 
    2580        This is a variant of :meth:`.Session.close` that will additionally 
    2581        ensure that the :meth:`_engine.Connection.invalidate` 
    2582        method will be called on each :class:`_engine.Connection` object 
    2583        that is currently in use for a transaction (typically there is only 
    2584        one connection unless the :class:`_orm.Session` is used with 
    2585        multiple engines). 
    2586 
    2587        This can be called when the database is known to be in a state where 
    2588        the connections are no longer safe to be used. 
    2589 
    2590        Below illustrates a scenario when using `gevent 
    2591        <https://www.gevent.org/>`_, which can produce ``Timeout`` exceptions 
    2592        that may mean the underlying connection should be discarded:: 
    2593 
    2594            import gevent 
    2595 
    2596            try: 
    2597                sess = Session() 
    2598                sess.add(User()) 
    2599                sess.commit() 
    2600            except gevent.Timeout: 
    2601                sess.invalidate() 
    2602                raise 
    2603            except: 
    2604                sess.rollback() 
    2605                raise 
    2606 
    2607        The method additionally does everything that :meth:`_orm.Session.close` 
    2608        does, including that all ORM objects are expunged. 
    2609 
    2610        """ 
    2611        self._close_impl(invalidate=True) 
    2612 
    2613    def _close_impl(self, invalidate: bool, is_reset: bool = False) -> None: 
    2614        if not is_reset and self._close_state is _SessionCloseState.ACTIVE: 
    2615            self._close_state = _SessionCloseState.CLOSED 
    2616        self.expunge_all() 
    2617        if self._transaction is not None: 
    2618            for transaction in self._transaction._iterate_self_and_parents(): 
    2619                transaction.close(invalidate) 
    2620 
    2621    def expunge_all(self) -> None: 
    2622        """Remove all object instances from this ``Session``. 
    2623 
    2624        This is equivalent to calling ``expunge(obj)`` on all objects in this 
    2625        ``Session``. 
    2626 
    2627        """ 
    2628 
    2629        all_states = self.identity_map.all_states() + list(self._new) 
    2630        self.identity_map._kill() 
    2631        self.identity_map = identity._WeakInstanceDict() 
    2632        self._new = {} 
    2633        self._deleted = {} 
    2634 
    2635        statelib.InstanceState._detach_states(all_states, self) 
    2636 
    2637    def _add_bind(self, key: _SessionBindKey, bind: _SessionBind) -> None: 
    2638        try: 
    2639            insp = inspect(key) 
    2640        except sa_exc.NoInspectionAvailable as err: 
    2641            if not isinstance(key, type): 
    2642                raise sa_exc.ArgumentError( 
    2643                    "Not an acceptable bind target: %s" % key 
    2644                ) from err 
    2645            else: 
    2646                self.__binds[key] = bind 
    2647        else: 
    2648            if TYPE_CHECKING: 
    2649                assert isinstance(insp, Inspectable) 
    2650 
    2651            if isinstance(insp, TableClause): 
    2652                self.__binds[insp] = bind 
    2653            elif insp_is_mapper(insp): 
    2654                self.__binds[insp.class_] = bind 
    2655                for _selectable in insp._all_tables: 
    2656                    self.__binds[_selectable] = bind 
    2657            else: 
    2658                raise sa_exc.ArgumentError( 
    2659                    "Not an acceptable bind target: %s" % key 
    2660                ) 
    2661 
    2662    def bind_mapper( 
    2663        self, mapper: _EntityBindKey[_O], bind: _SessionBind 
    2664    ) -> None: 
    2665        """Associate a :class:`_orm.Mapper` or arbitrary Python class with a 
    2666        "bind", e.g. an :class:`_engine.Engine` or 
    2667        :class:`_engine.Connection`. 
    2668 
    2669        The given entity is added to a lookup used by the 
    2670        :meth:`.Session.get_bind` method. 
    2671 
    2672        :param mapper: a :class:`_orm.Mapper` object, 
    2673         or an instance of a mapped 
    2674         class, or any Python class that is the base of a set of mapped 
    2675         classes. 
    2676 
    2677        :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 
    2678                    object. 
    2679 
    2680        .. seealso:: 
    2681 
    2682            :ref:`session_partitioning` 
    2683 
    2684            :paramref:`.Session.binds` 
    2685 
    2686            :meth:`.Session.bind_table` 
    2687 
    2688 
    2689        """ 
    2690        self._add_bind(mapper, bind) 
    2691 
    2692    def bind_table(self, table: TableClause, bind: _SessionBind) -> None: 
    2693        """Associate a :class:`_schema.Table` with a "bind", e.g. an 
    2694        :class:`_engine.Engine` 
    2695        or :class:`_engine.Connection`. 
    2696 
    2697        The given :class:`_schema.Table` is added to a lookup used by the 
    2698        :meth:`.Session.get_bind` method. 
    2699 
    2700        :param table: a :class:`_schema.Table` object, 
    2701         which is typically the target 
    2702         of an ORM mapping, or is present within a selectable that is 
    2703         mapped. 
    2704 
    2705        :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 
    2706         object. 
    2707 
    2708        .. seealso:: 
    2709 
    2710            :ref:`session_partitioning` 
    2711 
    2712            :paramref:`.Session.binds` 
    2713 
    2714            :meth:`.Session.bind_mapper` 
    2715 
    2716 
    2717        """ 
    2718        self._add_bind(table, bind) 
    2719 
    2720    def get_bind( 
    2721        self, 
    2722        mapper: Optional[_EntityBindKey[_O]] = None, 
    2723        *, 
    2724        clause: Optional[ClauseElement] = None, 
    2725        bind: Optional[_SessionBind] = None, 
    2726        _sa_skip_events: Optional[bool] = None, 
    2727        _sa_skip_for_implicit_returning: bool = False, 
    2728        **kw: Any, 
    2729    ) -> Union[Engine, Connection]: 
    2730        """Return a "bind" to which this :class:`.Session` is bound. 
    2731 
    2732        The "bind" is usually an instance of :class:`_engine.Engine`, 
    2733        except in the case where the :class:`.Session` has been 
    2734        explicitly bound directly to a :class:`_engine.Connection`. 
    2735 
    2736        For a multiply-bound or unbound :class:`.Session`, the 
    2737        ``mapper`` or ``clause`` arguments are used to determine the 
    2738        appropriate bind to return. 
    2739 
    2740        Note that the "mapper" argument is usually present 
    2741        when :meth:`.Session.get_bind` is called via an ORM 
    2742        operation such as a :meth:`.Session.query`, each 
    2743        individual INSERT/UPDATE/DELETE operation within a 
    2744        :meth:`.Session.flush`, call, etc. 
    2745 
    2746        The order of resolution is: 
    2747 
    2748        1. if mapper given and :paramref:`.Session.binds` is present, 
    2749           locate a bind based first on the mapper in use, then 
    2750           on the mapped class in use, then on any base classes that are 
    2751           present in the ``__mro__`` of the mapped class, from more specific 
    2752           superclasses to more general. 
    2753        2. if clause given and ``Session.binds`` is present, 
    2754           locate a bind based on :class:`_schema.Table` objects 
    2755           found in the given clause present in ``Session.binds``. 
    2756        3. if ``Session.binds`` is present, return that. 
    2757        4. if clause given, attempt to return a bind 
    2758           linked to the :class:`_schema.MetaData` ultimately 
    2759           associated with the clause. 
    2760        5. if mapper given, attempt to return a bind 
    2761           linked to the :class:`_schema.MetaData` ultimately 
    2762           associated with the :class:`_schema.Table` or other 
    2763           selectable to which the mapper is mapped. 
    2764        6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError` 
    2765           is raised. 
    2766 
    2767        Note that the :meth:`.Session.get_bind` method can be overridden on 
    2768        a user-defined subclass of :class:`.Session` to provide any kind 
    2769        of bind resolution scheme.  See the example at 
    2770        :ref:`session_custom_partitioning`. 
    2771 
    2772        :param mapper: 
    2773          Optional mapped class or corresponding :class:`_orm.Mapper` instance. 
    2774          The bind can be derived from a :class:`_orm.Mapper` first by 
    2775          consulting the "binds" map associated with this :class:`.Session`, 
    2776          and secondly by consulting the :class:`_schema.MetaData` associated 
    2777          with the :class:`_schema.Table` to which the :class:`_orm.Mapper` is 
    2778          mapped for a bind. 
    2779 
    2780        :param clause: 
    2781            A :class:`_expression.ClauseElement` (i.e. 
    2782            :func:`_expression.select`, 
    2783            :func:`_expression.text`, 
    2784            etc.).  If the ``mapper`` argument is not present or could not 
    2785            produce a bind, the given expression construct will be searched 
    2786            for a bound element, typically a :class:`_schema.Table` 
    2787            associated with 
    2788            bound :class:`_schema.MetaData`. 
    2789 
    2790        .. seealso:: 
    2791 
    2792             :ref:`session_partitioning` 
    2793 
    2794             :paramref:`.Session.binds` 
    2795 
    2796             :meth:`.Session.bind_mapper` 
    2797 
    2798             :meth:`.Session.bind_table` 
    2799 
    2800        """ 
    2801 
    2802        # this function is documented as a subclassing hook, so we have 
    2803        # to call this method even if the return is simple 
    2804        if bind: 
    2805            return bind 
    2806        elif not self.__binds and self.bind: 
    2807            # simplest and most common case, we have a bind and no 
    2808            # per-mapper/table binds, we're done 
    2809            return self.bind 
    2810 
    2811        # we don't have self.bind and either have self.__binds 
    2812        # or we don't have self.__binds (which is legacy).  Look at the 
    2813        # mapper and the clause 
    2814        if mapper is None and clause is None: 
    2815            if self.bind: 
    2816                return self.bind 
    2817            else: 
    2818                raise sa_exc.UnboundExecutionError( 
    2819                    "This session is not bound to a single Engine or " 
    2820                    "Connection, and no context was provided to locate " 
    2821                    "a binding." 
    2822                ) 
    2823 
    2824        # look more closely at the mapper. 
    2825        if mapper is not None: 
    2826            try: 
    2827                inspected_mapper = inspect(mapper) 
    2828            except sa_exc.NoInspectionAvailable as err: 
    2829                if isinstance(mapper, type): 
    2830                    raise exc.UnmappedClassError(mapper) from err 
    2831                else: 
    2832                    raise 
    2833        else: 
    2834            inspected_mapper = None 
    2835 
    2836        # match up the mapper or clause in the __binds 
    2837        if self.__binds: 
    2838            # matching mappers and selectables to entries in the 
    2839            # binds dictionary; supported use case. 
    2840            if inspected_mapper: 
    2841                for cls in inspected_mapper.class_.__mro__: 
    2842                    if cls in self.__binds: 
    2843                        return self.__binds[cls] 
    2844                if clause is None: 
    2845                    clause = inspected_mapper.persist_selectable 
    2846 
    2847            if clause is not None: 
    2848                plugin_subject = clause._propagate_attrs.get( 
    2849                    "plugin_subject", None 
    2850                ) 
    2851 
    2852                if plugin_subject is not None: 
    2853                    for cls in plugin_subject.mapper.class_.__mro__: 
    2854                        if cls in self.__binds: 
    2855                            return self.__binds[cls] 
    2856 
    2857                for obj in visitors.iterate(clause): 
    2858                    if obj in self.__binds: 
    2859                        if TYPE_CHECKING: 
    2860                            assert isinstance(obj, Table) 
    2861                        return self.__binds[obj] 
    2862 
    2863        # none of the __binds matched, but we have a fallback bind. 
    2864        # return that 
    2865        if self.bind: 
    2866            return self.bind 
    2867 
    2868        context = [] 
    2869        if inspected_mapper is not None: 
    2870            context.append(f"mapper {inspected_mapper}") 
    2871        if clause is not None: 
    2872            context.append("SQL expression") 
    2873 
    2874        raise sa_exc.UnboundExecutionError( 
    2875            f"Could not locate a bind configured on " 
    2876            f'{", ".join(context)} or this Session.' 
    2877        ) 
    2878 
    2879    @overload 
    2880    def query(self, _entity: _EntityType[_O]) -> Query[_O]: ... 
    2881 
    2882    @overload 
    2883    def query( 
    2884        self, _colexpr: TypedColumnsClauseRole[_T] 
    2885    ) -> RowReturningQuery[_T]: ... 
    2886 
    2887    # START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8 
    2888 
    2889    # code within this block is **programmatically, 
    2890    # statically generated** by tools/generate_tuple_map_overloads.py 
    2891 
    2892    @overload 
    2893    def query( 
    2894        self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], / 
    2895    ) -> RowReturningQuery[_T0, _T1]: ... 
    2896 
    2897    @overload 
    2898    def query( 
    2899        self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], / 
    2900    ) -> RowReturningQuery[_T0, _T1, _T2]: ... 
    2901 
    2902    @overload 
    2903    def query( 
    2904        self, 
    2905        __ent0: _TCCA[_T0], 
    2906        __ent1: _TCCA[_T1], 
    2907        __ent2: _TCCA[_T2], 
    2908        __ent3: _TCCA[_T3], 
    2909        /, 
    2910    ) -> RowReturningQuery[_T0, _T1, _T2, _T3]: ... 
    2911 
    2912    @overload 
    2913    def query( 
    2914        self, 
    2915        __ent0: _TCCA[_T0], 
    2916        __ent1: _TCCA[_T1], 
    2917        __ent2: _TCCA[_T2], 
    2918        __ent3: _TCCA[_T3], 
    2919        __ent4: _TCCA[_T4], 
    2920        /, 
    2921    ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4]: ... 
    2922 
    2923    @overload 
    2924    def query( 
    2925        self, 
    2926        __ent0: _TCCA[_T0], 
    2927        __ent1: _TCCA[_T1], 
    2928        __ent2: _TCCA[_T2], 
    2929        __ent3: _TCCA[_T3], 
    2930        __ent4: _TCCA[_T4], 
    2931        __ent5: _TCCA[_T5], 
    2932        /, 
    2933    ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5]: ... 
    2934 
    2935    @overload 
    2936    def query( 
    2937        self, 
    2938        __ent0: _TCCA[_T0], 
    2939        __ent1: _TCCA[_T1], 
    2940        __ent2: _TCCA[_T2], 
    2941        __ent3: _TCCA[_T3], 
    2942        __ent4: _TCCA[_T4], 
    2943        __ent5: _TCCA[_T5], 
    2944        __ent6: _TCCA[_T6], 
    2945        /, 
    2946    ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5, _T6]: ... 
    2947 
    2948    @overload 
    2949    def query( 
    2950        self, 
    2951        __ent0: _TCCA[_T0], 
    2952        __ent1: _TCCA[_T1], 
    2953        __ent2: _TCCA[_T2], 
    2954        __ent3: _TCCA[_T3], 
    2955        __ent4: _TCCA[_T4], 
    2956        __ent5: _TCCA[_T5], 
    2957        __ent6: _TCCA[_T6], 
    2958        __ent7: _TCCA[_T7], 
    2959        /, 
    2960        *entities: _ColumnsClauseArgument[Any], 
    2961    ) -> RowReturningQuery[ 
    2962        _T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, Unpack[TupleAny] 
    2963    ]: ... 
    2964 
    2965    # END OVERLOADED FUNCTIONS self.query 
    2966 
    2967    @overload 
    2968    def query( 
    2969        self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any 
    2970    ) -> Query[Any]: ... 
    2971 
    2972    def query( 
    2973        self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any 
    2974    ) -> Query[Any]: 
    2975        """Return a new :class:`_query.Query` object corresponding to this 
    2976        :class:`_orm.Session`. 
    2977 
    2978        Note that the :class:`_query.Query` object is legacy as of 
    2979        SQLAlchemy 2.0; the :func:`_sql.select` construct is now used 
    2980        to construct ORM queries. 
    2981 
    2982        .. seealso:: 
    2983 
    2984            :ref:`unified_tutorial` 
    2985 
    2986            :ref:`queryguide_toplevel` 
    2987 
    2988            :ref:`query_api_toplevel` - legacy API doc 
    2989 
    2990        """ 
    2991 
    2992        return self._query_cls(entities, self, **kwargs) 
    2993 
    2994    def _identity_lookup( 
    2995        self, 
    2996        mapper: Mapper[_O], 
    2997        primary_key_identity: Union[Any, Tuple[Any, ...]], 
    2998        identity_token: Any = None, 
    2999        passive: PassiveFlag = PassiveFlag.PASSIVE_OFF, 
    3000        lazy_loaded_from: Optional[InstanceState[Any]] = None, 
    3001        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    3002        bind_arguments: Optional[_BindArguments] = None, 
    3003    ) -> Union[Optional[_O], LoaderCallableStatus]: 
    3004        """Locate an object in the identity map. 
    3005 
    3006        Given a primary key identity, constructs an identity key and then 
    3007        looks in the session's identity map.  If present, the object may 
    3008        be run through unexpiration rules (e.g. load unloaded attributes, 
    3009        check if was deleted). 
    3010 
    3011        e.g.:: 
    3012 
    3013            obj = session._identity_lookup(inspect(SomeClass), (1,)) 
    3014 
    3015        :param mapper: mapper in use 
    3016        :param primary_key_identity: the primary key we are searching for, as 
    3017         a tuple. 
    3018        :param identity_token: identity token that should be used to create 
    3019         the identity key.  Used as is, however overriding subclasses can 
    3020         repurpose this in order to interpret the value in a special way, 
    3021         such as if None then look among multiple target tokens. 
    3022        :param passive: passive load flag passed to 
    3023         :func:`.loading.get_from_identity`, which impacts the behavior if 
    3024         the object is found; the object may be validated and/or unexpired 
    3025         if the flag allows for SQL to be emitted. 
    3026        :param lazy_loaded_from: an :class:`.InstanceState` that is 
    3027         specifically asking for this identity as a related identity.  Used 
    3028         for sharding schemes where there is a correspondence between an object 
    3029         and a related object being lazy-loaded (or otherwise 
    3030         relationship-loaded). 
    3031 
    3032        :return: None if the object is not found in the identity map, *or* 
    3033         if the object was unexpired and found to have been deleted. 
    3034         if passive flags disallow SQL and the object is expired, returns 
    3035         PASSIVE_NO_RESULT.   In all other cases the instance is returned. 
    3036 
    3037        .. versionchanged:: 1.4.0 - the :meth:`.Session._identity_lookup` 
    3038           method was moved from :class:`_query.Query` to 
    3039           :class:`.Session`, to avoid having to instantiate the 
    3040           :class:`_query.Query` object. 
    3041 
    3042 
    3043        """ 
    3044 
    3045        key = mapper.identity_key_from_primary_key( 
    3046            primary_key_identity, identity_token=identity_token 
    3047        ) 
    3048 
    3049        # work around: https://github.com/python/typing/discussions/1143 
    3050        return_value = loading.get_from_identity(self, mapper, key, passive) 
    3051        return return_value 
    3052 
    3053    @util.non_memoized_property 
    3054    @contextlib.contextmanager 
    3055    def no_autoflush(self) -> Iterator[Session]: 
    3056        """Return a context manager that disables autoflush. 
    3057 
    3058        e.g.:: 
    3059 
    3060            with session.no_autoflush: 
    3061 
    3062                some_object = SomeClass() 
    3063                session.add(some_object) 
    3064                # won't autoflush 
    3065                some_object.related_thing = session.query(SomeRelated).first() 
    3066 
    3067        Operations that proceed within the ``with:`` block 
    3068        will not be subject to flushes occurring upon query 
    3069        access.  This is useful when initializing a series 
    3070        of objects which involve existing database queries, 
    3071        where the uncompleted object should not yet be flushed. 
    3072 
    3073        """ 
    3074        autoflush = self.autoflush 
    3075        self.autoflush = False 
    3076        try: 
    3077            yield self 
    3078        finally: 
    3079            self.autoflush = autoflush 
    3080 
    3081    @util.langhelpers.tag_method_for_warnings( 
    3082        "This warning originated from the Session 'autoflush' process, " 
    3083        "which was invoked automatically in response to a user-initiated " 
    3084        "operation. Consider using ``no_autoflush`` context manager if this " 
    3085        "warning happened while initializing objects.", 
    3086        sa_exc.SAWarning, 
    3087    ) 
    3088    def _autoflush(self) -> None: 
    3089        if self.autoflush and not self._flushing: 
    3090            try: 
    3091                self.flush() 
    3092            except sa_exc.StatementError as e: 
    3093                # note we are reraising StatementError as opposed to 
    3094                # raising FlushError with "chaining" to remain compatible 
    3095                # with code that catches StatementError, IntegrityError, 
    3096                # etc. 
    3097                e.add_detail( 
    3098                    "raised as a result of Query-invoked autoflush; " 
    3099                    "consider using a session.no_autoflush block if this " 
    3100                    "flush is occurring prematurely" 
    3101                ) 
    3102                raise e.with_traceback(sys.exc_info()[2]) 
    3103 
    3104    def refresh( 
    3105        self, 
    3106        instance: object, 
    3107        attribute_names: Optional[Iterable[str]] = None, 
    3108        with_for_update: ForUpdateParameter = None, 
    3109    ) -> None: 
    3110        """Expire and refresh attributes on the given instance. 
    3111 
    3112        The selected attributes will first be expired as they would when using 
    3113        :meth:`_orm.Session.expire`; then a SELECT statement will be issued to 
    3114        the database to refresh column-oriented attributes with the current 
    3115        value available in the current transaction. 
    3116 
    3117        :func:`_orm.relationship` oriented attributes will also be immediately 
    3118        loaded if they were already eagerly loaded on the object, using the 
    3119        same eager loading strategy that they were loaded with originally. 
    3120 
    3121        .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method 
    3122           can also refresh eagerly loaded attributes. 
    3123 
    3124        :func:`_orm.relationship` oriented attributes that would normally 
    3125        load using the ``select`` (or "lazy") loader strategy will also 
    3126        load **if they are named explicitly in the attribute_names 
    3127        collection**, emitting a SELECT statement for the attribute using the 
    3128        ``immediate`` loader strategy.  If lazy-loaded relationships are not 
    3129        named in :paramref:`_orm.Session.refresh.attribute_names`, then 
    3130        they remain as "lazy loaded" attributes and are not implicitly 
    3131        refreshed. 
    3132 
    3133        .. versionchanged:: 2.0.4  The :meth:`_orm.Session.refresh` method 
    3134           will now refresh lazy-loaded :func:`_orm.relationship` oriented 
    3135           attributes for those which are named explicitly in the 
    3136           :paramref:`_orm.Session.refresh.attribute_names` collection. 
    3137 
    3138        .. tip:: 
    3139 
    3140            While the :meth:`_orm.Session.refresh` method is capable of 
    3141            refreshing both column and relationship oriented attributes, its 
    3142            primary focus is on refreshing of local column-oriented attributes 
    3143            on a single instance. For more open ended "refresh" functionality, 
    3144            including the ability to refresh the attributes on many objects at 
    3145            once while having explicit control over relationship loader 
    3146            strategies, use the 
    3147            :ref:`populate existing <orm_queryguide_populate_existing>` feature 
    3148            instead. 
    3149 
    3150        Note that a highly isolated transaction will return the same values as 
    3151        were previously read in that same transaction, regardless of changes 
    3152        in database state outside of that transaction.   Refreshing 
    3153        attributes usually only makes sense at the start of a transaction 
    3154        where database rows have not yet been accessed. 
    3155 
    3156        :param attribute_names: optional.  An iterable collection of 
    3157          string attribute names indicating a subset of attributes to 
    3158          be refreshed. 
    3159 
    3160        :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 
    3161          should be used, or may be a dictionary containing flags to 
    3162          indicate a more specific set of FOR UPDATE flags for the SELECT; 
    3163          flags should match the parameters of 
    3164          :meth:`_query.Query.with_for_update`. 
    3165          Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 
    3166 
    3167        .. seealso:: 
    3168 
    3169            :ref:`session_expire` - introductory material 
    3170 
    3171            :meth:`.Session.expire` 
    3172 
    3173            :meth:`.Session.expire_all` 
    3174 
    3175            :ref:`orm_queryguide_populate_existing` - allows any ORM query 
    3176            to refresh objects as they would be loaded normally. 
    3177 
    3178        """ 
    3179        try: 
    3180            state = attributes.instance_state(instance) 
    3181        except exc.NO_STATE as err: 
    3182            raise exc.UnmappedInstanceError(instance) from err 
    3183 
    3184        self._expire_state(state, attribute_names) 
    3185 
    3186        # this autoflush previously used to occur as a secondary effect 
    3187        # of the load_on_ident below.   Meaning we'd organize the SELECT 
    3188        # based on current DB pks, then flush, then if pks changed in that 
    3189        # flush, crash.  this was unticketed but discovered as part of 
    3190        # #8703.  So here, autoflush up front, dont autoflush inside 
    3191        # load_on_ident. 
    3192        self._autoflush() 
    3193 
    3194        if with_for_update == {}: 
    3195            raise sa_exc.ArgumentError( 
    3196                "with_for_update should be the boolean value " 
    3197                "True, or a dictionary with options.  " 
    3198                "A blank dictionary is ambiguous." 
    3199            ) 
    3200 
    3201        with_for_update = ForUpdateArg._from_argument(with_for_update) 
    3202 
    3203        stmt: Select[Unpack[TupleAny]] = sql.select(object_mapper(instance)) 
    3204        if ( 
    3205            loading._load_on_ident( 
    3206                self, 
    3207                stmt, 
    3208                state.key, 
    3209                refresh_state=state, 
    3210                with_for_update=with_for_update, 
    3211                only_load_props=attribute_names, 
    3212                require_pk_cols=True, 
    3213                # technically unnecessary as we just did autoflush 
    3214                # above, however removes the additional unnecessary 
    3215                # call to _autoflush() 
    3216                no_autoflush=True, 
    3217                is_user_refresh=True, 
    3218            ) 
    3219            is None 
    3220        ): 
    3221            raise sa_exc.InvalidRequestError( 
    3222                "Could not refresh instance '%s'" % instance_str(instance) 
    3223            ) 
    3224 
    3225    def expire_all(self) -> None: 
    3226        """Expires all persistent instances within this Session. 
    3227 
    3228        When any attributes on a persistent instance is next accessed, 
    3229        a query will be issued using the 
    3230        :class:`.Session` object's current transactional context in order to 
    3231        load all expired attributes for the given instance.   Note that 
    3232        a highly isolated transaction will return the same values as were 
    3233        previously read in that same transaction, regardless of changes 
    3234        in database state outside of that transaction. 
    3235 
    3236        To expire individual objects and individual attributes 
    3237        on those objects, use :meth:`Session.expire`. 
    3238 
    3239        The :class:`.Session` object's default behavior is to 
    3240        expire all state whenever the :meth:`Session.rollback` 
    3241        or :meth:`Session.commit` methods are called, so that new 
    3242        state can be loaded for the new transaction.   For this reason, 
    3243        calling :meth:`Session.expire_all` is not usually needed, 
    3244        assuming the transaction is isolated. 
    3245 
    3246        .. seealso:: 
    3247 
    3248            :ref:`session_expire` - introductory material 
    3249 
    3250            :meth:`.Session.expire` 
    3251 
    3252            :meth:`.Session.refresh` 
    3253 
    3254            :meth:`_orm.Query.populate_existing` 
    3255 
    3256        """ 
    3257        for state in self.identity_map.all_states(): 
    3258            state._expire(state.dict, self.identity_map._modified) 
    3259 
    3260    def expire( 
    3261        self, instance: object, attribute_names: Optional[Iterable[str]] = None 
    3262    ) -> None: 
    3263        """Expire the attributes on an instance. 
    3264 
    3265        Marks the attributes of an instance as out of date. When an expired 
    3266        attribute is next accessed, a query will be issued to the 
    3267        :class:`.Session` object's current transactional context in order to 
    3268        load all expired attributes for the given instance.   Note that 
    3269        a highly isolated transaction will return the same values as were 
    3270        previously read in that same transaction, regardless of changes 
    3271        in database state outside of that transaction. 
    3272 
    3273        To expire all objects in the :class:`.Session` simultaneously, 
    3274        use :meth:`Session.expire_all`. 
    3275 
    3276        The :class:`.Session` object's default behavior is to 
    3277        expire all state whenever the :meth:`Session.rollback` 
    3278        or :meth:`Session.commit` methods are called, so that new 
    3279        state can be loaded for the new transaction.   For this reason, 
    3280        calling :meth:`Session.expire` only makes sense for the specific 
    3281        case that a non-ORM SQL statement was emitted in the current 
    3282        transaction. 
    3283 
    3284        :param instance: The instance to be refreshed. 
    3285        :param attribute_names: optional list of string attribute names 
    3286          indicating a subset of attributes to be expired. 
    3287 
    3288        .. seealso:: 
    3289 
    3290            :ref:`session_expire` - introductory material 
    3291 
    3292            :meth:`.Session.expire` 
    3293 
    3294            :meth:`.Session.refresh` 
    3295 
    3296            :meth:`_orm.Query.populate_existing` 
    3297 
    3298        """ 
    3299        try: 
    3300            state = attributes.instance_state(instance) 
    3301        except exc.NO_STATE as err: 
    3302            raise exc.UnmappedInstanceError(instance) from err 
    3303        self._expire_state(state, attribute_names) 
    3304 
    3305    def _expire_state( 
    3306        self, 
    3307        state: InstanceState[Any], 
    3308        attribute_names: Optional[Iterable[str]], 
    3309    ) -> None: 
    3310        self._validate_persistent(state) 
    3311        if attribute_names: 
    3312            state._expire_attributes(state.dict, attribute_names) 
    3313        else: 
    3314            # pre-fetch the full cascade since the expire is going to 
    3315            # remove associations 
    3316            cascaded = list( 
    3317                state.manager.mapper.cascade_iterator("refresh-expire", state) 
    3318            ) 
    3319            self._conditional_expire(state) 
    3320            for o, m, st_, dct_ in cascaded: 
    3321                self._conditional_expire(st_) 
    3322 
    3323    def _conditional_expire( 
    3324        self, state: InstanceState[Any], autoflush: Optional[bool] = None 
    3325    ) -> None: 
    3326        """Expire a state if persistent, else expunge if pending""" 
    3327 
    3328        if state.key: 
    3329            state._expire(state.dict, self.identity_map._modified) 
    3330        elif state in self._new: 
    3331            self._new.pop(state) 
    3332            state._detach(self) 
    3333 
    3334    def expunge(self, instance: object) -> None: 
    3335        """Remove the `instance` from this ``Session``. 
    3336 
    3337        This will free all internal references to the instance.  Cascading 
    3338        will be applied according to the *expunge* cascade rule. 
    3339 
    3340        """ 
    3341        try: 
    3342            state = attributes.instance_state(instance) 
    3343        except exc.NO_STATE as err: 
    3344            raise exc.UnmappedInstanceError(instance) from err 
    3345        if state.session_id is not self.hash_key: 
    3346            raise sa_exc.InvalidRequestError( 
    3347                "Instance %s is not present in this Session" % state_str(state) 
    3348            ) 
    3349 
    3350        cascaded = list( 
    3351            state.manager.mapper.cascade_iterator("expunge", state) 
    3352        ) 
    3353        self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded]) 
    3354 
    3355    def _expunge_states( 
    3356        self, states: Iterable[InstanceState[Any]], to_transient: bool = False 
    3357    ) -> None: 
    3358        for state in states: 
    3359            if state in self._new: 
    3360                self._new.pop(state) 
    3361            elif self.identity_map.contains_state(state): 
    3362                self.identity_map.safe_discard(state) 
    3363                self._deleted.pop(state, None) 
    3364            elif self._transaction: 
    3365                # state is "detached" from being deleted, but still present 
    3366                # in the transaction snapshot 
    3367                self._transaction._deleted.pop(state, None) 
    3368        statelib.InstanceState._detach_states( 
    3369            states, self, to_transient=to_transient 
    3370        ) 
    3371 
    3372    def _register_persistent(self, states: Set[InstanceState[Any]]) -> None: 
    3373        """Register all persistent objects from a flush. 
    3374 
    3375        This is used both for pending objects moving to the persistent 
    3376        state as well as already persistent objects. 
    3377 
    3378        """ 
    3379 
    3380        pending_to_persistent = self.dispatch.pending_to_persistent or None 
    3381        for state in states: 
    3382            mapper = _state_mapper(state) 
    3383 
    3384            # prevent against last minute dereferences of the object 
    3385            obj = state.obj() 
    3386            if obj is not None: 
    3387                instance_key = mapper._identity_key_from_state(state) 
    3388 
    3389                if ( 
    3390                    _none_set.intersection(instance_key[1]) 
    3391                    and not mapper.allow_partial_pks 
    3392                    or _none_set.issuperset(instance_key[1]) 
    3393                ): 
    3394                    raise exc.FlushError( 
    3395                        "Instance %s has a NULL identity key.  If this is an " 
    3396                        "auto-generated value, check that the database table " 
    3397                        "allows generation of new primary key values, and " 
    3398                        "that the mapped Column object is configured to " 
    3399                        "expect these generated values.  Ensure also that " 
    3400                        "this flush() is not occurring at an inappropriate " 
    3401                        "time, such as within a load() event." 
    3402                        % state_str(state) 
    3403                    ) 
    3404 
    3405                if state.key is None: 
    3406                    state.key = instance_key 
    3407                elif state.key != instance_key: 
    3408                    # primary key switch. use safe_discard() in case another 
    3409                    # state has already replaced this one in the identity 
    3410                    # map (see test/orm/test_naturalpks.py ReversePKsTest) 
    3411                    self.identity_map.safe_discard(state) 
    3412                    trans = self._transaction 
    3413                    assert trans is not None 
    3414                    if state in trans._key_switches: 
    3415                        orig_key = trans._key_switches[state][0] 
    3416                    else: 
    3417                        orig_key = state.key 
    3418                    trans._key_switches[state] = ( 
    3419                        orig_key, 
    3420                        instance_key, 
    3421                    ) 
    3422                    state.key = instance_key 
    3423 
    3424                # there can be an existing state in the identity map 
    3425                # that is replaced when the primary keys of two instances 
    3426                # are swapped; see test/orm/test_naturalpks.py -> test_reverse 
    3427                old = self.identity_map.replace(state) 
    3428                if ( 
    3429                    old is not None 
    3430                    and mapper._identity_key_from_state(old) == instance_key 
    3431                    and old.obj() is not None 
    3432                ): 
    3433                    util.warn( 
    3434                        "Identity map already had an identity for %s, " 
    3435                        "replacing it with newly flushed object.   Are there " 
    3436                        "load operations occurring inside of an event handler " 
    3437                        "within the flush?" % (instance_key,) 
    3438                    ) 
    3439                state._orphaned_outside_of_session = False 
    3440 
    3441        statelib.InstanceState._commit_all_states( 
    3442            ((state, state.dict) for state in states), self.identity_map 
    3443        ) 
    3444 
    3445        self._register_altered(states) 
    3446 
    3447        if pending_to_persistent is not None: 
    3448            for state in states.intersection(self._new): 
    3449                pending_to_persistent(self, state) 
    3450 
    3451        # remove from new last, might be the last strong ref 
    3452        for state in set(states).intersection(self._new): 
    3453            self._new.pop(state) 
    3454 
    3455    def _register_altered(self, states: Iterable[InstanceState[Any]]) -> None: 
    3456        if self._transaction: 
    3457            for state in states: 
    3458                if state in self._new: 
    3459                    self._transaction._new[state] = True 
    3460                else: 
    3461                    self._transaction._dirty[state] = True 
    3462 
    3463    def _remove_newly_deleted( 
    3464        self, states: Iterable[InstanceState[Any]] 
    3465    ) -> None: 
    3466        persistent_to_deleted = self.dispatch.persistent_to_deleted or None 
    3467        for state in states: 
    3468            if self._transaction: 
    3469                self._transaction._deleted[state] = True 
    3470 
    3471            if persistent_to_deleted is not None: 
    3472                # get a strong reference before we pop out of 
    3473                # self._deleted 
    3474                obj = state.obj()  # noqa 
    3475 
    3476            self.identity_map.safe_discard(state) 
    3477            self._deleted.pop(state, None) 
    3478            state._deleted = True 
    3479            # can't call state._detach() here, because this state 
    3480            # is still in the transaction snapshot and needs to be 
    3481            # tracked as part of that 
    3482            if persistent_to_deleted is not None: 
    3483                persistent_to_deleted(self, state) 
    3484 
    3485    def add(self, instance: object, *, _warn: bool = True) -> None: 
    3486        """Place an object into this :class:`_orm.Session`. 
    3487 
    3488        Objects that are in the :term:`transient` state when passed to the 
    3489        :meth:`_orm.Session.add` method will move to the 
    3490        :term:`pending` state, until the next flush, at which point they 
    3491        will move to the :term:`persistent` state. 
    3492 
    3493        Objects that are in the :term:`detached` state when passed to the 
    3494        :meth:`_orm.Session.add` method will move to the :term:`persistent` 
    3495        state directly. 
    3496 
    3497        If the transaction used by the :class:`_orm.Session` is rolled back, 
    3498        objects which were transient when they were passed to 
    3499        :meth:`_orm.Session.add` will be moved back to the 
    3500        :term:`transient` state, and will no longer be present within this 
    3501        :class:`_orm.Session`. 
    3502 
    3503        .. seealso:: 
    3504 
    3505            :meth:`_orm.Session.add_all` 
    3506 
    3507            :ref:`session_adding` - at :ref:`session_basics` 
    3508 
    3509        """ 
    3510        if _warn and self._warn_on_events: 
    3511            self._flush_warning("Session.add()") 
    3512 
    3513        try: 
    3514            state = attributes.instance_state(instance) 
    3515        except exc.NO_STATE as err: 
    3516            raise exc.UnmappedInstanceError(instance) from err 
    3517 
    3518        self._save_or_update_state(state) 
    3519 
    3520    def add_all(self, instances: Iterable[object]) -> None: 
    3521        """Add the given collection of instances to this :class:`_orm.Session`. 
    3522 
    3523        See the documentation for :meth:`_orm.Session.add` for a general 
    3524        behavioral description. 
    3525 
    3526        .. seealso:: 
    3527 
    3528            :meth:`_orm.Session.add` 
    3529 
    3530            :ref:`session_adding` - at :ref:`session_basics` 
    3531 
    3532        """ 
    3533 
    3534        if self._warn_on_events: 
    3535            self._flush_warning("Session.add_all()") 
    3536 
    3537        for instance in instances: 
    3538            self.add(instance, _warn=False) 
    3539 
    3540    def _save_or_update_state(self, state: InstanceState[Any]) -> None: 
    3541        state._orphaned_outside_of_session = False 
    3542        self._save_or_update_impl(state) 
    3543 
    3544        mapper = _state_mapper(state) 
    3545        for o, m, st_, dct_ in mapper.cascade_iterator( 
    3546            "save-update", state, halt_on=self._contains_state 
    3547        ): 
    3548            self._save_or_update_impl(st_) 
    3549 
    3550    def delete(self, instance: object) -> None: 
    3551        """Mark an instance as deleted. 
    3552 
    3553        The object is assumed to be either :term:`persistent` or 
    3554        :term:`detached` when passed; after the method is called, the 
    3555        object will remain in the :term:`persistent` state until the next 
    3556        flush proceeds.  During this time, the object will also be a member 
    3557        of the :attr:`_orm.Session.deleted` collection. 
    3558 
    3559        When the next flush proceeds, the object will move to the 
    3560        :term:`deleted` state, indicating a ``DELETE`` statement was emitted 
    3561        for its row within the current transaction.   When the transaction 
    3562        is successfully committed, 
    3563        the deleted object is moved to the :term:`detached` state and is 
    3564        no longer present within this :class:`_orm.Session`. 
    3565 
    3566        .. seealso:: 
    3567 
    3568            :ref:`session_deleting` - at :ref:`session_basics` 
    3569 
    3570            :meth:`.Session.delete_all` - multiple instance version 
    3571 
    3572        """ 
    3573        if self._warn_on_events: 
    3574            self._flush_warning("Session.delete()") 
    3575 
    3576        self._delete_impl(object_state(instance), instance, head=True) 
    3577 
    3578    def delete_all(self, instances: Iterable[object]) -> None: 
    3579        """Calls :meth:`.Session.delete` on multiple instances. 
    3580 
    3581        .. seealso:: 
    3582 
    3583            :meth:`.Session.delete` - main documentation on delete 
    3584 
    3585        .. versionadded:: 2.1 
    3586 
    3587        """ 
    3588 
    3589        if self._warn_on_events: 
    3590            self._flush_warning("Session.delete_all()") 
    3591 
    3592        for instance in instances: 
    3593            self._delete_impl(object_state(instance), instance, head=True) 
    3594 
    3595    def _delete_impl( 
    3596        self, state: InstanceState[Any], obj: object, head: bool 
    3597    ) -> None: 
    3598        if state.key is None: 
    3599            if head: 
    3600                raise sa_exc.InvalidRequestError( 
    3601                    "Instance '%s' is not persisted" % state_str(state) 
    3602                ) 
    3603            else: 
    3604                return 
    3605 
    3606        to_attach = self._before_attach(state, obj) 
    3607 
    3608        if state in self._deleted: 
    3609            return 
    3610 
    3611        self.identity_map.add(state) 
    3612 
    3613        if to_attach: 
    3614            self._after_attach(state, obj) 
    3615 
    3616        if head: 
    3617            # grab the cascades before adding the item to the deleted list 
    3618            # so that autoflush does not delete the item 
    3619            # the strong reference to the instance itself is significant here 
    3620            cascade_states = list( 
    3621                state.manager.mapper.cascade_iterator("delete", state) 
    3622            ) 
    3623        else: 
    3624            cascade_states = None 
    3625 
    3626        self._deleted[state] = obj 
    3627 
    3628        if head: 
    3629            if TYPE_CHECKING: 
    3630                assert cascade_states is not None 
    3631            for o, m, st_, dct_ in cascade_states: 
    3632                self._delete_impl(st_, o, False) 
    3633 
    3634    def get( 
    3635        self, 
    3636        entity: _EntityBindKey[_O], 
    3637        ident: _PKIdentityArgument, 
    3638        *, 
    3639        options: Optional[Sequence[ORMOption]] = None, 
    3640        populate_existing: bool = False, 
    3641        with_for_update: ForUpdateParameter = None, 
    3642        identity_token: Optional[Any] = None, 
    3643        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    3644        bind_arguments: Optional[_BindArguments] = None, 
    3645    ) -> Optional[_O]: 
    3646        """Return an instance based on the given primary key identifier, 
    3647        or ``None`` if not found. 
    3648 
    3649        E.g.:: 
    3650 
    3651            my_user = session.get(User, 5) 
    3652 
    3653            some_object = session.get(VersionedFoo, (5, 10)) 
    3654 
    3655            some_object = session.get(VersionedFoo, {"id": 5, "version_id": 10}) 
    3656 
    3657        .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved 
    3658           from the now legacy :meth:`_orm.Query.get` method. 
    3659 
    3660        :meth:`_orm.Session.get` is special in that it provides direct 
    3661        access to the identity map of the :class:`.Session`. 
    3662        If the given primary key identifier is present 
    3663        in the local identity map, the object is returned 
    3664        directly from this collection and no SQL is emitted, 
    3665        unless the object has been marked fully expired. 
    3666        If not present, 
    3667        a SELECT is performed in order to locate the object. 
    3668 
    3669        :meth:`_orm.Session.get` also will perform a check if 
    3670        the object is present in the identity map and 
    3671        marked as expired - a SELECT 
    3672        is emitted to refresh the object as well as to 
    3673        ensure that the row is still present. 
    3674        If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 
    3675 
    3676        :param entity: a mapped class or :class:`.Mapper` indicating the 
    3677         type of entity to be loaded. 
    3678 
    3679        :param ident: A scalar, tuple, or dictionary representing the 
    3680         primary key.  For a composite (e.g. multiple column) primary key, 
    3681         a tuple or dictionary should be passed. 
    3682 
    3683         For a single-column primary key, the scalar calling form is typically 
    3684         the most expedient.  If the primary key of a row is the value "5", 
    3685         the call looks like:: 
    3686 
    3687            my_object = session.get(SomeClass, 5) 
    3688 
    3689         The tuple form contains primary key values typically in 
    3690         the order in which they correspond to the mapped 
    3691         :class:`_schema.Table` 
    3692         object's primary key columns, or if the 
    3693         :paramref:`_orm.Mapper.primary_key` configuration parameter were 
    3694         used, in 
    3695         the order used for that parameter. For example, if the primary key 
    3696         of a row is represented by the integer 
    3697         digits "5, 10" the call would look like:: 
    3698 
    3699             my_object = session.get(SomeClass, (5, 10)) 
    3700 
    3701         The dictionary form should include as keys the mapped attribute names 
    3702         corresponding to each element of the primary key.  If the mapped class 
    3703         has the attributes ``id``, ``version_id`` as the attributes which 
    3704         store the object's primary key value, the call would look like:: 
    3705 
    3706            my_object = session.get(SomeClass, {"id": 5, "version_id": 10}) 
    3707 
    3708        :param options: optional sequence of loader options which will be 
    3709         applied to the query, if one is emitted. 
    3710 
    3711        :param populate_existing: causes the method to unconditionally emit 
    3712         a SQL query and refresh the object with the newly loaded data, 
    3713         regardless of whether or not the object is already present. 
    3714 
    3715        :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 
    3716          should be used, or may be a dictionary containing flags to 
    3717          indicate a more specific set of FOR UPDATE flags for the SELECT; 
    3718          flags should match the parameters of 
    3719          :meth:`_query.Query.with_for_update`. 
    3720          Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 
    3721 
    3722        :param execution_options: optional dictionary of execution options, 
    3723         which will be associated with the query execution if one is emitted. 
    3724         This dictionary can provide a subset of the options that are 
    3725         accepted by :meth:`_engine.Connection.execution_options`, and may 
    3726         also provide additional options understood only in an ORM context. 
    3727 
    3728         .. versionadded:: 1.4.29 
    3729 
    3730         .. seealso:: 
    3731 
    3732            :ref:`orm_queryguide_execution_options` - ORM-specific execution 
    3733            options 
    3734 
    3735        :param bind_arguments: dictionary of additional arguments to determine 
    3736         the bind.  May include "mapper", "bind", or other custom arguments. 
    3737         Contents of this dictionary are passed to the 
    3738         :meth:`.Session.get_bind` method. 
    3739 
    3740         .. versionadded:: 2.0.0rc1 
    3741 
    3742        :return: The object instance, or ``None``. 
    3743 
    3744        """  # noqa: E501 
    3745        return self._get_impl( 
    3746            entity, 
    3747            ident, 
    3748            loading._load_on_pk_identity, 
    3749            options=options, 
    3750            populate_existing=populate_existing, 
    3751            with_for_update=with_for_update, 
    3752            identity_token=identity_token, 
    3753            execution_options=execution_options, 
    3754            bind_arguments=bind_arguments, 
    3755        ) 
    3756 
    3757    def get_one( 
    3758        self, 
    3759        entity: _EntityBindKey[_O], 
    3760        ident: _PKIdentityArgument, 
    3761        *, 
    3762        options: Optional[Sequence[ORMOption]] = None, 
    3763        populate_existing: bool = False, 
    3764        with_for_update: ForUpdateParameter = None, 
    3765        identity_token: Optional[Any] = None, 
    3766        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    3767        bind_arguments: Optional[_BindArguments] = None, 
    3768    ) -> _O: 
    3769        """Return exactly one instance based on the given primary key 
    3770        identifier, or raise an exception if not found. 
    3771 
    3772        Raises :class:`_exc.NoResultFound` if the query selects no rows. 
    3773 
    3774        For a detailed documentation of the arguments see the 
    3775        method :meth:`.Session.get`. 
    3776 
    3777        .. versionadded:: 2.0.22 
    3778 
    3779        :return: The object instance. 
    3780 
    3781        .. seealso:: 
    3782 
    3783            :meth:`.Session.get` - equivalent method that instead 
    3784              returns ``None`` if no row was found with the provided primary 
    3785              key 
    3786 
    3787        """ 
    3788 
    3789        instance = self.get( 
    3790            entity, 
    3791            ident, 
    3792            options=options, 
    3793            populate_existing=populate_existing, 
    3794            with_for_update=with_for_update, 
    3795            identity_token=identity_token, 
    3796            execution_options=execution_options, 
    3797            bind_arguments=bind_arguments, 
    3798        ) 
    3799 
    3800        if instance is None: 
    3801            raise sa_exc.NoResultFound( 
    3802                "No row was found when one was required" 
    3803            ) 
    3804 
    3805        return instance 
    3806 
    3807    def _get_impl( 
    3808        self, 
    3809        entity: _EntityBindKey[_O], 
    3810        primary_key_identity: _PKIdentityArgument, 
    3811        db_load_fn: Callable[..., _O], 
    3812        *, 
    3813        options: Optional[Sequence[ExecutableOption]] = None, 
    3814        populate_existing: bool = False, 
    3815        with_for_update: ForUpdateParameter = None, 
    3816        identity_token: Optional[Any] = None, 
    3817        execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 
    3818        bind_arguments: Optional[_BindArguments] = None, 
    3819    ) -> Optional[_O]: 
    3820        # convert composite types to individual args 
    3821        if ( 
    3822            is_composite_class(primary_key_identity) 
    3823            and type(primary_key_identity) 
    3824            in descriptor_props._composite_getters 
    3825        ): 
    3826            getter = descriptor_props._composite_getters[ 
    3827                type(primary_key_identity) 
    3828            ] 
    3829            primary_key_identity = getter(primary_key_identity) 
    3830 
    3831        mapper: Optional[Mapper[_O]] = inspect(entity) 
    3832 
    3833        if mapper is None or not mapper.is_mapper: 
    3834            raise sa_exc.ArgumentError( 
    3835                "Expected mapped class or mapper, got: %r" % entity 
    3836            ) 
    3837 
    3838        is_dict = isinstance(primary_key_identity, dict) 
    3839        if not is_dict: 
    3840            primary_key_identity = util.to_list( 
    3841                primary_key_identity, default=[None] 
    3842            ) 
    3843 
    3844        if len(primary_key_identity) != len(mapper.primary_key): 
    3845            raise sa_exc.InvalidRequestError( 
    3846                "Incorrect number of values in identifier to formulate " 
    3847                "primary key for session.get(); primary key columns " 
    3848                "are %s" % ",".join("'%s'" % c for c in mapper.primary_key) 
    3849            ) 
    3850 
    3851        if is_dict: 
    3852            pk_synonyms = mapper._pk_synonyms 
    3853 
    3854            if pk_synonyms: 
    3855                correct_keys = set(pk_synonyms).intersection( 
    3856                    primary_key_identity 
    3857                ) 
    3858 
    3859                if correct_keys: 
    3860                    primary_key_identity = dict(primary_key_identity) 
    3861                    for k in correct_keys: 
    3862                        primary_key_identity[pk_synonyms[k]] = ( 
    3863                            primary_key_identity[k] 
    3864                        ) 
    3865 
    3866            try: 
    3867                primary_key_identity = list( 
    3868                    primary_key_identity[prop.key] 
    3869                    for prop in mapper._identity_key_props 
    3870                ) 
    3871 
    3872            except KeyError as err: 
    3873                raise sa_exc.InvalidRequestError( 
    3874                    "Incorrect names of values in identifier to formulate " 
    3875                    "primary key for session.get(); primary key attribute " 
    3876                    "names are %s (synonym names are also accepted)" 
    3877                    % ",".join( 
    3878                        "'%s'" % prop.key 
    3879                        for prop in mapper._identity_key_props 
    3880                    ) 
    3881                ) from err 
    3882 
    3883        if ( 
    3884            not populate_existing 
    3885            and not mapper.always_refresh 
    3886            and with_for_update is None 
    3887        ): 
    3888            instance = self._identity_lookup( 
    3889                mapper, 
    3890                primary_key_identity, 
    3891                identity_token=identity_token, 
    3892                execution_options=execution_options, 
    3893                bind_arguments=bind_arguments, 
    3894            ) 
    3895 
    3896            if instance is not None: 
    3897                # reject calls for id in identity map but class 
    3898                # mismatch. 
    3899                if not isinstance(instance, mapper.class_): 
    3900                    return None 
    3901                return instance 
    3902 
    3903            # TODO: this was being tested before, but this is not possible 
    3904            assert instance is not LoaderCallableStatus.PASSIVE_CLASS_MISMATCH 
    3905 
    3906        # set_label_style() not strictly necessary, however this will ensure 
    3907        # that tablename_colname style is used which at the moment is 
    3908        # asserted in a lot of unit tests :) 
    3909 
    3910        load_options = context.QueryContext.default_load_options 
    3911 
    3912        if populate_existing: 
    3913            load_options += {"_populate_existing": populate_existing} 
    3914        statement = sql.select(mapper).set_label_style( 
    3915            LABEL_STYLE_TABLENAME_PLUS_COL 
    3916        ) 
    3917        if with_for_update is not None: 
    3918            statement._for_update_arg = ForUpdateArg._from_argument( 
    3919                with_for_update 
    3920            ) 
    3921 
    3922        if options: 
    3923            statement = statement.options(*options) 
    3924        if self.execution_options: 
    3925            execution_options = self.execution_options.union(execution_options) 
    3926        return db_load_fn( 
    3927            self, 
    3928            statement, 
    3929            primary_key_identity, 
    3930            load_options=load_options, 
    3931            identity_token=identity_token, 
    3932            execution_options=execution_options, 
    3933            bind_arguments=bind_arguments, 
    3934        ) 
    3935 
    3936    def merge( 
    3937        self, 
    3938        instance: _O, 
    3939        *, 
    3940        load: bool = True, 
    3941        options: Optional[Sequence[ORMOption]] = None, 
    3942    ) -> _O: 
    3943        """Copy the state of a given instance into a corresponding instance 
    3944        within this :class:`.Session`. 
    3945 
    3946        :meth:`.Session.merge` examines the primary key attributes of the 
    3947        source instance, and attempts to reconcile it with an instance of the 
    3948        same primary key in the session.   If not found locally, it attempts 
    3949        to load the object from the database based on primary key, and if 
    3950        none can be located, creates a new instance.  The state of each 
    3951        attribute on the source instance is then copied to the target 
    3952        instance.  The resulting target instance is then returned by the 
    3953        method; the original source instance is left unmodified, and 
    3954        un-associated with the :class:`.Session` if not already. 
    3955 
    3956        This operation cascades to associated instances if the association is 
    3957        mapped with ``cascade="merge"``. 
    3958 
    3959        See :ref:`unitofwork_merging` for a detailed discussion of merging. 
    3960 
    3961        :param instance: Instance to be merged. 
    3962        :param load: Boolean, when False, :meth:`.merge` switches into 
    3963         a "high performance" mode which causes it to forego emitting history 
    3964         events as well as all database access.  This flag is used for 
    3965         cases such as transferring graphs of objects into a :class:`.Session` 
    3966         from a second level cache, or to transfer just-loaded objects 
    3967         into the :class:`.Session` owned by a worker thread or process 
    3968         without re-querying the database. 
    3969 
    3970         The ``load=False`` use case adds the caveat that the given 
    3971         object has to be in a "clean" state, that is, has no pending changes 
    3972         to be flushed - even if the incoming object is detached from any 
    3973         :class:`.Session`.   This is so that when 
    3974         the merge operation populates local attributes and 
    3975         cascades to related objects and 
    3976         collections, the values can be "stamped" onto the 
    3977         target object as is, without generating any history or attribute 
    3978         events, and without the need to reconcile the incoming data with 
    3979         any existing related objects or collections that might not 
    3980         be loaded.  The resulting objects from ``load=False`` are always 
    3981         produced as "clean", so it is only appropriate that the given objects 
    3982         should be "clean" as well, else this suggests a mis-use of the 
    3983         method. 
    3984        :param options: optional sequence of loader options which will be 
    3985         applied to the :meth:`_orm.Session.get` method when the merge 
    3986         operation loads the existing version of the object from the database. 
    3987 
    3988         .. versionadded:: 1.4.24 
    3989 
    3990 
    3991        .. seealso:: 
    3992 
    3993            :func:`.make_transient_to_detached` - provides for an alternative 
    3994            means of "merging" a single object into the :class:`.Session` 
    3995 
    3996            :meth:`.Session.merge_all` - multiple instance version 
    3997 
    3998        """ 
    3999 
    4000        if self._warn_on_events: 
    4001            self._flush_warning("Session.merge()") 
    4002 
    4003        if load: 
    4004            # flush current contents if we expect to load data 
    4005            self._autoflush() 
    4006 
    4007        with self.no_autoflush: 
    4008            return self._merge( 
    4009                object_state(instance), 
    4010                attributes.instance_dict(instance), 
    4011                load=load, 
    4012                options=options, 
    4013                _recursive={}, 
    4014                _resolve_conflict_map={}, 
    4015            ) 
    4016 
    4017    def merge_all( 
    4018        self, 
    4019        instances: Iterable[_O], 
    4020        *, 
    4021        load: bool = True, 
    4022        options: Optional[Sequence[ORMOption]] = None, 
    4023    ) -> Sequence[_O]: 
    4024        """Calls :meth:`.Session.merge` on multiple instances. 
    4025 
    4026        .. seealso:: 
    4027 
    4028            :meth:`.Session.merge` - main documentation on merge 
    4029 
    4030        .. versionadded:: 2.1 
    4031 
    4032        """ 
    4033 
    4034        if self._warn_on_events: 
    4035            self._flush_warning("Session.merge_all()") 
    4036 
    4037        if load: 
    4038            # flush current contents if we expect to load data 
    4039            self._autoflush() 
    4040 
    4041        return [ 
    4042            self._merge( 
    4043                object_state(instance), 
    4044                attributes.instance_dict(instance), 
    4045                load=load, 
    4046                options=options, 
    4047                _recursive={}, 
    4048                _resolve_conflict_map={}, 
    4049            ) 
    4050            for instance in instances 
    4051        ] 
    4052 
    4053    def _merge( 
    4054        self, 
    4055        state: InstanceState[_O], 
    4056        state_dict: _InstanceDict, 
    4057        *, 
    4058        options: Optional[Sequence[ORMOption]] = None, 
    4059        load: bool, 
    4060        _recursive: Dict[Any, object], 
    4061        _resolve_conflict_map: Dict[_IdentityKeyType[Any], object], 
    4062    ) -> _O: 
    4063        mapper: Mapper[_O] = _state_mapper(state) 
    4064        if state in _recursive: 
    4065            return cast(_O, _recursive[state]) 
    4066 
    4067        new_instance = False 
    4068        key = state.key 
    4069 
    4070        merged: Optional[_O] 
    4071 
    4072        if key is None: 
    4073            if state in self._new: 
    4074                util.warn( 
    4075                    "Instance %s is already pending in this Session yet is " 
    4076                    "being merged again; this is probably not what you want " 
    4077                    "to do" % state_str(state) 
    4078                ) 
    4079 
    4080            if not load: 
    4081                raise sa_exc.InvalidRequestError( 
    4082                    "merge() with load=False option does not support " 
    4083                    "objects transient (i.e. unpersisted) objects.  flush() " 
    4084                    "all changes on mapped instances before merging with " 
    4085                    "load=False." 
    4086                ) 
    4087            key = mapper._identity_key_from_state(state) 
    4088            key_is_persistent = LoaderCallableStatus.NEVER_SET not in key[ 
    4089                1 
    4090            ] and ( 
    4091                not _none_set.intersection(key[1]) 
    4092                or ( 
    4093                    mapper.allow_partial_pks 
    4094                    and not _none_set.issuperset(key[1]) 
    4095                ) 
    4096            ) 
    4097        else: 
    4098            key_is_persistent = True 
    4099 
    4100        merged = self.identity_map.get(key) 
    4101 
    4102        if merged is None: 
    4103            if key_is_persistent and key in _resolve_conflict_map: 
    4104                merged = cast(_O, _resolve_conflict_map[key]) 
    4105 
    4106            elif not load: 
    4107                if state.modified: 
    4108                    raise sa_exc.InvalidRequestError( 
    4109                        "merge() with load=False option does not support " 
    4110                        "objects marked as 'dirty'.  flush() all changes on " 
    4111                        "mapped instances before merging with load=False." 
    4112                    ) 
    4113                merged = mapper.class_manager.new_instance() 
    4114                merged_state = attributes.instance_state(merged) 
    4115                merged_state.key = key 
    4116                self._update_impl(merged_state) 
    4117                new_instance = True 
    4118 
    4119            elif key_is_persistent: 
    4120                merged = self.get( 
    4121                    mapper.class_, 
    4122                    key[1], 
    4123                    identity_token=key[2], 
    4124                    options=options, 
    4125                ) 
    4126 
    4127        if merged is None: 
    4128            merged = mapper.class_manager.new_instance() 
    4129            merged_state = attributes.instance_state(merged) 
    4130            merged_dict = attributes.instance_dict(merged) 
    4131            new_instance = True 
    4132            self._save_or_update_state(merged_state) 
    4133        else: 
    4134            merged_state = attributes.instance_state(merged) 
    4135            merged_dict = attributes.instance_dict(merged) 
    4136 
    4137        _recursive[state] = merged 
    4138        _resolve_conflict_map[key] = merged 
    4139 
    4140        # check that we didn't just pull the exact same 
    4141        # state out. 
    4142        if state is not merged_state: 
    4143            # version check if applicable 
    4144            if mapper.version_id_col is not None: 
    4145                existing_version = mapper._get_state_attr_by_column( 
    4146                    state, 
    4147                    state_dict, 
    4148                    mapper.version_id_col, 
    4149                    passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 
    4150                ) 
    4151 
    4152                merged_version = mapper._get_state_attr_by_column( 
    4153                    merged_state, 
    4154                    merged_dict, 
    4155                    mapper.version_id_col, 
    4156                    passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 
    4157                ) 
    4158 
    4159                if ( 
    4160                    existing_version 
    4161                    is not LoaderCallableStatus.PASSIVE_NO_RESULT 
    4162                    and merged_version 
    4163                    is not LoaderCallableStatus.PASSIVE_NO_RESULT 
    4164                    and existing_version != merged_version 
    4165                ): 
    4166                    raise exc.StaleDataError( 
    4167                        "Version id '%s' on merged state %s " 
    4168                        "does not match existing version '%s'. " 
    4169                        "Leave the version attribute unset when " 
    4170                        "merging to update the most recent version." 
    4171                        % ( 
    4172                            existing_version, 
    4173                            state_str(merged_state), 
    4174                            merged_version, 
    4175                        ) 
    4176                    ) 
    4177 
    4178            merged_state.load_path = state.load_path 
    4179            merged_state.load_options = state.load_options 
    4180 
    4181            # since we are copying load_options, we need to copy 
    4182            # the callables_ that would have been generated by those 
    4183            # load_options. 
    4184            # assumes that the callables we put in state.callables_ 
    4185            # are not instance-specific (which they should not be) 
    4186            merged_state._copy_callables(state) 
    4187 
    4188            for prop in mapper.iterate_properties: 
    4189                prop.merge( 
    4190                    self, 
    4191                    state, 
    4192                    state_dict, 
    4193                    merged_state, 
    4194                    merged_dict, 
    4195                    load, 
    4196                    _recursive, 
    4197                    _resolve_conflict_map, 
    4198                ) 
    4199 
    4200        if not load: 
    4201            # remove any history 
    4202            merged_state._commit_all(merged_dict, self.identity_map) 
    4203            merged_state.manager.dispatch._sa_event_merge_wo_load( 
    4204                merged_state, None 
    4205            ) 
    4206 
    4207        if new_instance: 
    4208            merged_state.manager.dispatch.load(merged_state, None) 
    4209 
    4210        return merged 
    4211 
    4212    def _validate_persistent(self, state: InstanceState[Any]) -> None: 
    4213        if not self.identity_map.contains_state(state): 
    4214            raise sa_exc.InvalidRequestError( 
    4215                "Instance '%s' is not persistent within this Session" 
    4216                % state_str(state) 
    4217            ) 
    4218 
    4219    def _save_impl(self, state: InstanceState[Any]) -> None: 
    4220        if state.key is not None: 
    4221            raise sa_exc.InvalidRequestError( 
    4222                "Object '%s' already has an identity - " 
    4223                "it can't be registered as pending" % state_str(state) 
    4224            ) 
    4225 
    4226        obj = state.obj() 
    4227        to_attach = self._before_attach(state, obj) 
    4228        if state not in self._new: 
    4229            self._new[state] = obj 
    4230            state.insert_order = len(self._new) 
    4231        if to_attach: 
    4232            self._after_attach(state, obj) 
    4233 
    4234    def _update_impl( 
    4235        self, state: InstanceState[Any], revert_deletion: bool = False 
    4236    ) -> None: 
    4237        if state.key is None: 
    4238            raise sa_exc.InvalidRequestError( 
    4239                "Instance '%s' is not persisted" % state_str(state) 
    4240            ) 
    4241 
    4242        if state._deleted: 
    4243            if revert_deletion: 
    4244                if not state._attached: 
    4245                    return 
    4246                del state._deleted 
    4247            else: 
    4248                raise sa_exc.InvalidRequestError( 
    4249                    "Instance '%s' has been deleted.  " 
    4250                    "Use the make_transient() " 
    4251                    "function to send this object back " 
    4252                    "to the transient state." % state_str(state) 
    4253                ) 
    4254 
    4255        obj = state.obj() 
    4256 
    4257        # check for late gc 
    4258        if obj is None: 
    4259            return 
    4260 
    4261        to_attach = self._before_attach(state, obj) 
    4262 
    4263        self._deleted.pop(state, None) 
    4264        if revert_deletion: 
    4265            self.identity_map.replace(state) 
    4266        else: 
    4267            self.identity_map.add(state) 
    4268 
    4269        if to_attach: 
    4270            self._after_attach(state, obj) 
    4271        elif revert_deletion: 
    4272            self.dispatch.deleted_to_persistent(self, state) 
    4273 
    4274    def _save_or_update_impl(self, state: InstanceState[Any]) -> None: 
    4275        if state.key is None: 
    4276            self._save_impl(state) 
    4277        else: 
    4278            self._update_impl(state) 
    4279 
    4280    def enable_relationship_loading(self, obj: object) -> None: 
    4281        """Associate an object with this :class:`.Session` for related 
    4282        object loading. 
    4283 
    4284        .. warning:: 
    4285 
    4286            :meth:`.enable_relationship_loading` exists to serve special 
    4287            use cases and is not recommended for general use. 
    4288 
    4289        Accesses of attributes mapped with :func:`_orm.relationship` 
    4290        will attempt to load a value from the database using this 
    4291        :class:`.Session` as the source of connectivity.  The values 
    4292        will be loaded based on foreign key and primary key values 
    4293        present on this object - if not present, then those relationships 
    4294        will be unavailable. 
    4295 
    4296        The object will be attached to this session, but will 
    4297        **not** participate in any persistence operations; its state 
    4298        for almost all purposes will remain either "transient" or 
    4299        "detached", except for the case of relationship loading. 
    4300 
    4301        Also note that backrefs will often not work as expected. 
    4302        Altering a relationship-bound attribute on the target object 
    4303        may not fire off a backref event, if the effective value 
    4304        is what was already loaded from a foreign-key-holding value. 
    4305 
    4306        The :meth:`.Session.enable_relationship_loading` method is 
    4307        similar to the ``load_on_pending`` flag on :func:`_orm.relationship`. 
    4308        Unlike that flag, :meth:`.Session.enable_relationship_loading` allows 
    4309        an object to remain transient while still being able to load 
    4310        related items. 
    4311 
    4312        To make a transient object associated with a :class:`.Session` 
    4313        via :meth:`.Session.enable_relationship_loading` pending, add 
    4314        it to the :class:`.Session` using :meth:`.Session.add` normally. 
    4315        If the object instead represents an existing identity in the database, 
    4316        it should be merged using :meth:`.Session.merge`. 
    4317 
    4318        :meth:`.Session.enable_relationship_loading` does not improve 
    4319        behavior when the ORM is used normally - object references should be 
    4320        constructed at the object level, not at the foreign key level, so 
    4321        that they are present in an ordinary way before flush() 
    4322        proceeds.  This method is not intended for general use. 
    4323 
    4324        .. seealso:: 
    4325 
    4326            :paramref:`_orm.relationship.load_on_pending` - this flag 
    4327            allows per-relationship loading of many-to-ones on items that 
    4328            are pending. 
    4329 
    4330            :func:`.make_transient_to_detached` - allows for an object to 
    4331            be added to a :class:`.Session` without SQL emitted, which then 
    4332            will unexpire attributes on access. 
    4333 
    4334        """ 
    4335        try: 
    4336            state = attributes.instance_state(obj) 
    4337        except exc.NO_STATE as err: 
    4338            raise exc.UnmappedInstanceError(obj) from err 
    4339 
    4340        to_attach = self._before_attach(state, obj) 
    4341        state._load_pending = True 
    4342        if to_attach: 
    4343            self._after_attach(state, obj) 
    4344 
    4345    def _before_attach(self, state: InstanceState[Any], obj: object) -> bool: 
    4346        self._autobegin_t() 
    4347 
    4348        if state.session_id == self.hash_key: 
    4349            return False 
    4350 
    4351        if state.session_id and state.session_id in _sessions: 
    4352            raise sa_exc.InvalidRequestError( 
    4353                "Object '%s' is already attached to session '%s' " 
    4354                "(this is '%s')" 
    4355                % (state_str(state), state.session_id, self.hash_key) 
    4356            ) 
    4357 
    4358        self.dispatch.before_attach(self, state) 
    4359 
    4360        return True 
    4361 
    4362    def _after_attach(self, state: InstanceState[Any], obj: object) -> None: 
    4363        state.session_id = self.hash_key 
    4364        if state.modified and state._strong_obj is None: 
    4365            state._strong_obj = obj 
    4366        self.dispatch.after_attach(self, state) 
    4367 
    4368        if state.key: 
    4369            self.dispatch.detached_to_persistent(self, state) 
    4370        else: 
    4371            self.dispatch.transient_to_pending(self, state) 
    4372 
    4373    def __contains__(self, instance: object) -> bool: 
    4374        """Return True if the instance is associated with this session. 
    4375 
    4376        The instance may be pending or persistent within the Session for a 
    4377        result of True. 
    4378 
    4379        """ 
    4380        try: 
    4381            state = attributes.instance_state(instance) 
    4382        except exc.NO_STATE as err: 
    4383            raise exc.UnmappedInstanceError(instance) from err 
    4384        return self._contains_state(state) 
    4385 
    4386    def __iter__(self) -> Iterator[object]: 
    4387        """Iterate over all pending or persistent instances within this 
    4388        Session. 
    4389 
    4390        """ 
    4391        return iter( 
    4392            list(self._new.values()) + list(self.identity_map.values()) 
    4393        ) 
    4394 
    4395    def _contains_state(self, state: InstanceState[Any]) -> bool: 
    4396        return state in self._new or self.identity_map.contains_state(state) 
    4397 
    4398    def flush(self, objects: Optional[Sequence[Any]] = None) -> None: 
    4399        """Flush all the object changes to the database. 
    4400 
    4401        Writes out all pending object creations, deletions and modifications 
    4402        to the database as INSERTs, DELETEs, UPDATEs, etc.  Operations are 
    4403        automatically ordered by the Session's unit of work dependency 
    4404        solver. 
    4405 
    4406        Database operations will be issued in the current transactional 
    4407        context and do not affect the state of the transaction, unless an 
    4408        error occurs, in which case the entire transaction is rolled back. 
    4409        You may flush() as often as you like within a transaction to move 
    4410        changes from Python to the database's transaction buffer. 
    4411 
    4412        :param objects: Optional; restricts the flush operation to operate 
    4413          only on elements that are in the given collection. 
    4414 
    4415          This feature is for an extremely narrow set of use cases where 
    4416          particular objects may need to be operated upon before the 
    4417          full flush() occurs.  It is not intended for general use. 
    4418 
    4419          .. deprecated:: 2.1 
    4420 
    4421        """ 
    4422 
    4423        if self._flushing: 
    4424            raise sa_exc.InvalidRequestError("Session is already flushing") 
    4425 
    4426        if self._is_clean(): 
    4427            return 
    4428        try: 
    4429            self._flushing = True 
    4430            self._flush(objects) 
    4431        finally: 
    4432            self._flushing = False 
    4433 
    4434    def _flush_warning(self, method: Any) -> None: 
    4435        util.warn( 
    4436            "Usage of the '%s' operation is not currently supported " 
    4437            "within the execution stage of the flush process. " 
    4438            "Results may not be consistent.  Consider using alternative " 
    4439            "event listeners or connection-level operations instead." % method 
    4440        ) 
    4441 
    4442    def _is_clean(self) -> bool: 
    4443        return ( 
    4444            not self.identity_map.check_modified() 
    4445            and not self._deleted 
    4446            and not self._new 
    4447        ) 
    4448 
    4449    # have this here since it otherwise causes issues with the proxy 
    4450    # method generation 
    4451    @deprecated_params( 
    4452        objects=( 
    4453            "2.1", 
    4454            "The `objects` parameter of `Session.flush` is deprecated", 
    4455        ) 
    4456    ) 
    4457    def _flush(self, objects: Optional[Sequence[object]] = None) -> None: 
    4458        dirty = self._dirty_states 
    4459        if not dirty and not self._deleted and not self._new: 
    4460            self.identity_map._modified.clear() 
    4461            return 
    4462 
    4463        flush_context = UOWTransaction(self) 
    4464 
    4465        if self.dispatch.before_flush: 
    4466            self.dispatch.before_flush(self, flush_context, objects) 
    4467            # re-establish "dirty states" in case the listeners 
    4468            # added 
    4469            dirty = self._dirty_states 
    4470 
    4471        deleted = set(self._deleted) 
    4472        new = set(self._new) 
    4473 
    4474        dirty = set(dirty).difference(deleted) 
    4475 
    4476        # create the set of all objects we want to operate upon 
    4477        if objects: 
    4478            # specific list passed in 
    4479            objset = set() 
    4480            for o in objects: 
    4481                try: 
    4482                    state = attributes.instance_state(o) 
    4483 
    4484                except exc.NO_STATE as err: 
    4485                    raise exc.UnmappedInstanceError(o) from err 
    4486                objset.add(state) 
    4487        else: 
    4488            objset = None 
    4489 
    4490        # store objects whose fate has been decided 
    4491        processed = set() 
    4492 
    4493        # put all saves/updates into the flush context.  detect top-level 
    4494        # orphans and throw them into deleted. 
    4495        if objset: 
    4496            proc = new.union(dirty).intersection(objset).difference(deleted) 
    4497        else: 
    4498            proc = new.union(dirty).difference(deleted) 
    4499 
    4500        for state in proc: 
    4501            is_orphan = _state_mapper(state)._is_orphan(state) 
    4502 
    4503            is_persistent_orphan = is_orphan and state.has_identity 
    4504 
    4505            if ( 
    4506                is_orphan 
    4507                and not is_persistent_orphan 
    4508                and state._orphaned_outside_of_session 
    4509            ): 
    4510                self._expunge_states([state]) 
    4511            else: 
    4512                _reg = flush_context.register_object( 
    4513                    state, isdelete=is_persistent_orphan 
    4514                ) 
    4515                assert _reg, "Failed to add object to the flush context!" 
    4516                processed.add(state) 
    4517 
    4518        # put all remaining deletes into the flush context. 
    4519        if objset: 
    4520            proc = deleted.intersection(objset).difference(processed) 
    4521        else: 
    4522            proc = deleted.difference(processed) 
    4523        for state in proc: 
    4524            _reg = flush_context.register_object(state, isdelete=True) 
    4525            assert _reg, "Failed to add object to the flush context!" 
    4526 
    4527        if not flush_context.has_work: 
    4528            return 
    4529 
    4530        flush_context.transaction = transaction = self._autobegin_t()._begin() 
    4531        try: 
    4532            self._warn_on_events = True 
    4533            try: 
    4534                flush_context.execute() 
    4535            finally: 
    4536                self._warn_on_events = False 
    4537 
    4538            self.dispatch.after_flush(self, flush_context) 
    4539 
    4540            flush_context.finalize_flush_changes() 
    4541 
    4542            if not objects and self.identity_map._modified: 
    4543                len_ = len(self.identity_map._modified) 
    4544 
    4545                statelib.InstanceState._commit_all_states( 
    4546                    [ 
    4547                        (state, state.dict) 
    4548                        for state in self.identity_map._modified 
    4549                    ], 
    4550                    instance_dict=self.identity_map, 
    4551                ) 
    4552                util.warn( 
    4553                    "Attribute history events accumulated on %d " 
    4554                    "previously clean instances " 
    4555                    "within inner-flush event handlers have been " 
    4556                    "reset, and will not result in database updates. " 
    4557                    "Consider using set_committed_value() within " 
    4558                    "inner-flush event handlers to avoid this warning." % len_ 
    4559                ) 
    4560 
    4561            # useful assertions: 
    4562            # if not objects: 
    4563            #    assert not self.identity_map._modified 
    4564            # else: 
    4565            #    assert self.identity_map._modified == \ 
    4566            #            self.identity_map._modified.difference(objects) 
    4567 
    4568            self.dispatch.after_flush_postexec(self, flush_context) 
    4569 
    4570            transaction.commit() 
    4571 
    4572        except: 
    4573            with util.safe_reraise(): 
    4574                transaction.rollback(_capture_exception=True) 
    4575 
    4576    def bulk_save_objects( 
    4577        self, 
    4578        objects: Iterable[object], 
    4579        return_defaults: bool = False, 
    4580        update_changed_only: bool = True, 
    4581        preserve_order: bool = True, 
    4582    ) -> None: 
    4583        """Perform a bulk save of the given list of objects. 
    4584 
    4585        .. legacy:: 
    4586 
    4587            This method is a legacy feature as of the 2.0 series of 
    4588            SQLAlchemy.   For modern bulk INSERT and UPDATE, see 
    4589            the sections :ref:`orm_queryguide_bulk_insert` and 
    4590            :ref:`orm_queryguide_bulk_update`. 
    4591 
    4592            For general INSERT and UPDATE of existing ORM mapped objects, 
    4593            prefer standard :term:`unit of work` data management patterns, 
    4594            introduced in the :ref:`unified_tutorial` at 
    4595            :ref:`tutorial_orm_data_manipulation`.  SQLAlchemy 2.0 
    4596            now uses :ref:`engine_insertmanyvalues` with modern dialects 
    4597            which solves previous issues of bulk INSERT slowness. 
    4598 
    4599        :param objects: a sequence of mapped object instances.  The mapped 
    4600         objects are persisted as is, and are **not** associated with the 
    4601         :class:`.Session` afterwards. 
    4602 
    4603         For each object, whether the object is sent as an INSERT or an 
    4604         UPDATE is dependent on the same rules used by the :class:`.Session` 
    4605         in traditional operation; if the object has the 
    4606         :attr:`.InstanceState.key` 
    4607         attribute set, then the object is assumed to be "detached" and 
    4608         will result in an UPDATE.  Otherwise, an INSERT is used. 
    4609 
    4610         In the case of an UPDATE, statements are grouped based on which 
    4611         attributes have changed, and are thus to be the subject of each 
    4612         SET clause.  If ``update_changed_only`` is False, then all 
    4613         attributes present within each object are applied to the UPDATE 
    4614         statement, which may help in allowing the statements to be grouped 
    4615         together into a larger executemany(), and will also reduce the 
    4616         overhead of checking history on attributes. 
    4617 
    4618        :param return_defaults: when True, rows that are missing values which 
    4619         generate defaults, namely integer primary key defaults and sequences, 
    4620         will be inserted **one at a time**, so that the primary key value 
    4621         is available.  In particular this will allow joined-inheritance 
    4622         and other multi-table mappings to insert correctly without the need 
    4623         to provide primary key values ahead of time; however, 
    4624         :paramref:`.Session.bulk_save_objects.return_defaults` **greatly 
    4625         reduces the performance gains** of the method overall.  It is strongly 
    4626         advised to please use the standard :meth:`_orm.Session.add_all` 
    4627         approach. 
    4628 
    4629        :param update_changed_only: when True, UPDATE statements are rendered 
    4630         based on those attributes in each state that have logged changes. 
    4631         When False, all attributes present are rendered into the SET clause 
    4632         with the exception of primary key attributes. 
    4633 
    4634        :param preserve_order: when True, the order of inserts and updates 
    4635         matches exactly the order in which the objects are given.   When 
    4636         False, common types of objects are grouped into inserts 
    4637         and updates, to allow for more batching opportunities. 
    4638 
    4639        .. seealso:: 
    4640 
    4641            :doc:`queryguide/dml` 
    4642 
    4643            :meth:`.Session.bulk_insert_mappings` 
    4644 
    4645            :meth:`.Session.bulk_update_mappings` 
    4646 
    4647        """ 
    4648 
    4649        obj_states: Iterable[InstanceState[Any]] 
    4650 
    4651        obj_states = (attributes.instance_state(obj) for obj in objects) 
    4652 
    4653        if not preserve_order: 
    4654            # the purpose of this sort is just so that common mappers 
    4655            # and persistence states are grouped together, so that groupby 
    4656            # will return a single group for a particular type of mapper. 
    4657            # it's not trying to be deterministic beyond that. 
    4658            obj_states = sorted( 
    4659                obj_states, 
    4660                key=lambda state: (id(state.mapper), state.key is not None), 
    4661            ) 
    4662 
    4663        def grouping_key( 
    4664            state: InstanceState[_O], 
    4665        ) -> Tuple[Mapper[_O], bool]: 
    4666            return (state.mapper, state.key is not None) 
    4667 
    4668        for (mapper, isupdate), states in itertools.groupby( 
    4669            obj_states, grouping_key 
    4670        ): 
    4671            self._bulk_save_mappings( 
    4672                mapper, 
    4673                states, 
    4674                isupdate=isupdate, 
    4675                isstates=True, 
    4676                return_defaults=return_defaults, 
    4677                update_changed_only=update_changed_only, 
    4678                render_nulls=False, 
    4679            ) 
    4680 
    4681    def bulk_insert_mappings( 
    4682        self, 
    4683        mapper: Mapper[Any], 
    4684        mappings: Iterable[Dict[str, Any]], 
    4685        return_defaults: bool = False, 
    4686        render_nulls: bool = False, 
    4687    ) -> None: 
    4688        """Perform a bulk insert of the given list of mapping dictionaries. 
    4689 
    4690        .. legacy:: 
    4691 
    4692            This method is a legacy feature as of the 2.0 series of 
    4693            SQLAlchemy.   For modern bulk INSERT and UPDATE, see 
    4694            the sections :ref:`orm_queryguide_bulk_insert` and 
    4695            :ref:`orm_queryguide_bulk_update`.  The 2.0 API shares 
    4696            implementation details with this method and adds new features 
    4697            as well. 
    4698 
    4699        :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 
    4700         object, 
    4701         representing the single kind of object represented within the mapping 
    4702         list. 
    4703 
    4704        :param mappings: a sequence of dictionaries, each one containing the 
    4705         state of the mapped row to be inserted, in terms of the attribute 
    4706         names on the mapped class.   If the mapping refers to multiple tables, 
    4707         such as a joined-inheritance mapping, each dictionary must contain all 
    4708         keys to be populated into all tables. 
    4709 
    4710        :param return_defaults: when True, the INSERT process will be altered 
    4711         to ensure that newly generated primary key values will be fetched. 
    4712         The rationale for this parameter is typically to enable 
    4713         :ref:`Joined Table Inheritance <joined_inheritance>` mappings to 
    4714         be bulk inserted. 
    4715 
    4716         .. note:: for backends that don't support RETURNING, the 
    4717            :paramref:`_orm.Session.bulk_insert_mappings.return_defaults` 
    4718            parameter can significantly decrease performance as INSERT 
    4719            statements can no longer be batched.   See 
    4720            :ref:`engine_insertmanyvalues` 
    4721            for background on which backends are affected. 
    4722 
    4723        :param render_nulls: When True, a value of ``None`` will result 
    4724         in a NULL value being included in the INSERT statement, rather 
    4725         than the column being omitted from the INSERT.   This allows all 
    4726         the rows being INSERTed to have the identical set of columns which 
    4727         allows the full set of rows to be batched to the DBAPI.  Normally, 
    4728         each column-set that contains a different combination of NULL values 
    4729         than the previous row must omit a different series of columns from 
    4730         the rendered INSERT statement, which means it must be emitted as a 
    4731         separate statement.   By passing this flag, the full set of rows 
    4732         are guaranteed to be batchable into one batch; the cost however is 
    4733         that server-side defaults which are invoked by an omitted column will 
    4734         be skipped, so care must be taken to ensure that these are not 
    4735         necessary. 
    4736 
    4737         .. warning:: 
    4738 
    4739            When this flag is set, **server side default SQL values will 
    4740            not be invoked** for those columns that are inserted as NULL; 
    4741            the NULL value will be sent explicitly.   Care must be taken 
    4742            to ensure that no server-side default functions need to be 
    4743            invoked for the operation as a whole. 
    4744 
    4745        .. seealso:: 
    4746 
    4747            :doc:`queryguide/dml` 
    4748 
    4749            :meth:`.Session.bulk_save_objects` 
    4750 
    4751            :meth:`.Session.bulk_update_mappings` 
    4752 
    4753        """ 
    4754        self._bulk_save_mappings( 
    4755            mapper, 
    4756            mappings, 
    4757            isupdate=False, 
    4758            isstates=False, 
    4759            return_defaults=return_defaults, 
    4760            update_changed_only=False, 
    4761            render_nulls=render_nulls, 
    4762        ) 
    4763 
    4764    def bulk_update_mappings( 
    4765        self, mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]] 
    4766    ) -> None: 
    4767        """Perform a bulk update of the given list of mapping dictionaries. 
    4768 
    4769        .. legacy:: 
    4770 
    4771            This method is a legacy feature as of the 2.0 series of 
    4772            SQLAlchemy.   For modern bulk INSERT and UPDATE, see 
    4773            the sections :ref:`orm_queryguide_bulk_insert` and 
    4774            :ref:`orm_queryguide_bulk_update`.  The 2.0 API shares 
    4775            implementation details with this method and adds new features 
    4776            as well. 
    4777 
    4778        :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 
    4779         object, 
    4780         representing the single kind of object represented within the mapping 
    4781         list. 
    4782 
    4783        :param mappings: a sequence of dictionaries, each one containing the 
    4784         state of the mapped row to be updated, in terms of the attribute names 
    4785         on the mapped class.   If the mapping refers to multiple tables, such 
    4786         as a joined-inheritance mapping, each dictionary may contain keys 
    4787         corresponding to all tables.   All those keys which are present and 
    4788         are not part of the primary key are applied to the SET clause of the 
    4789         UPDATE statement; the primary key values, which are required, are 
    4790         applied to the WHERE clause. 
    4791 
    4792 
    4793        .. seealso:: 
    4794 
    4795            :doc:`queryguide/dml` 
    4796 
    4797            :meth:`.Session.bulk_insert_mappings` 
    4798 
    4799            :meth:`.Session.bulk_save_objects` 
    4800 
    4801        """ 
    4802        self._bulk_save_mappings( 
    4803            mapper, 
    4804            mappings, 
    4805            isupdate=True, 
    4806            isstates=False, 
    4807            return_defaults=False, 
    4808            update_changed_only=False, 
    4809            render_nulls=False, 
    4810        ) 
    4811 
    4812    def _bulk_save_mappings( 
    4813        self, 
    4814        mapper: Mapper[_O], 
    4815        mappings: Union[Iterable[InstanceState[_O]], Iterable[Dict[str, Any]]], 
    4816        *, 
    4817        isupdate: bool, 
    4818        isstates: bool, 
    4819        return_defaults: bool, 
    4820        update_changed_only: bool, 
    4821        render_nulls: bool, 
    4822    ) -> None: 
    4823        mapper = _class_to_mapper(mapper) 
    4824        self._flushing = True 
    4825 
    4826        transaction = self._autobegin_t()._begin() 
    4827        try: 
    4828            if isupdate: 
    4829                bulk_persistence._bulk_update( 
    4830                    mapper, 
    4831                    mappings, 
    4832                    transaction, 
    4833                    isstates=isstates, 
    4834                    update_changed_only=update_changed_only, 
    4835                ) 
    4836            else: 
    4837                bulk_persistence._bulk_insert( 
    4838                    mapper, 
    4839                    mappings, 
    4840                    transaction, 
    4841                    isstates=isstates, 
    4842                    return_defaults=return_defaults, 
    4843                    render_nulls=render_nulls, 
    4844                ) 
    4845            transaction.commit() 
    4846 
    4847        except: 
    4848            with util.safe_reraise(): 
    4849                transaction.rollback(_capture_exception=True) 
    4850        finally: 
    4851            self._flushing = False 
    4852 
    4853    def is_modified( 
    4854        self, instance: object, include_collections: bool = True 
    4855    ) -> bool: 
    4856        r"""Return ``True`` if the given instance has locally 
    4857        modified attributes. 
    4858 
    4859        This method retrieves the history for each instrumented 
    4860        attribute on the instance and performs a comparison of the current 
    4861        value to its previously flushed or committed value, if any. 
    4862 
    4863        It is in effect a more expensive and accurate 
    4864        version of checking for the given instance in the 
    4865        :attr:`.Session.dirty` collection; a full test for 
    4866        each attribute's net "dirty" status is performed. 
    4867 
    4868        E.g.:: 
    4869 
    4870            return session.is_modified(someobject) 
    4871 
    4872        A few caveats to this method apply: 
    4873 
    4874        * Instances present in the :attr:`.Session.dirty` collection may 
    4875          report ``False`` when tested with this method.  This is because 
    4876          the object may have received change events via attribute mutation, 
    4877          thus placing it in :attr:`.Session.dirty`, but ultimately the state 
    4878          is the same as that loaded from the database, resulting in no net 
    4879          change here. 
    4880        * Scalar attributes may not have recorded the previously set 
    4881          value when a new value was applied, if the attribute was not loaded, 
    4882          or was expired, at the time the new value was received - in these 
    4883          cases, the attribute is assumed to have a change, even if there is 
    4884          ultimately no net change against its database value. SQLAlchemy in 
    4885          most cases does not need the "old" value when a set event occurs, so 
    4886          it skips the expense of a SQL call if the old value isn't present, 
    4887          based on the assumption that an UPDATE of the scalar value is 
    4888          usually needed, and in those few cases where it isn't, is less 
    4889          expensive on average than issuing a defensive SELECT. 
    4890 
    4891          The "old" value is fetched unconditionally upon set only if the 
    4892          attribute container has the ``active_history`` flag set to ``True``. 
    4893          This flag is set typically for primary key attributes and scalar 
    4894          object references that are not a simple many-to-one.  To set this 
    4895          flag for any arbitrary mapped column, use the ``active_history`` 
    4896          argument with :func:`.column_property`. 
    4897 
    4898        :param instance: mapped instance to be tested for pending changes. 
    4899        :param include_collections: Indicates if multivalued collections 
    4900         should be included in the operation.  Setting this to ``False`` is a 
    4901         way to detect only local-column based properties (i.e. scalar columns 
    4902         or many-to-one foreign keys) that would result in an UPDATE for this 
    4903         instance upon flush. 
    4904 
    4905        """ 
    4906        state = object_state(instance) 
    4907 
    4908        if not state.modified: 
    4909            return False 
    4910 
    4911        dict_ = state.dict 
    4912 
    4913        for attr in state.manager.attributes: 
    4914            if ( 
    4915                not include_collections 
    4916                and hasattr(attr.impl, "get_collection") 
    4917            ) or not hasattr(attr.impl, "get_history"): 
    4918                continue 
    4919 
    4920            (added, unchanged, deleted) = attr.impl.get_history( 
    4921                state, dict_, passive=PassiveFlag.NO_CHANGE 
    4922            ) 
    4923 
    4924            if added or deleted: 
    4925                return True 
    4926        else: 
    4927            return False 
    4928 
    4929    @property 
    4930    def is_active(self) -> bool: 
    4931        """True if this :class:`.Session` not in "partial rollback" state. 
    4932 
    4933        .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins 
    4934           a new transaction immediately, so this attribute will be False 
    4935           when the :class:`_orm.Session` is first instantiated. 
    4936 
    4937        "partial rollback" state typically indicates that the flush process 
    4938        of the :class:`_orm.Session` has failed, and that the 
    4939        :meth:`_orm.Session.rollback` method must be emitted in order to 
    4940        fully roll back the transaction. 
    4941 
    4942        If this :class:`_orm.Session` is not in a transaction at all, the 
    4943        :class:`_orm.Session` will autobegin when it is first used, so in this 
    4944        case :attr:`_orm.Session.is_active` will return True. 
    4945 
    4946        Otherwise, if this :class:`_orm.Session` is within a transaction, 
    4947        and that transaction has not been rolled back internally, the 
    4948        :attr:`_orm.Session.is_active` will also return True. 
    4949 
    4950        .. seealso:: 
    4951 
    4952            :ref:`faq_session_rollback` 
    4953 
    4954            :meth:`_orm.Session.in_transaction` 
    4955 
    4956        """ 
    4957        return self._transaction is None or self._transaction.is_active 
    4958 
    4959    @property 
    4960    def _dirty_states(self) -> Iterable[InstanceState[Any]]: 
    4961        """The set of all persistent states considered dirty. 
    4962 
    4963        This method returns all states that were modified including 
    4964        those that were possibly deleted. 
    4965 
    4966        """ 
    4967        return self.identity_map._dirty_states() 
    4968 
    4969    @property 
    4970    def dirty(self) -> IdentitySet: 
    4971        """The set of all persistent instances considered dirty. 
    4972 
    4973        E.g.:: 
    4974 
    4975            some_mapped_object in session.dirty 
    4976 
    4977        Instances are considered dirty when they were modified but not 
    4978        deleted. 
    4979 
    4980        Note that this 'dirty' calculation is 'optimistic'; most 
    4981        attribute-setting or collection modification operations will 
    4982        mark an instance as 'dirty' and place it in this set, even if 
    4983        there is no net change to the attribute's value.  At flush 
    4984        time, the value of each attribute is compared to its 
    4985        previously saved value, and if there's no net change, no SQL 
    4986        operation will occur (this is a more expensive operation so 
    4987        it's only done at flush time). 
    4988 
    4989        To check if an instance has actionable net changes to its 
    4990        attributes, use the :meth:`.Session.is_modified` method. 
    4991 
    4992        """ 
    4993        return IdentitySet( 
    4994            [ 
    4995                state.obj() 
    4996                for state in self._dirty_states 
    4997                if state not in self._deleted 
    4998            ] 
    4999        ) 
    5000 
    5001    @property 
    5002    def deleted(self) -> IdentitySet: 
    5003        "The set of all instances marked as 'deleted' within this ``Session``" 
    5004 
    5005        return util.IdentitySet(list(self._deleted.values())) 
    5006 
    5007    @property 
    5008    def new(self) -> IdentitySet: 
    5009        "The set of all instances marked as 'new' within this ``Session``." 
    5010 
    5011        return util.IdentitySet(list(self._new.values())) 
    5012 
    5013 
    5014_S = TypeVar("_S", bound="Session") 
    5015 
    5016 
    5017class sessionmaker(_SessionClassMethods, Generic[_S]): 
    5018    """A configurable :class:`.Session` factory. 
    5019 
    5020    The :class:`.sessionmaker` factory generates new 
    5021    :class:`.Session` objects when called, creating them given 
    5022    the configurational arguments established here. 
    5023 
    5024    e.g.:: 
    5025 
    5026        from sqlalchemy import create_engine 
    5027        from sqlalchemy.orm import sessionmaker 
    5028 
    5029        # an Engine, which the Session will use for connection 
    5030        # resources 
    5031        engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/") 
    5032 
    5033        Session = sessionmaker(engine) 
    5034 
    5035        with Session() as session: 
    5036            session.add(some_object) 
    5037            session.add(some_other_object) 
    5038            session.commit() 
    5039 
    5040    Context manager use is optional; otherwise, the returned 
    5041    :class:`_orm.Session` object may be closed explicitly via the 
    5042    :meth:`_orm.Session.close` method.   Using a 
    5043    ``try:/finally:`` block is optional, however will ensure that the close 
    5044    takes place even if there are database errors:: 
    5045 
    5046        session = Session() 
    5047        try: 
    5048            session.add(some_object) 
    5049            session.add(some_other_object) 
    5050            session.commit() 
    5051        finally: 
    5052            session.close() 
    5053 
    5054    :class:`.sessionmaker` acts as a factory for :class:`_orm.Session` 
    5055    objects in the same way as an :class:`_engine.Engine` acts as a factory 
    5056    for :class:`_engine.Connection` objects.  In this way it also includes 
    5057    a :meth:`_orm.sessionmaker.begin` method, that provides a context 
    5058    manager which both begins and commits a transaction, as well as closes 
    5059    out the :class:`_orm.Session` when complete, rolling back the transaction 
    5060    if any errors occur:: 
    5061 
    5062        Session = sessionmaker(engine) 
    5063 
    5064        with Session.begin() as session: 
    5065            session.add(some_object) 
    5066            session.add(some_other_object) 
    5067        # commits transaction, closes session 
    5068 
    5069    .. versionadded:: 1.4 
    5070 
    5071    When calling upon :class:`_orm.sessionmaker` to construct a 
    5072    :class:`_orm.Session`, keyword arguments may also be passed to the 
    5073    method; these arguments will override that of the globally configured 
    5074    parameters.  Below we use a :class:`_orm.sessionmaker` bound to a certain 
    5075    :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead 
    5076    bound to a specific :class:`_engine.Connection` procured from that engine:: 
    5077 
    5078        Session = sessionmaker(engine) 
    5079 
    5080        # bind an individual session to a connection 
    5081 
    5082        with engine.connect() as connection: 
    5083            with Session(bind=connection) as session: 
    5084                ...  # work with session 
    5085 
    5086    The class also includes a method :meth:`_orm.sessionmaker.configure`, which 
    5087    can be used to specify additional keyword arguments to the factory, which 
    5088    will take effect for subsequent :class:`.Session` objects generated. This 
    5089    is usually used to associate one or more :class:`_engine.Engine` objects 
    5090    with an existing 
    5091    :class:`.sessionmaker` factory before it is first used:: 
    5092 
    5093        # application starts, sessionmaker does not have 
    5094        # an engine bound yet 
    5095        Session = sessionmaker() 
    5096 
    5097        # ... later, when an engine URL is read from a configuration 
    5098        # file or other events allow the engine to be created 
    5099        engine = create_engine("sqlite:///foo.db") 
    5100        Session.configure(bind=engine) 
    5101 
    5102        sess = Session() 
    5103        # work with session 
    5104 
    5105    .. seealso:: 
    5106 
    5107        :ref:`session_getting` - introductory text on creating 
    5108        sessions using :class:`.sessionmaker`. 
    5109 
    5110    """ 
    5111 
    5112    class_: Type[_S] 
    5113 
    5114    @overload 
    5115    def __init__( 
    5116        self, 
    5117        bind: Optional[_SessionBind] = ..., 
    5118        *, 
    5119        class_: Type[_S], 
    5120        autoflush: bool = ..., 
    5121        expire_on_commit: bool = ..., 
    5122        info: Optional[_InfoType] = ..., 
    5123        **kw: Any, 
    5124    ): ... 
    5125 
    5126    @overload 
    5127    def __init__( 
    5128        self: "sessionmaker[Session]", 
    5129        bind: Optional[_SessionBind] = ..., 
    5130        *, 
    5131        autoflush: bool = ..., 
    5132        expire_on_commit: bool = ..., 
    5133        info: Optional[_InfoType] = ..., 
    5134        **kw: Any, 
    5135    ): ... 
    5136 
    5137    def __init__( 
    5138        self, 
    5139        bind: Optional[_SessionBind] = None, 
    5140        *, 
    5141        class_: Type[_S] = Session,  # type: ignore 
    5142        autoflush: bool = True, 
    5143        expire_on_commit: bool = True, 
    5144        info: Optional[_InfoType] = None, 
    5145        **kw: Any, 
    5146    ): 
    5147        r"""Construct a new :class:`.sessionmaker`. 
    5148 
    5149        All arguments here except for ``class_`` correspond to arguments 
    5150        accepted by :class:`.Session` directly.  See the 
    5151        :meth:`.Session.__init__` docstring for more details on parameters. 
    5152 
    5153        :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 
    5154         with 
    5155         which newly created :class:`.Session` objects will be associated. 
    5156        :param class\_: class to use in order to create new :class:`.Session` 
    5157         objects.  Defaults to :class:`.Session`. 
    5158        :param autoflush: The autoflush setting to use with newly created 
    5159         :class:`.Session` objects. 
    5160 
    5161         .. seealso:: 
    5162 
    5163            :ref:`session_flushing` - additional background on autoflush 
    5164 
    5165        :param expire_on_commit=True: the 
    5166         :paramref:`_orm.Session.expire_on_commit` setting to use 
    5167         with newly created :class:`.Session` objects. 
    5168 
    5169        :param info: optional dictionary of information that will be available 
    5170         via :attr:`.Session.info`.  Note this dictionary is *updated*, not 
    5171         replaced, when the ``info`` parameter is specified to the specific 
    5172         :class:`.Session` construction operation. 
    5173 
    5174        :param \**kw: all other keyword arguments are passed to the 
    5175         constructor of newly created :class:`.Session` objects. 
    5176 
    5177        """ 
    5178        kw["bind"] = bind 
    5179        kw["autoflush"] = autoflush 
    5180        kw["expire_on_commit"] = expire_on_commit 
    5181        if info is not None: 
    5182            kw["info"] = info 
    5183        self.kw = kw 
    5184        # make our own subclass of the given class, so that 
    5185        # events can be associated with it specifically. 
    5186        self.class_ = type(class_.__name__, (class_,), {}) 
    5187 
    5188    def begin(self) -> contextlib.AbstractContextManager[_S]: 
    5189        """Produce a context manager that both provides a new 
    5190        :class:`_orm.Session` as well as a transaction that commits. 
    5191 
    5192 
    5193        e.g.:: 
    5194 
    5195            Session = sessionmaker(some_engine) 
    5196 
    5197            with Session.begin() as session: 
    5198                session.add(some_object) 
    5199 
    5200            # commits transaction, closes session 
    5201 
    5202        .. versionadded:: 1.4 
    5203 
    5204 
    5205        """ 
    5206 
    5207        session = self() 
    5208        return session._maker_context_manager() 
    5209 
    5210    def __call__(self, **local_kw: Any) -> _S: 
    5211        """Produce a new :class:`.Session` object using the configuration 
    5212        established in this :class:`.sessionmaker`. 
    5213 
    5214        In Python, the ``__call__`` method is invoked on an object when 
    5215        it is "called" in the same way as a function:: 
    5216 
    5217            Session = sessionmaker(some_engine) 
    5218            session = Session()  # invokes sessionmaker.__call__() 
    5219 
    5220        """ 
    5221        for k, v in self.kw.items(): 
    5222            if k == "info" and "info" in local_kw: 
    5223                d = v.copy() 
    5224                d.update(local_kw["info"]) 
    5225                local_kw["info"] = d 
    5226            else: 
    5227                local_kw.setdefault(k, v) 
    5228        return self.class_(**local_kw) 
    5229 
    5230    def configure(self, **new_kw: Any) -> None: 
    5231        """(Re)configure the arguments for this sessionmaker. 
    5232 
    5233        e.g.:: 
    5234 
    5235            Session = sessionmaker() 
    5236 
    5237            Session.configure(bind=create_engine("sqlite://")) 
    5238        """ 
    5239        self.kw.update(new_kw) 
    5240 
    5241    def __repr__(self) -> str: 
    5242        return "%s(class_=%r, %s)" % ( 
    5243            self.__class__.__name__, 
    5244            self.class_.__name__, 
    5245            ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 
    5246        ) 
    5247 
    5248 
    5249def close_all_sessions() -> None: 
    5250    """Close all sessions in memory. 
    5251 
    5252    This function consults a global registry of all :class:`.Session` objects 
    5253    and calls :meth:`.Session.close` on them, which resets them to a clean 
    5254    state. 
    5255 
    5256    This function is not for general use but may be useful for test suites 
    5257    within the teardown scheme. 
    5258 
    5259    """ 
    5260 
    5261    for sess in _sessions.values(): 
    5262        sess.close() 
    5263 
    5264 
    5265def make_transient(instance: object) -> None: 
    5266    """Alter the state of the given instance so that it is :term:`transient`. 
    5267 
    5268    .. note:: 
    5269 
    5270        :func:`.make_transient` is a special-case function for 
    5271        advanced use cases only. 
    5272 
    5273    The given mapped instance is assumed to be in the :term:`persistent` or 
    5274    :term:`detached` state.   The function will remove its association with any 
    5275    :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 
    5276    effect is that the object will behave as though it were newly constructed, 
    5277    except retaining any attribute / collection values that were loaded at the 
    5278    time of the call.   The :attr:`.InstanceState.deleted` flag is also reset 
    5279    if this object had been deleted as a result of using 
    5280    :meth:`.Session.delete`. 
    5281 
    5282    .. warning:: 
    5283 
    5284        :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 
    5285        load ORM-mapped attributes that are not currently loaded at the time 
    5286        the function is called.   This includes attributes which: 
    5287 
    5288        * were expired via :meth:`.Session.expire` 
    5289 
    5290        * were expired as the natural effect of committing a session 
    5291          transaction, e.g. :meth:`.Session.commit` 
    5292 
    5293        * are normally :term:`lazy loaded` but are not currently loaded 
    5294 
    5295        * are "deferred" (see :ref:`orm_queryguide_column_deferral`) and are 
    5296          not yet loaded 
    5297 
    5298        * were not present in the query which loaded this object, such as that 
    5299          which is common in joined table inheritance and other scenarios. 
    5300 
    5301        After :func:`.make_transient` is called, unloaded attributes such 
    5302        as those above will normally resolve to the value ``None`` when 
    5303        accessed, or an empty collection for a collection-oriented attribute. 
    5304        As the object is transient and un-associated with any database 
    5305        identity, it will no longer retrieve these values. 
    5306 
    5307    .. seealso:: 
    5308 
    5309        :func:`.make_transient_to_detached` 
    5310 
    5311    """ 
    5312    state = attributes.instance_state(instance) 
    5313    s = _state_session(state) 
    5314    if s: 
    5315        s._expunge_states([state]) 
    5316 
    5317    # remove expired state 
    5318    state.expired_attributes.clear() 
    5319 
    5320    # remove deferred callables 
    5321    if state.callables: 
    5322        del state.callables 
    5323 
    5324    if state.key: 
    5325        del state.key 
    5326    if state._deleted: 
    5327        del state._deleted 
    5328 
    5329 
    5330def make_transient_to_detached(instance: object) -> None: 
    5331    """Make the given transient instance :term:`detached`. 
    5332 
    5333    .. note:: 
    5334 
    5335        :func:`.make_transient_to_detached` is a special-case function for 
    5336        advanced use cases only. 
    5337 
    5338    All attribute history on the given instance 
    5339    will be reset as though the instance were freshly loaded 
    5340    from a query.  Missing attributes will be marked as expired. 
    5341    The primary key attributes of the object, which are required, will be made 
    5342    into the "key" of the instance. 
    5343 
    5344    The object can then be added to a session, or merged 
    5345    possibly with the load=False flag, at which point it will look 
    5346    as if it were loaded that way, without emitting SQL. 
    5347 
    5348    This is a special use case function that differs from a normal 
    5349    call to :meth:`.Session.merge` in that a given persistent state 
    5350    can be manufactured without any SQL calls. 
    5351 
    5352    .. seealso:: 
    5353 
    5354        :func:`.make_transient` 
    5355 
    5356        :meth:`.Session.enable_relationship_loading` 
    5357 
    5358    """ 
    5359    state = attributes.instance_state(instance) 
    5360    if state.session_id or state.key: 
    5361        raise sa_exc.InvalidRequestError("Given object must be transient") 
    5362    state.key = state.mapper._identity_key_from_state(state) 
    5363    if state._deleted: 
    5364        del state._deleted 
    5365    state._commit_all(state.dict) 
    5366    state._expire_attributes(state.dict, state.unloaded) 
    5367 
    5368 
    5369def object_session(instance: object) -> Optional[Session]: 
    5370    """Return the :class:`.Session` to which the given instance belongs. 
    5371 
    5372    This is essentially the same as the :attr:`.InstanceState.session` 
    5373    accessor.  See that attribute for details. 
    5374 
    5375    """ 
    5376 
    5377    try: 
    5378        state = attributes.instance_state(instance) 
    5379    except exc.NO_STATE as err: 
    5380        raise exc.UnmappedInstanceError(instance) from err 
    5381    else: 
    5382        return _state_session(state) 
    5383 
    5384 
    5385_new_sessionid = util.counter()