1# orm/session.py
2# Copyright (C) 2005-2021 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"""Provides the Session class and related utilities."""
8
9
10import itertools
11import sys
12import weakref
13
14from . import attributes
15from . import context
16from . import exc
17from . import identity
18from . import loading
19from . import persistence
20from . import query
21from . import state as statelib
22from .base import _class_to_mapper
23from .base import _none_set
24from .base import _state_mapper
25from .base import instance_str
26from .base import object_mapper
27from .base import object_state
28from .base import state_str
29from .unitofwork import UOWTransaction
30from .. import engine
31from .. import exc as sa_exc
32from .. import sql
33from .. import util
34from ..engine.util import TransactionalContext
35from ..inspection import inspect
36from ..sql import coercions
37from ..sql import dml
38from ..sql import roles
39from ..sql import visitors
40from ..sql.base import CompileState
41from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
42
43__all__ = [
44    "Session",
45    "SessionTransaction",
46    "sessionmaker",
47    "ORMExecuteState",
48    "close_all_sessions",
49    "make_transient",
50    "make_transient_to_detached",
51    "object_session",
52]
53
54_sessions = weakref.WeakValueDictionary()
55"""Weak-referencing dictionary of :class:`.Session` objects.
56"""
57
58statelib._sessions = _sessions
59
60
61def _state_session(state):
62    """Given an :class:`.InstanceState`, return the :class:`.Session`
63    associated, if any.
64    """
65    return state.session
66
67
68class _SessionClassMethods(object):
69    """Class-level methods for :class:`.Session`, :class:`.sessionmaker`."""
70
71    @classmethod
72    @util.deprecated(
73        "1.3",
74        "The :meth:`.Session.close_all` method is deprecated and will be "
75        "removed in a future release.  Please refer to "
76        ":func:`.session.close_all_sessions`.",
77    )
78    def close_all(cls):
79        """Close *all* sessions in memory."""
80
81        close_all_sessions()
82
83    @classmethod
84    @util.preload_module("sqlalchemy.orm.util")
85    def identity_key(cls, *args, **kwargs):
86        """Return an identity key.
87
88        This is an alias of :func:`.util.identity_key`.
89
90        """
91        return util.preloaded.orm_util.identity_key(*args, **kwargs)
92
93    @classmethod
94    def object_session(cls, instance):
95        """Return the :class:`.Session` to which an object belongs.
96
97        This is an alias of :func:`.object_session`.
98
99        """
100
101        return object_session(instance)
102
103
104ACTIVE = util.symbol("ACTIVE")
105PREPARED = util.symbol("PREPARED")
106COMMITTED = util.symbol("COMMITTED")
107DEACTIVE = util.symbol("DEACTIVE")
108CLOSED = util.symbol("CLOSED")
109
110
111class ORMExecuteState(util.MemoizedSlots):
112    """Represents a call to the :meth:`_orm.Session.execute` method, as passed
113    to the :meth:`.SessionEvents.do_orm_execute` event hook.
114
115    .. versionadded:: 1.4
116
117    .. seealso::
118
119        :ref:`session_execute_events` - top level documentation on how
120        to use :meth:`_orm.SessionEvents.do_orm_execute`
121
122    """
123
124    __slots__ = (
125        "session",
126        "statement",
127        "parameters",
128        "execution_options",
129        "local_execution_options",
130        "bind_arguments",
131        "_compile_state_cls",
132        "_starting_event_idx",
133        "_events_todo",
134        "_update_execution_options",
135    )
136
137    def __init__(
138        self,
139        session,
140        statement,
141        parameters,
142        execution_options,
143        bind_arguments,
144        compile_state_cls,
145        events_todo,
146    ):
147        self.session = session
148        self.statement = statement
149        self.parameters = parameters
150        self.local_execution_options = execution_options
151        self.execution_options = statement._execution_options.union(
152            execution_options
153        )
154        self.bind_arguments = bind_arguments
155        self._compile_state_cls = compile_state_cls
156        self._events_todo = list(events_todo)
157
158    def _remaining_events(self):
159        return self._events_todo[self._starting_event_idx + 1 :]
160
161    def invoke_statement(
162        self,
163        statement=None,
164        params=None,
165        execution_options=None,
166        bind_arguments=None,
167    ):
168        """Execute the statement represented by this
169        :class:`.ORMExecuteState`, without re-invoking events that have
170        already proceeded.
171
172        This method essentially performs a re-entrant execution of the current
173        statement for which the :meth:`.SessionEvents.do_orm_execute` event is
174        being currently invoked.    The use case for this is for event handlers
175        that want to override how the ultimate
176        :class:`_engine.Result` object is returned, such as for schemes that
177        retrieve results from an offline cache or which concatenate results
178        from multiple executions.
179
180        When the :class:`_engine.Result` object is returned by the actual
181        handler function within :meth:`_orm.SessionEvents.do_orm_execute` and
182        is propagated to the calling
183        :meth:`_orm.Session.execute` method, the remainder of the
184        :meth:`_orm.Session.execute` method is preempted and the
185        :class:`_engine.Result` object is returned to the caller of
186        :meth:`_orm.Session.execute` immediately.
187
188        :param statement: optional statement to be invoked, in place of the
189         statement currently represented by :attr:`.ORMExecuteState.statement`.
190
191        :param params: optional dictionary of parameters which will be merged
192         into the existing :attr:`.ORMExecuteState.parameters` of this
193         :class:`.ORMExecuteState`.
194
195        :param execution_options: optional dictionary of execution options
196         will be merged into the existing
197         :attr:`.ORMExecuteState.execution_options` of this
198         :class:`.ORMExecuteState`.
199
200        :param bind_arguments: optional dictionary of bind_arguments
201         which will be merged amongst the current
202         :attr:`.ORMExecuteState.bind_arguments`
203         of this :class:`.ORMExecuteState`.
204
205        :return: a :class:`_engine.Result` object with ORM-level results.
206
207        .. seealso::
208
209            :ref:`do_orm_execute_re_executing` - background and examples on the
210            appropriate usage of :meth:`_orm.ORMExecuteState.invoke_statement`.
211
212
213        """
214
215        if statement is None:
216            statement = self.statement
217
218        _bind_arguments = dict(self.bind_arguments)
219        if bind_arguments:
220            _bind_arguments.update(bind_arguments)
221        _bind_arguments["_sa_skip_events"] = True
222
223        if params:
224            _params = dict(self.parameters)
225            _params.update(params)
226        else:
227            _params = self.parameters
228
229        _execution_options = self.local_execution_options
230        if execution_options:
231            _execution_options = _execution_options.union(execution_options)
232
233        return self.session.execute(
234            statement,
235            _params,
236            _execution_options,
237            _bind_arguments,
238            _parent_execute_state=self,
239        )
240
241    @property
242    def bind_mapper(self):
243        """Return the :class:`_orm.Mapper` that is the primary "bind" mapper.
244
245        For an :class:`_orm.ORMExecuteState` object invoking an ORM
246        statement, that is, the :attr:`_orm.ORMExecuteState.is_orm_statement`
247        attribute is ``True``, this attribute will return the
248        :class:`_orm.Mapper` that is considered to be the "primary" mapper
249        of the statement.   The term "bind mapper" refers to the fact that
250        a :class:`_orm.Session` object may be "bound" to multiple
251        :class:`_engine.Engine` objects keyed to mapped classes, and the
252        "bind mapper" determines which of those :class:`_engine.Engine` objects
253        would be selected.
254
255        For a statement that is invoked against a single mapped class,
256        :attr:`_orm.ORMExecuteState.bind_mapper` is intended to be a reliable
257        way of getting this mapper.
258
259        .. versionadded:: 1.4.0b2
260
261        .. seealso::
262
263            :attr:`_orm.ORMExecuteState.all_mappers`
264
265
266        """
267        return self.bind_arguments.get("mapper", None)
268
269    @property
270    def all_mappers(self):
271        """Return a sequence of all :class:`_orm.Mapper` objects that are
272        involved at the top level of this statement.
273
274        By "top level" we mean those :class:`_orm.Mapper` objects that would
275        be represented in the result set rows for a :func:`_sql.select`
276        query, or for a :func:`_dml.update` or :func:`_dml.delete` query,
277        the mapper that is the main subject of the UPDATE or DELETE.
278
279        .. versionadded:: 1.4.0b2
280
281        .. seealso::
282
283            :attr:`_orm.ORMExecuteState.bind_mapper`
284
285
286
287        """
288        if not self.is_orm_statement:
289            return []
290        elif self.is_select:
291            result = []
292            seen = set()
293            for d in self.statement.column_descriptions:
294                ent = d["entity"]
295                if ent:
296                    insp = inspect(ent, raiseerr=False)
297                    if insp and insp.mapper and insp.mapper not in seen:
298                        seen.add(insp.mapper)
299                        result.append(insp.mapper)
300            return result
301        elif self.is_update or self.is_delete:
302            return [self.bind_mapper]
303        else:
304            return []
305
306    @property
307    def is_orm_statement(self):
308        """return True if the operation is an ORM statement.
309
310        This indicates that the select(), update(), or delete() being
311        invoked contains ORM entities as subjects.   For a statement
312        that does not have ORM entities and instead refers only to
313        :class:`.Table` metadata, it is invoked as a Core SQL statement
314        and no ORM-level automation takes place.
315
316        """
317        return self._compile_state_cls is not None
318
319    @property
320    def is_select(self):
321        """return True if this is a SELECT operation."""
322        return self.statement.is_select
323
324    @property
325    def is_insert(self):
326        """return True if this is an INSERT operation."""
327        return self.statement.is_dml and self.statement.is_insert
328
329    @property
330    def is_update(self):
331        """return True if this is an UPDATE operation."""
332        return self.statement.is_dml and self.statement.is_update
333
334    @property
335    def is_delete(self):
336        """return True if this is a DELETE operation."""
337        return self.statement.is_dml and self.statement.is_delete
338
339    @property
340    def _is_crud(self):
341        return isinstance(self.statement, (dml.Update, dml.Delete))
342
343    def update_execution_options(self, **opts):
344        # TODO: no coverage
345        self.local_execution_options = self.local_execution_options.union(opts)
346
347    def _orm_compile_options(self):
348        if not self.is_select:
349            return None
350        opts = self.statement._compile_options
351        if opts.isinstance(context.ORMCompileState.default_compile_options):
352            return opts
353        else:
354            return None
355
356    @property
357    def lazy_loaded_from(self):
358        """An :class:`.InstanceState` that is using this statement execution
359        for a lazy load operation.
360
361        The primary rationale for this attribute is to support the horizontal
362        sharding extension, where it is available within specific query
363        execution time hooks created by this extension.   To that end, the
364        attribute is only intended to be meaningful at **query execution
365        time**, and importantly not any time prior to that, including query
366        compilation time.
367
368        """
369        return self.load_options._lazy_loaded_from
370
371    @property
372    def loader_strategy_path(self):
373        """Return the :class:`.PathRegistry` for the current load path.
374
375        This object represents the "path" in a query along relationships
376        when a particular object or collection is being loaded.
377
378        """
379        opts = self._orm_compile_options()
380        if opts is not None:
381            return opts._current_path
382        else:
383            return None
384
385    @property
386    def is_column_load(self):
387        """Return True if the operation is refreshing column-oriented
388        attributes on an existing ORM object.
389
390        This occurs during operations such as :meth:`_orm.Session.refresh`,
391        as well as when an attribute deferred by :func:`_orm.defer` is
392        being loaded, or an attribute that was expired either directly
393        by :meth:`_orm.Session.expire` or via a commit operation is being
394        loaded.
395
396        Handlers will very likely not want to add any options to queries
397        when such an operation is occurring as the query should be a straight
398        primary key fetch which should not have any additional WHERE criteria,
399        and loader options travelling with the instance
400        will have already been added to the query.
401
402        .. versionadded:: 1.4.0b2
403
404        .. seealso::
405
406            :attr:`_orm.ORMExecuteState.is_relationship_load`
407
408        """
409        opts = self._orm_compile_options()
410        return opts is not None and opts._for_refresh_state
411
412    @property
413    def is_relationship_load(self):
414        """Return True if this load is loading objects on behalf of a
415        relationship.
416
417        This means, the loader in effect is either a LazyLoader,
418        SelectInLoader, SubqueryLoader, or similar, and the entire
419        SELECT statement being emitted is on behalf of a relationship
420        load.
421
422        Handlers will very likely not want to add any options to queries
423        when such an operation is occurring, as loader options are already
424        capable of being propagated to relationship loaders and should
425        be already present.
426
427        .. seealso::
428
429            :attr:`_orm.ORMExecuteState.is_column_load`
430
431        """
432        opts = self._orm_compile_options()
433        if opts is None:
434            return False
435        path = self.loader_strategy_path
436        return path is not None and not path.is_root
437
438    @property
439    def load_options(self):
440        """Return the load_options that will be used for this execution."""
441
442        if not self.is_select:
443            raise sa_exc.InvalidRequestError(
444                "This ORM execution is not against a SELECT statement "
445                "so there are no load options."
446            )
447        return self.execution_options.get(
448            "_sa_orm_load_options", context.QueryContext.default_load_options
449        )
450
451    @property
452    def update_delete_options(self):
453        """Return the update_delete_options that will be used for this
454        execution."""
455
456        if not self._is_crud:
457            raise sa_exc.InvalidRequestError(
458                "This ORM execution is not against an UPDATE or DELETE "
459                "statement so there are no update options."
460            )
461        return self.execution_options.get(
462            "_sa_orm_update_options",
463            persistence.BulkUDCompileState.default_update_options,
464        )
465
466    @property
467    def user_defined_options(self):
468        """The sequence of :class:`.UserDefinedOptions` that have been
469        associated with the statement being invoked.
470
471        """
472        return [
473            opt
474            for opt in self.statement._with_options
475            if not opt._is_compile_state and not opt._is_legacy_option
476        ]
477
478
479class SessionTransaction(TransactionalContext):
480    """A :class:`.Session`-level transaction.
481
482    :class:`.SessionTransaction` is produced from the
483    :meth:`_orm.Session.begin`
484    and :meth:`_orm.Session.begin_nested` methods.   It's largely an internal
485    object that in modern use provides a context manager for session
486    transactions.
487
488    Documentation on interacting with :class:`_orm.SessionTransaction` is
489    at: :ref:`unitofwork_transaction`.
490
491
492    .. versionchanged:: 1.4  The scoping and API methods to work with the
493       :class:`_orm.SessionTransaction` object directly have been simplified.
494
495    .. seealso::
496
497        :ref:`unitofwork_transaction`
498
499        :meth:`.Session.begin`
500
501        :meth:`.Session.begin_nested`
502
503        :meth:`.Session.rollback`
504
505        :meth:`.Session.commit`
506
507        :meth:`.Session.in_transaction`
508
509        :meth:`.Session.in_nested_transaction`
510
511        :meth:`.Session.get_transaction`
512
513        :meth:`.Session.get_nested_transaction`
514
515
516    """
517
518    _rollback_exception = None
519
520    def __init__(
521        self,
522        session,
523        parent=None,
524        nested=False,
525        autobegin=False,
526    ):
527        TransactionalContext._trans_ctx_check(session)
528
529        self.session = session
530        self._connections = {}
531        self._parent = parent
532        self.nested = nested
533        if nested:
534            self._previous_nested_transaction = session._nested_transaction
535        self._state = ACTIVE
536        if not parent and nested:
537            raise sa_exc.InvalidRequestError(
538                "Can't start a SAVEPOINT transaction when no existing "
539                "transaction is in progress"
540            )
541
542        self._take_snapshot(autobegin=autobegin)
543
544        # make sure transaction is assigned before we call the
545        # dispatch
546        self.session._transaction = self
547
548        self.session.dispatch.after_transaction_create(self.session, self)
549
550    @property
551    def parent(self):
552        """The parent :class:`.SessionTransaction` of this
553        :class:`.SessionTransaction`.
554
555        If this attribute is ``None``, indicates this
556        :class:`.SessionTransaction` is at the top of the stack, and
557        corresponds to a real "COMMIT"/"ROLLBACK"
558        block.  If non-``None``, then this is either a "subtransaction"
559        or a "nested" / SAVEPOINT transaction.  If the
560        :attr:`.SessionTransaction.nested` attribute is ``True``, then
561        this is a SAVEPOINT, and if ``False``, indicates this a subtransaction.
562
563        .. versionadded:: 1.0.16 - use ._parent for previous versions
564
565        """
566        return self._parent
567
568    nested = False
569    """Indicates if this is a nested, or SAVEPOINT, transaction.
570
571    When :attr:`.SessionTransaction.nested` is True, it is expected
572    that :attr:`.SessionTransaction.parent` will be True as well.
573
574    """
575
576    @property
577    def is_active(self):
578        return self.session is not None and self._state is ACTIVE
579
580    def _assert_active(
581        self,
582        prepared_ok=False,
583        rollback_ok=False,
584        deactive_ok=False,
585        closed_msg="This transaction is closed",
586    ):
587        if self._state is COMMITTED:
588            raise sa_exc.InvalidRequestError(
589                "This session is in 'committed' state; no further "
590                "SQL can be emitted within this transaction."
591            )
592        elif self._state is PREPARED:
593            if not prepared_ok:
594                raise sa_exc.InvalidRequestError(
595                    "This session is in 'prepared' state; no further "
596                    "SQL can be emitted within this transaction."
597                )
598        elif self._state is DEACTIVE:
599            if not deactive_ok and not rollback_ok:
600                if self._rollback_exception:
601                    raise sa_exc.PendingRollbackError(
602                        "This Session's transaction has been rolled back "
603                        "due to a previous exception during flush."
604                        " To begin a new transaction with this Session, "
605                        "first issue Session.rollback()."
606                        " Original exception was: %s"
607                        % self._rollback_exception,
608                        code="7s2a",
609                    )
610                elif not deactive_ok:
611                    raise sa_exc.InvalidRequestError(
612                        "This session is in 'inactive' state, due to the "
613                        "SQL transaction being rolled back; no further "
614                        "SQL can be emitted within this transaction."
615                    )
616        elif self._state is CLOSED:
617            raise sa_exc.ResourceClosedError(closed_msg)
618
619    @property
620    def _is_transaction_boundary(self):
621        return self.nested or not self._parent
622
623    def connection(self, bindkey, execution_options=None, **kwargs):
624        self._assert_active()
625        bind = self.session.get_bind(bindkey, **kwargs)
626        return self._connection_for_bind(bind, execution_options)
627
628    def _begin(self, nested=False):
629        self._assert_active()
630        return SessionTransaction(self.session, self, nested=nested)
631
632    def _iterate_self_and_parents(self, upto=None):
633
634        current = self
635        result = ()
636        while current:
637            result += (current,)
638            if current._parent is upto:
639                break
640            elif current._parent is None:
641                raise sa_exc.InvalidRequestError(
642                    "Transaction %s is not on the active transaction list"
643                    % (upto)
644                )
645            else:
646                current = current._parent
647
648        return result
649
650    def _take_snapshot(self, autobegin=False):
651        if not self._is_transaction_boundary:
652            self._new = self._parent._new
653            self._deleted = self._parent._deleted
654            self._dirty = self._parent._dirty
655            self._key_switches = self._parent._key_switches
656            return
657
658        if not autobegin and not self.session._flushing:
659            self.session.flush()
660
661        self._new = weakref.WeakKeyDictionary()
662        self._deleted = weakref.WeakKeyDictionary()
663        self._dirty = weakref.WeakKeyDictionary()
664        self._key_switches = weakref.WeakKeyDictionary()
665
666    def _restore_snapshot(self, dirty_only=False):
667        """Restore the restoration state taken before a transaction began.
668
669        Corresponds to a rollback.
670
671        """
672        assert self._is_transaction_boundary
673
674        to_expunge = set(self._new).union(self.session._new)
675        self.session._expunge_states(to_expunge, to_transient=True)
676
677        for s, (oldkey, newkey) in self._key_switches.items():
678            # we probably can do this conditionally based on
679            # if we expunged or not, but safe_discard does that anyway
680            self.session.identity_map.safe_discard(s)
681
682            # restore the old key
683            s.key = oldkey
684
685            # now restore the object, but only if we didn't expunge
686            if s not in to_expunge:
687                self.session.identity_map.replace(s)
688
689        for s in set(self._deleted).union(self.session._deleted):
690            self.session._update_impl(s, revert_deletion=True)
691
692        assert not self.session._deleted
693
694        for s in self.session.identity_map.all_states():
695            if not dirty_only or s.modified or s in self._dirty:
696                s._expire(s.dict, self.session.identity_map._modified)
697
698    def _remove_snapshot(self):
699        """Remove the restoration state taken before a transaction began.
700
701        Corresponds to a commit.
702
703        """
704        assert self._is_transaction_boundary
705
706        if not self.nested and self.session.expire_on_commit:
707            for s in self.session.identity_map.all_states():
708                s._expire(s.dict, self.session.identity_map._modified)
709
710            statelib.InstanceState._detach_states(
711                list(self._deleted), self.session
712            )
713            self._deleted.clear()
714        elif self.nested:
715            self._parent._new.update(self._new)
716            self._parent._dirty.update(self._dirty)
717            self._parent._deleted.update(self._deleted)
718            self._parent._key_switches.update(self._key_switches)
719
720    def _connection_for_bind(self, bind, execution_options):
721        self._assert_active()
722
723        if bind in self._connections:
724            if execution_options:
725                util.warn(
726                    "Connection is already established for the "
727                    "given bind; execution_options ignored"
728                )
729            return self._connections[bind][0]
730
731        local_connect = False
732        should_commit = True
733
734        if self._parent:
735            conn = self._parent._connection_for_bind(bind, execution_options)
736            if not self.nested:
737                return conn
738        else:
739            if isinstance(bind, engine.Connection):
740                conn = bind
741                if conn.engine in self._connections:
742                    raise sa_exc.InvalidRequestError(
743                        "Session already has a Connection associated for the "
744                        "given Connection's Engine"
745                    )
746            else:
747                conn = bind.connect()
748                local_connect = True
749
750        try:
751            if execution_options:
752                conn = conn.execution_options(**execution_options)
753
754            if self.session.twophase and self._parent is None:
755                transaction = conn.begin_twophase()
756            elif self.nested:
757                transaction = conn.begin_nested()
758            elif conn.in_transaction():
759                # if given a future connection already in a transaction, don't
760                # commit that transaction unless it is a savepoint
761                if conn.in_nested_transaction():
762                    transaction = conn.get_nested_transaction()
763                else:
764                    transaction = conn.get_transaction()
765                    should_commit = False
766            else:
767                transaction = conn.begin()
768        except:
769            # connection will not not be associated with this Session;
770            # close it immediately so that it isn't closed under GC
771            if local_connect:
772                conn.close()
773            raise
774        else:
775            bind_is_connection = isinstance(bind, engine.Connection)
776
777            self._connections[conn] = self._connections[conn.engine] = (
778                conn,
779                transaction,
780                should_commit,
781                not bind_is_connection,
782            )
783            self.session.dispatch.after_begin(self.session, self, conn)
784            return conn
785
786    def prepare(self):
787        if self._parent is not None or not self.session.twophase:
788            raise sa_exc.InvalidRequestError(
789                "'twophase' mode not enabled, or not root transaction; "
790                "can't prepare."
791            )
792        self._prepare_impl()
793
794    def _prepare_impl(self):
795        self._assert_active()
796        if self._parent is None or self.nested:
797            self.session.dispatch.before_commit(self.session)
798
799        stx = self.session._transaction
800        if stx is not self:
801            for subtransaction in stx._iterate_self_and_parents(upto=self):
802                subtransaction.commit()
803
804        if not self.session._flushing:
805            for _flush_guard in range(100):
806                if self.session._is_clean():
807                    break
808                self.session.flush()
809            else:
810                raise exc.FlushError(
811                    "Over 100 subsequent flushes have occurred within "
812                    "session.commit() - is an after_flush() hook "
813                    "creating new objects?"
814                )
815
816        if self._parent is None and self.session.twophase:
817            try:
818                for t in set(self._connections.values()):
819                    t[1].prepare()
820            except:
821                with util.safe_reraise():
822                    self.rollback()
823
824        self._state = PREPARED
825
826    def commit(self, _to_root=False):
827        self._assert_active(prepared_ok=True)
828        if self._state is not PREPARED:
829            self._prepare_impl()
830
831        if self._parent is None or self.nested:
832            for conn, trans, should_commit, autoclose in set(
833                self._connections.values()
834            ):
835                if should_commit:
836                    trans.commit()
837
838            self._state = COMMITTED
839            self.session.dispatch.after_commit(self.session)
840
841            self._remove_snapshot()
842
843        self.close()
844
845        if _to_root and self._parent:
846            return self._parent.commit(_to_root=True)
847
848        return self._parent
849
850    def rollback(self, _capture_exception=False, _to_root=False):
851        self._assert_active(prepared_ok=True, rollback_ok=True)
852
853        stx = self.session._transaction
854        if stx is not self:
855            for subtransaction in stx._iterate_self_and_parents(upto=self):
856                subtransaction.close()
857
858        boundary = self
859        rollback_err = None
860        if self._state in (ACTIVE, PREPARED):
861            for transaction in self._iterate_self_and_parents():
862                if transaction._parent is None or transaction.nested:
863                    try:
864                        for t in set(transaction._connections.values()):
865                            t[1].rollback()
866
867                        transaction._state = DEACTIVE
868                        self.session.dispatch.after_rollback(self.session)
869                    except:
870                        rollback_err = sys.exc_info()
871                    finally:
872                        transaction._state = DEACTIVE
873                        transaction._restore_snapshot(
874                            dirty_only=transaction.nested
875                        )
876                    boundary = transaction
877                    break
878                else:
879                    transaction._state = DEACTIVE
880
881        sess = self.session
882
883        if not rollback_err and not sess._is_clean():
884
885            # if items were added, deleted, or mutated
886            # here, we need to re-restore the snapshot
887            util.warn(
888                "Session's state has been changed on "
889                "a non-active transaction - this state "
890                "will be discarded."
891            )
892            boundary._restore_snapshot(dirty_only=boundary.nested)
893
894        self.close()
895
896        if self._parent and _capture_exception:
897            self._parent._rollback_exception = sys.exc_info()[1]
898
899        if rollback_err:
900            util.raise_(rollback_err[1], with_traceback=rollback_err[2])
901
902        sess.dispatch.after_soft_rollback(sess, self)
903
904        if _to_root and self._parent:
905            return self._parent.rollback(_to_root=True)
906        return self._parent
907
908    def close(self, invalidate=False):
909        if self.nested:
910            self.session._nested_transaction = (
911                self._previous_nested_transaction
912            )
913
914        self.session._transaction = self._parent
915
916        if self._parent is None:
917            for connection, transaction, should_commit, autoclose in set(
918                self._connections.values()
919            ):
920                if invalidate:
921                    connection.invalidate()
922                if should_commit and transaction.is_active:
923                    transaction.close()
924                if autoclose:
925                    connection.close()
926
927        self._state = CLOSED
928        self.session.dispatch.after_transaction_end(self.session, self)
929
930        self.session = None
931        self._connections = None
932
933    def _get_subject(self):
934        return self.session
935
936    def _transaction_is_active(self):
937        return self._state is ACTIVE
938
939    def _transaction_is_closed(self):
940        return self._state is CLOSED
941
942
943class Session(_SessionClassMethods):
944    """Manages persistence operations for ORM-mapped objects.
945
946    The Session's usage paradigm is described at :doc:`/orm/session`.
947
948
949    """
950
951    _is_asyncio = False
952
953    @util.deprecated_params(
954        autocommit=(
955            "2.0",
956            "The :paramref:`.Session.autocommit` parameter is deprecated "
957            "and will be removed in SQLAlchemy version 2.0.  The "
958            ':class:`_orm.Session` now features "autobegin" behavior '
959            "such that the :meth:`.Session.begin` method may be called "
960            "if a transaction has not yet been started yet.  See the section "
961            ":ref:`session_explicit_begin` for background.",
962        ),
963    )
964    def __init__(
965        self,
966        bind=None,
967        autoflush=True,
968        future=False,
969        expire_on_commit=True,
970        autocommit=False,
971        twophase=False,
972        binds=None,
973        enable_baked_queries=True,
974        info=None,
975        query_cls=None,
976    ):
977        r"""Construct a new Session.
978
979        See also the :class:`.sessionmaker` function which is used to
980        generate a :class:`.Session`-producing callable with a given
981        set of arguments.
982
983        :param autocommit:
984          Defaults to ``False``. When ``True``, the
985          :class:`.Session` does not automatically begin transactions for
986          individual statement executions, will acquire connections from the
987          engine on an as-needed basis, releasing to the connection pool
988          after each statement. Flushes will begin and commit (or possibly
989          rollback) their own transaction if no transaction is present.
990          When using this mode, the
991          :meth:`.Session.begin` method may be used to explicitly start
992          transactions, but the usual "autobegin" behavior is not present.
993
994        :param autoflush: When ``True``, all query operations will issue a
995           :meth:`~.Session.flush` call to this ``Session`` before proceeding.
996           This is a convenience feature so that :meth:`~.Session.flush` need
997           not be called repeatedly in order for database queries to retrieve
998           results. It's typical that ``autoflush`` is used in conjunction
999           with ``autocommit=False``. In this scenario, explicit calls to
1000           :meth:`~.Session.flush` are rarely needed; you usually only need to
1001           call :meth:`~.Session.commit` (which flushes) to finalize changes.
1002
1003        :param bind: An optional :class:`_engine.Engine` or
1004           :class:`_engine.Connection` to
1005           which this ``Session`` should be bound. When specified, all SQL
1006           operations performed by this session will execute via this
1007           connectable.
1008
1009        :param binds: A dictionary which may specify any number of
1010           :class:`_engine.Engine` or :class:`_engine.Connection`
1011           objects as the source of
1012           connectivity for SQL operations on a per-entity basis.   The keys
1013           of the dictionary consist of any series of mapped classes,
1014           arbitrary Python classes that are bases for mapped classes,
1015           :class:`_schema.Table` objects and :class:`_orm.Mapper` objects.
1016           The
1017           values of the dictionary are then instances of
1018           :class:`_engine.Engine`
1019           or less commonly :class:`_engine.Connection` objects.
1020           Operations which
1021           proceed relative to a particular mapped class will consult this
1022           dictionary for the closest matching entity in order to determine
1023           which :class:`_engine.Engine` should be used for a particular SQL
1024           operation.    The complete heuristics for resolution are
1025           described at :meth:`.Session.get_bind`.  Usage looks like::
1026
1027            Session = sessionmaker(binds={
1028                SomeMappedClass: create_engine('postgresql://engine1'),
1029                SomeDeclarativeBase: create_engine('postgresql://engine2'),
1030                some_mapper: create_engine('postgresql://engine3'),
1031                some_table: create_engine('postgresql://engine4'),
1032                })
1033
1034           .. seealso::
1035
1036                :ref:`session_partitioning`
1037
1038                :meth:`.Session.bind_mapper`
1039
1040                :meth:`.Session.bind_table`
1041
1042                :meth:`.Session.get_bind`
1043
1044
1045        :param \class_: Specify an alternate class other than
1046           ``sqlalchemy.orm.session.Session`` which should be used by the
1047           returned class. This is the only argument that is local to the
1048           :class:`.sessionmaker` function, and is not sent directly to the
1049           constructor for ``Session``.
1050
1051        :param enable_baked_queries: defaults to ``True``.  A flag consumed
1052           by the :mod:`sqlalchemy.ext.baked` extension to determine if
1053           "baked queries" should be cached, as is the normal operation
1054           of this extension.  When set to ``False``, caching as used by
1055           this particular extension is disabled.
1056
1057           .. versionchanged:: 1.4 The ``sqlalchemy.ext.baked`` extension is
1058              legacy and is not used by any of SQLAlchemy's internals. This
1059              flag therefore only affects applications that are making explicit
1060              use of this extension within their own code.
1061
1062        :param expire_on_commit:  Defaults to ``True``. When ``True``, all
1063           instances will be fully expired after each :meth:`~.commit`,
1064           so that all attribute/object access subsequent to a completed
1065           transaction will load from the most recent database state.
1066
1067            .. seealso::
1068
1069                :ref:`session_committing`
1070
1071        :param future: if True, use 2.0 style transactional and engine
1072          behavior.  Future mode includes the following behaviors:
1073
1074          * The :class:`_orm.Session` will not use "bound" metadata in order
1075            to locate an :class:`_engine.Engine`; the engine or engines in use
1076            must be specified to the constructor of :class:`_orm.Session` or
1077            otherwise be configured against the :class:`_orm.sessionmaker`
1078            in use
1079
1080          * The "subtransactions" feature of :meth:`_orm.Session.begin` is
1081            removed in version 2.0 and is disabled when the future flag is
1082            set.
1083
1084          * The behavior of the :paramref:`_orm.relationship.cascade_backrefs`
1085            flag on a :func:`_orm.relationship` will always assume
1086            "False" behavior.
1087
1088          .. versionadded:: 1.4
1089
1090          .. seealso::
1091
1092            :ref:`migration_20_toplevel`
1093
1094        :param info: optional dictionary of arbitrary data to be associated
1095           with this :class:`.Session`.  Is available via the
1096           :attr:`.Session.info` attribute.  Note the dictionary is copied at
1097           construction time so that modifications to the per-
1098           :class:`.Session` dictionary will be local to that
1099           :class:`.Session`.
1100
1101        :param query_cls:  Class which should be used to create new Query
1102          objects, as returned by the :meth:`~.Session.query` method.
1103          Defaults to :class:`_query.Query`.
1104
1105        :param twophase:  When ``True``, all transactions will be started as
1106            a "two phase" transaction, i.e. using the "two phase" semantics
1107            of the database in use along with an XID.  During a
1108            :meth:`~.commit`, after :meth:`~.flush` has been issued for all
1109            attached databases, the :meth:`~.TwoPhaseTransaction.prepare`
1110            method on each database's :class:`.TwoPhaseTransaction` will be
1111            called. This allows each database to roll back the entire
1112            transaction, before each transaction is committed.
1113
1114        """
1115        self.identity_map = identity.WeakInstanceDict()
1116
1117        self._new = {}  # InstanceState->object, strong refs object
1118        self._deleted = {}  # same
1119        self.bind = bind
1120        self.__binds = {}
1121        self._flushing = False
1122        self._warn_on_events = False
1123        self._transaction = None
1124        self._nested_transaction = None
1125        self.future = future
1126        self.hash_key = _new_sessionid()
1127        self.autoflush = autoflush
1128        self.expire_on_commit = expire_on_commit
1129        self.enable_baked_queries = enable_baked_queries
1130
1131        if autocommit:
1132            if future:
1133                raise sa_exc.ArgumentError(
1134                    "Cannot use autocommit mode with future=True."
1135                )
1136            self.autocommit = True
1137        else:
1138            self.autocommit = False
1139
1140        self.twophase = twophase
1141        self._query_cls = query_cls if query_cls else query.Query
1142        if info:
1143            self.info.update(info)
1144
1145        if binds is not None:
1146            for key, bind in binds.items():
1147                self._add_bind(key, bind)
1148
1149        _sessions[self.hash_key] = self
1150
1151    # used by sqlalchemy.engine.util.TransactionalContext
1152    _trans_context_manager = None
1153
1154    connection_callable = None
1155
1156    def __enter__(self):
1157        return self
1158
1159    def __exit__(self, type_, value, traceback):
1160        self.close()
1161
1162    @util.contextmanager
1163    def _maker_context_manager(self):
1164        with self:
1165            with self.begin():
1166                yield self
1167
1168    @property
1169    @util.deprecated_20(
1170        ":attr:`_orm.Session.transaction`",
1171        alternative="For context manager use, use "
1172        ":meth:`_orm.Session.begin`.  To access "
1173        "the current root transaction, use "
1174        ":meth:`_orm.Session.get_transaction`.",
1175        warn_on_attribute_access=True,
1176    )
1177    def transaction(self):
1178        """The current active or inactive :class:`.SessionTransaction`.
1179
1180        May be None if no transaction has begun yet.
1181
1182        .. versionchanged:: 1.4  the :attr:`.Session.transaction` attribute
1183           is now a read-only descriptor that also may return None if no
1184           transaction has begun yet.
1185
1186
1187        """
1188        return self._legacy_transaction()
1189
1190    def _legacy_transaction(self):
1191        if not self.future:
1192            self._autobegin()
1193        return self._transaction
1194
1195    def in_transaction(self):
1196        """Return True if this :class:`_orm.Session` has begun a transaction.
1197
1198        .. versionadded:: 1.4
1199
1200        .. seealso::
1201
1202            :attr:`_orm.Session.is_active`
1203
1204
1205        """
1206        return self._transaction is not None
1207
1208    def in_nested_transaction(self):
1209        """Return True if this :class:`_orm.Session` has begun a nested
1210        transaction, e.g. SAVEPOINT.
1211
1212        .. versionadded:: 1.4
1213
1214        """
1215        return self._nested_transaction is not None
1216
1217    def get_transaction(self):
1218        """Return the current root transaction in progress, if any.
1219
1220        .. versionadded:: 1.4
1221
1222        """
1223        trans = self._transaction
1224        while trans is not None and trans._parent is not None:
1225            trans = trans._parent
1226        return trans
1227
1228    def get_nested_transaction(self):
1229        """Return the current nested transaction in progress, if any.
1230
1231        .. versionadded:: 1.4
1232
1233        """
1234
1235        return self._nested_transaction
1236
1237    @util.memoized_property
1238    def info(self):
1239        """A user-modifiable dictionary.
1240
1241        The initial value of this dictionary can be populated using the
1242        ``info`` argument to the :class:`.Session` constructor or
1243        :class:`.sessionmaker` constructor or factory methods.  The dictionary
1244        here is always local to this :class:`.Session` and can be modified
1245        independently of all other :class:`.Session` objects.
1246
1247        """
1248        return {}
1249
1250    def _autobegin(self):
1251        if not self.autocommit and self._transaction is None:
1252
1253            trans = SessionTransaction(self, autobegin=True)
1254            assert self._transaction is trans
1255            return True
1256
1257        return False
1258
1259    @util.deprecated_params(
1260        subtransactions=(
1261            "2.0",
1262            "The :paramref:`_orm.Session.begin.subtransactions` flag is "
1263            "deprecated and "
1264            "will be removed in SQLAlchemy version 2.0.  See "
1265            "the documentation at :ref:`session_subtransactions` for "
1266            "background on a compatible alternative pattern.",
1267        )
1268    )
1269    def begin(self, subtransactions=False, nested=False, _subtrans=False):
1270        """Begin a transaction, or nested transaction,
1271        on this :class:`.Session`, if one is not already begun.
1272
1273        The :class:`_orm.Session` object features **autobegin** behavior,
1274        so that normally it is not necessary to call the
1275        :meth:`_orm.Session.begin`
1276        method explicitly. However, it may be used in order to control
1277        the scope of when the transactional state is begun.
1278
1279        When used to begin the outermost transaction, an error is raised
1280        if this :class:`.Session` is already inside of a transaction.
1281
1282        :param nested: if True, begins a SAVEPOINT transaction and is
1283         equivalent to calling :meth:`~.Session.begin_nested`. For
1284         documentation on SAVEPOINT transactions, please see
1285         :ref:`session_begin_nested`.
1286
1287        :param subtransactions: if True, indicates that this
1288         :meth:`~.Session.begin` can create a "subtransaction".
1289
1290        :return: the :class:`.SessionTransaction` object.  Note that
1291         :class:`.SessionTransaction`
1292         acts as a Python context manager, allowing :meth:`.Session.begin`
1293         to be used in a "with" block.  See :ref:`session_autocommit` for
1294         an example.
1295
1296        .. seealso::
1297
1298            :ref:`session_autobegin`
1299
1300            :ref:`unitofwork_transaction`
1301
1302            :meth:`.Session.begin_nested`
1303
1304
1305        """
1306
1307        if subtransactions and self.future:
1308            raise NotImplementedError(
1309                "subtransactions are not implemented in future "
1310                "Session objects."
1311            )
1312
1313        if self._autobegin():
1314            if not subtransactions and not nested and not _subtrans:
1315                return self._transaction
1316
1317        if self._transaction is not None:
1318            if subtransactions or _subtrans or nested:
1319                trans = self._transaction._begin(nested=nested)
1320                assert self._transaction is trans
1321                if nested:
1322                    self._nested_transaction = trans
1323            else:
1324                raise sa_exc.InvalidRequestError(
1325                    "A transaction is already begun on this Session."
1326                )
1327        elif not self.autocommit:
1328            # outermost transaction.  must be a not nested and not
1329            # a subtransaction
1330
1331            assert not nested and not _subtrans and not subtransactions
1332            trans = SessionTransaction(self)
1333            assert self._transaction is trans
1334        else:
1335            # legacy autocommit mode
1336            assert not self.future
1337            trans = SessionTransaction(self, nested=nested)
1338            assert self._transaction is trans
1339
1340        return self._transaction  # needed for __enter__/__exit__ hook
1341
1342    def begin_nested(self):
1343        """Begin a "nested" transaction on this Session, e.g. SAVEPOINT.
1344
1345        The target database(s) and associated drivers must support SQL
1346        SAVEPOINT for this method to function correctly.
1347
1348        For documentation on SAVEPOINT
1349        transactions, please see :ref:`session_begin_nested`.
1350
1351        :return: the :class:`.SessionTransaction` object.  Note that
1352         :class:`.SessionTransaction` acts as a context manager, allowing
1353         :meth:`.Session.begin_nested` to be used in a "with" block.
1354         See :ref:`session_begin_nested` for a usage example.
1355
1356        .. seealso::
1357
1358            :ref:`session_begin_nested`
1359
1360            :ref:`pysqlite_serializable` - special workarounds required
1361            with the SQLite driver in order for SAVEPOINT to work
1362            correctly.
1363
1364        """
1365        return self.begin(nested=True)
1366
1367    def rollback(self):
1368        """Rollback the current transaction in progress.
1369
1370        If no transaction is in progress, this method is a pass-through.
1371
1372        In :term:`1.x-style` use, this method rolls back the topmost
1373        database transaction if no nested transactions are in effect, or
1374        to the current nested transaction if one is in effect.
1375
1376        When
1377        :term:`2.0-style` use is in effect via the
1378        :paramref:`_orm.Session.future` flag, the method always rolls back
1379        the topmost database transaction, discarding any nested
1380        transactions that may be in progress.
1381
1382        .. seealso::
1383
1384            :ref:`session_rollback`
1385
1386            :ref:`unitofwork_transaction`
1387
1388        """
1389        if self._transaction is None:
1390            pass
1391        else:
1392            self._transaction.rollback(_to_root=self.future)
1393
1394    def commit(self):
1395        """Flush pending changes and commit the current transaction.
1396
1397        If no transaction is in progress, the method will first
1398        "autobegin" a new transaction and commit.
1399
1400        If :term:`1.x-style` use is in effect and there are currently
1401        SAVEPOINTs in progress via :meth:`_orm.Session.begin_nested`,
1402        the operation will release the current SAVEPOINT but not commit
1403        the outermost database transaction.
1404
1405        If :term:`2.0-style` use is in effect via the
1406        :paramref:`_orm.Session.future` flag, the outermost database
1407        transaction is committed unconditionally, automatically releasing any
1408        SAVEPOINTs in effect.
1409
1410        When using legacy "autocommit" mode, this method is only
1411        valid to call if a transaction is actually in progress, else
1412        an error is raised.   Similarly, when using legacy "subtransactions",
1413        the method will instead close out the current "subtransaction",
1414        rather than the actual database transaction, if a transaction
1415        is in progress.
1416
1417        .. seealso::
1418
1419            :ref:`session_committing`
1420
1421            :ref:`unitofwork_transaction`
1422
1423        """
1424        if self._transaction is None:
1425            if not self._autobegin():
1426                raise sa_exc.InvalidRequestError("No transaction is begun.")
1427
1428        self._transaction.commit(_to_root=self.future)
1429
1430    def prepare(self):
1431        """Prepare the current transaction in progress for two phase commit.
1432
1433        If no transaction is in progress, this method raises an
1434        :exc:`~sqlalchemy.exc.InvalidRequestError`.
1435
1436        Only root transactions of two phase sessions can be prepared. If the
1437        current transaction is not such, an
1438        :exc:`~sqlalchemy.exc.InvalidRequestError` is raised.
1439
1440        """
1441        if self._transaction is None:
1442            if not self._autobegin():
1443                raise sa_exc.InvalidRequestError("No transaction is begun.")
1444
1445        self._transaction.prepare()
1446
1447    def connection(
1448        self,
1449        bind_arguments=None,
1450        close_with_result=False,
1451        execution_options=None,
1452        **kw
1453    ):
1454        r"""Return a :class:`_engine.Connection` object corresponding to this
1455        :class:`.Session` object's transactional state.
1456
1457        If this :class:`.Session` is configured with ``autocommit=False``,
1458        either the :class:`_engine.Connection` corresponding to the current
1459        transaction is returned, or if no transaction is in progress, a new
1460        one is begun and the :class:`_engine.Connection`
1461        returned (note that no
1462        transactional state is established with the DBAPI until the first
1463        SQL statement is emitted).
1464
1465        Alternatively, if this :class:`.Session` is configured with
1466        ``autocommit=True``, an ad-hoc :class:`_engine.Connection` is returned
1467        using :meth:`_engine.Engine.connect` on the underlying
1468        :class:`_engine.Engine`.
1469
1470        Ambiguity in multi-bind or unbound :class:`.Session` objects can be
1471        resolved through any of the optional keyword arguments.   This
1472        ultimately makes usage of the :meth:`.get_bind` method for resolution.
1473
1474        :param bind_arguments: dictionary of bind arguments.  May include
1475         "mapper", "bind", "clause", other custom arguments that are passed
1476         to :meth:`.Session.get_bind`.
1477
1478        :param bind:
1479          deprecated; use bind_arguments
1480
1481        :param mapper:
1482          deprecated; use bind_arguments
1483
1484        :param clause:
1485          deprecated; use bind_arguments
1486
1487        :param close_with_result: Passed to :meth:`_engine.Engine.connect`,
1488          indicating the :class:`_engine.Connection` should be considered
1489          "single use", automatically closing when the first result set is
1490          closed.  This flag only has an effect if this :class:`.Session` is
1491          configured with ``autocommit=True`` and does not already have a
1492          transaction in progress.
1493
1494          .. deprecated:: 1.4  this parameter is deprecated and will be removed
1495             in SQLAlchemy 2.0
1496
1497        :param execution_options: a dictionary of execution options that will
1498         be passed to :meth:`_engine.Connection.execution_options`, **when the
1499         connection is first procured only**.   If the connection is already
1500         present within the :class:`.Session`, a warning is emitted and
1501         the arguments are ignored.
1502
1503         .. seealso::
1504
1505            :ref:`session_transaction_isolation`
1506
1507        :param \**kw:
1508          deprecated; use bind_arguments
1509
1510        """
1511
1512        if not bind_arguments:
1513            bind_arguments = kw
1514
1515        bind = bind_arguments.pop("bind", None)
1516        if bind is None:
1517            bind = self.get_bind(**bind_arguments)
1518
1519        return self._connection_for_bind(
1520            bind,
1521            close_with_result=close_with_result,
1522            execution_options=execution_options,
1523        )
1524
1525    def _connection_for_bind(self, engine, execution_options=None, **kw):
1526        TransactionalContext._trans_ctx_check(self)
1527
1528        if self._transaction is not None or self._autobegin():
1529            return self._transaction._connection_for_bind(
1530                engine, execution_options
1531            )
1532
1533        assert self._transaction is None
1534        assert self.autocommit
1535        conn = engine.connect(**kw)
1536        if execution_options:
1537            conn = conn.execution_options(**execution_options)
1538        return conn
1539
1540    def execute(
1541        self,
1542        statement,
1543        params=None,
1544        execution_options=util.EMPTY_DICT,
1545        bind_arguments=None,
1546        _parent_execute_state=None,
1547        _add_event=None,
1548        **kw
1549    ):
1550        r"""Execute a SQL expression construct.
1551
1552        Returns a :class:`_engine.Result` object representing
1553        results of the statement execution.
1554
1555        E.g.::
1556
1557            from sqlalchemy import select
1558            result = session.execute(
1559                select(User).where(User.id == 5)
1560            )
1561
1562        The API contract of :meth:`_orm.Session.execute` is similar to that
1563        of :meth:`_future.Connection.execute`, the :term:`2.0 style` version
1564        of :class:`_future.Connection`.
1565
1566        .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is
1567           now the primary point of ORM statement execution when using
1568           :term:`2.0 style` ORM usage.
1569
1570        :param statement:
1571            An executable statement (i.e. an :class:`.Executable` expression
1572            such as :func:`_expression.select`).
1573
1574        :param params:
1575            Optional dictionary, or list of dictionaries, containing
1576            bound parameter values.   If a single dictionary, single-row
1577            execution occurs; if a list of dictionaries, an
1578            "executemany" will be invoked.  The keys in each dictionary
1579            must correspond to parameter names present in the statement.
1580
1581        :param execution_options: optional dictionary of execution options,
1582         which will be associated with the statement execution.  This
1583         dictionary can provide a subset of the options that are accepted
1584         by :meth:`_engine.Connection.execution_options`, and may also
1585         provide additional options understood only in an ORM context.
1586
1587        :param bind_arguments: dictionary of additional arguments to determine
1588         the bind.  May include "mapper", "bind", or other custom arguments.
1589         Contents of this dictionary are passed to the
1590         :meth:`.Session.get_bind` method.
1591
1592        :param mapper:
1593          deprecated; use the bind_arguments dictionary
1594
1595        :param bind:
1596          deprecated; use the bind_arguments dictionary
1597
1598        :param \**kw:
1599          deprecated; use the bind_arguments dictionary
1600
1601        :return: a :class:`_engine.Result` object.
1602
1603
1604        """
1605        statement = coercions.expect(roles.StatementRole, statement)
1606
1607        if kw:
1608            util.warn_deprecated_20(
1609                "Passing bind arguments to Session.execute() as keyword "
1610                "arguments is deprecated and will be removed SQLAlchemy 2.0. "
1611                "Please use the bind_arguments parameter."
1612            )
1613            if not bind_arguments:
1614                bind_arguments = kw
1615            else:
1616                bind_arguments.update(kw)
1617        elif not bind_arguments:
1618            bind_arguments = {}
1619
1620        if (
1621            statement._propagate_attrs.get("compile_state_plugin", None)
1622            == "orm"
1623        ):
1624            # note that even without "future" mode, we need
1625            compile_state_cls = CompileState._get_plugin_class_for_plugin(
1626                statement, "orm"
1627            )
1628        else:
1629            compile_state_cls = None
1630
1631        execution_options = util.coerce_to_immutabledict(execution_options)
1632
1633        if compile_state_cls is not None:
1634            (
1635                statement,
1636                execution_options,
1637            ) = compile_state_cls.orm_pre_session_exec(
1638                self,
1639                statement,
1640                params,
1641                execution_options,
1642                bind_arguments,
1643                _parent_execute_state is not None,
1644            )
1645        else:
1646            bind_arguments.setdefault("clause", statement)
1647            execution_options = execution_options.union(
1648                {"future_result": True}
1649            )
1650
1651        if _parent_execute_state:
1652            events_todo = _parent_execute_state._remaining_events()
1653        else:
1654            events_todo = self.dispatch.do_orm_execute
1655            if _add_event:
1656                events_todo = list(events_todo) + [_add_event]
1657
1658        if events_todo:
1659            orm_exec_state = ORMExecuteState(
1660                self,
1661                statement,
1662                params,
1663                execution_options,
1664                bind_arguments,
1665                compile_state_cls,
1666                events_todo,
1667            )
1668            for idx, fn in enumerate(events_todo):
1669                orm_exec_state._starting_event_idx = idx
1670                result = fn(orm_exec_state)
1671                if result:
1672                    return result
1673
1674            statement = orm_exec_state.statement
1675            execution_options = orm_exec_state.local_execution_options
1676
1677        bind = self.get_bind(**bind_arguments)
1678
1679        if self.autocommit:
1680            # legacy stuff, we can't use future_result w/ autocommit because
1681            # we rely upon close_with_result, also legacy.  it's all
1682            # interrelated
1683            conn = self._connection_for_bind(bind, close_with_result=True)
1684            execution_options = execution_options.union(
1685                dict(future_result=False)
1686            )
1687        else:
1688            conn = self._connection_for_bind(bind)
1689        result = conn._execute_20(statement, params or {}, execution_options)
1690
1691        if compile_state_cls:
1692            result = compile_state_cls.orm_setup_cursor_result(
1693                self,
1694                statement,
1695                params,
1696                execution_options,
1697                bind_arguments,
1698                result,
1699            )
1700
1701        return result
1702
1703    def scalar(
1704        self,
1705        statement,
1706        params=None,
1707        execution_options=util.EMPTY_DICT,
1708        bind_arguments=None,
1709        **kw
1710    ):
1711        """Execute a statement and return a scalar result.
1712
1713        Usage and parameters are the same as that of
1714        :meth:`_orm.Session.execute`; the return result is a scalar Python
1715        value.
1716
1717        """
1718
1719        return self.execute(
1720            statement,
1721            params=params,
1722            execution_options=execution_options,
1723            bind_arguments=bind_arguments,
1724            **kw
1725        ).scalar()
1726
1727    def scalars(
1728        self,
1729        statement,
1730        params=None,
1731        execution_options=util.EMPTY_DICT,
1732        bind_arguments=None,
1733        **kw
1734    ):
1735        """Execute a statement and return the results as scalars.
1736
1737        Usage and parameters are the same as that of
1738        :meth:`_orm.Session.execute`; the return result is a
1739        :class:`_result.ScalarResult` filtering object which
1740        will return single elements rather than :class:`_row.Row` objects.
1741
1742        :return:  a :class:`_result.ScalarResult` object
1743
1744        .. versionadded:: 1.4.24
1745
1746        """
1747
1748        return self.execute(
1749            statement,
1750            params=params,
1751            execution_options=execution_options,
1752            bind_arguments=bind_arguments,
1753            **kw
1754        ).scalars()
1755
1756    def close(self):
1757        """Close out the transactional resources and ORM objects used by this
1758        :class:`_orm.Session`.
1759
1760        This expunges all ORM objects associated with this
1761        :class:`_orm.Session`, ends any transaction in progress and
1762        :term:`releases` any :class:`_engine.Connection` objects which this
1763        :class:`_orm.Session` itself has checked out from associated
1764        :class:`_engine.Engine` objects. The operation then leaves the
1765        :class:`_orm.Session` in a state which it may be used again.
1766
1767        .. tip::
1768
1769            The :meth:`_orm.Session.close` method **does not prevent the
1770            Session from being used again**.   The :class:`_orm.Session` itself
1771            does not actually have a distinct "closed" state; it merely means
1772            the :class:`_orm.Session` will release all database connections
1773            and ORM objects.
1774
1775        .. versionchanged:: 1.4  The :meth:`.Session.close` method does not
1776           immediately create a new :class:`.SessionTransaction` object;
1777           instead, the new :class:`.SessionTransaction` is created only if
1778           the :class:`.Session` is used again for a database operation.
1779
1780        .. seealso::
1781
1782            :ref:`session_closing` - detail on the semantics of
1783            :meth:`_orm.Session.close`
1784
1785        """
1786        self._close_impl(invalidate=False)
1787
1788    def invalidate(self):
1789        """Close this Session, using connection invalidation.
1790
1791        This is a variant of :meth:`.Session.close` that will additionally
1792        ensure that the :meth:`_engine.Connection.invalidate`
1793        method will be called on each :class:`_engine.Connection` object
1794        that is currently in use for a transaction (typically there is only
1795        one connection unless the :class:`_orm.Session` is used with
1796        multiple engines).
1797
1798        This can be called when the database is known to be in a state where
1799        the connections are no longer safe to be used.
1800
1801        Below illustrates a scenario when using `gevent
1802        <https://www.gevent.org/>`_, which can produce ``Timeout`` exceptions
1803        that may mean the underlying connection should be discarded::
1804
1805            import gevent
1806
1807            try:
1808                sess = Session()
1809                sess.add(User())
1810                sess.commit()
1811            except gevent.Timeout:
1812                sess.invalidate()
1813                raise
1814            except:
1815                sess.rollback()
1816                raise
1817
1818        The method additionally does everything that :meth:`_orm.Session.close`
1819        does, including that all ORM objects are expunged.
1820
1821        """
1822        self._close_impl(invalidate=True)
1823
1824    def _close_impl(self, invalidate):
1825        self.expunge_all()
1826        if self._transaction is not None:
1827            for transaction in self._transaction._iterate_self_and_parents():
1828                transaction.close(invalidate)
1829
1830    def expunge_all(self):
1831        """Remove all object instances from this ``Session``.
1832
1833        This is equivalent to calling ``expunge(obj)`` on all objects in this
1834        ``Session``.
1835
1836        """
1837
1838        all_states = self.identity_map.all_states() + list(self._new)
1839        self.identity_map._kill()
1840        self.identity_map = identity.WeakInstanceDict()
1841        self._new = {}
1842        self._deleted = {}
1843
1844        statelib.InstanceState._detach_states(all_states, self)
1845
1846    def _add_bind(self, key, bind):
1847        try:
1848            insp = inspect(key)
1849        except sa_exc.NoInspectionAvailable as err:
1850            if not isinstance(key, type):
1851                util.raise_(
1852                    sa_exc.ArgumentError(
1853                        "Not an acceptable bind target: %s" % key
1854                    ),
1855                    replace_context=err,
1856                )
1857            else:
1858                self.__binds[key] = bind
1859        else:
1860            if insp.is_selectable:
1861                self.__binds[insp] = bind
1862            elif insp.is_mapper:
1863                self.__binds[insp.class_] = bind
1864                for _selectable in insp._all_tables:
1865                    self.__binds[_selectable] = bind
1866            else:
1867                raise sa_exc.ArgumentError(
1868                    "Not an acceptable bind target: %s" % key
1869                )
1870
1871    def bind_mapper(self, mapper, bind):
1872        """Associate a :class:`_orm.Mapper` or arbitrary Python class with a
1873        "bind", e.g. an :class:`_engine.Engine` or
1874        :class:`_engine.Connection`.
1875
1876        The given entity is added to a lookup used by the
1877        :meth:`.Session.get_bind` method.
1878
1879        :param mapper: a :class:`_orm.Mapper` object,
1880         or an instance of a mapped
1881         class, or any Python class that is the base of a set of mapped
1882         classes.
1883
1884        :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection`
1885                    object.
1886
1887        .. seealso::
1888
1889            :ref:`session_partitioning`
1890
1891            :paramref:`.Session.binds`
1892
1893            :meth:`.Session.bind_table`
1894
1895
1896        """
1897        self._add_bind(mapper, bind)
1898
1899    def bind_table(self, table, bind):
1900        """Associate a :class:`_schema.Table` with a "bind", e.g. an
1901        :class:`_engine.Engine`
1902        or :class:`_engine.Connection`.
1903
1904        The given :class:`_schema.Table` is added to a lookup used by the
1905        :meth:`.Session.get_bind` method.
1906
1907        :param table: a :class:`_schema.Table` object,
1908         which is typically the target
1909         of an ORM mapping, or is present within a selectable that is
1910         mapped.
1911
1912        :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection`
1913                    object.
1914
1915        .. seealso::
1916
1917            :ref:`session_partitioning`
1918
1919            :paramref:`.Session.binds`
1920
1921            :meth:`.Session.bind_mapper`
1922
1923
1924        """
1925        self._add_bind(table, bind)
1926
1927    def get_bind(
1928        self,
1929        mapper=None,
1930        clause=None,
1931        bind=None,
1932        _sa_skip_events=None,
1933        _sa_skip_for_implicit_returning=False,
1934    ):
1935        """Return a "bind" to which this :class:`.Session` is bound.
1936
1937        The "bind" is usually an instance of :class:`_engine.Engine`,
1938        except in the case where the :class:`.Session` has been
1939        explicitly bound directly to a :class:`_engine.Connection`.
1940
1941        For a multiply-bound or unbound :class:`.Session`, the
1942        ``mapper`` or ``clause`` arguments are used to determine the
1943        appropriate bind to return.
1944
1945        Note that the "mapper" argument is usually present
1946        when :meth:`.Session.get_bind` is called via an ORM
1947        operation such as a :meth:`.Session.query`, each
1948        individual INSERT/UPDATE/DELETE operation within a
1949        :meth:`.Session.flush`, call, etc.
1950
1951        The order of resolution is:
1952
1953        1. if mapper given and :paramref:`.Session.binds` is present,
1954           locate a bind based first on the mapper in use, then
1955           on the mapped class in use, then on any base classes that are
1956           present in the ``__mro__`` of the mapped class, from more specific
1957           superclasses to more general.
1958        2. if clause given and ``Session.binds`` is present,
1959           locate a bind based on :class:`_schema.Table` objects
1960           found in the given clause present in ``Session.binds``.
1961        3. if ``Session.binds`` is present, return that.
1962        4. if clause given, attempt to return a bind
1963           linked to the :class:`_schema.MetaData` ultimately
1964           associated with the clause.
1965        5. if mapper given, attempt to return a bind
1966           linked to the :class:`_schema.MetaData` ultimately
1967           associated with the :class:`_schema.Table` or other
1968           selectable to which the mapper is mapped.
1969        6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError`
1970           is raised.
1971
1972        Note that the :meth:`.Session.get_bind` method can be overridden on
1973        a user-defined subclass of :class:`.Session` to provide any kind
1974        of bind resolution scheme.  See the example at
1975        :ref:`session_custom_partitioning`.
1976
1977        :param mapper:
1978          Optional :func:`.mapper` mapped class or instance of
1979          :class:`_orm.Mapper`.   The bind can be derived from a
1980          :class:`_orm.Mapper`
1981          first by consulting the "binds" map associated with this
1982          :class:`.Session`, and secondly by consulting the
1983          :class:`_schema.MetaData`
1984          associated with the :class:`_schema.Table` to which the
1985          :class:`_orm.Mapper`
1986          is mapped for a bind.
1987
1988        :param clause:
1989            A :class:`_expression.ClauseElement` (i.e.
1990            :func:`_expression.select`,
1991            :func:`_expression.text`,
1992            etc.).  If the ``mapper`` argument is not present or could not
1993            produce a bind, the given expression construct will be searched
1994            for a bound element, typically a :class:`_schema.Table`
1995            associated with
1996            bound :class:`_schema.MetaData`.
1997
1998        .. seealso::
1999
2000             :ref:`session_partitioning`
2001
2002             :paramref:`.Session.binds`
2003
2004             :meth:`.Session.bind_mapper`
2005
2006             :meth:`.Session.bind_table`
2007
2008        """
2009
2010        # this function is documented as a subclassing hook, so we have
2011        # to call this method even if the return is simple
2012        if bind:
2013            return bind
2014        elif not self.__binds and self.bind:
2015            # simplest and most common case, we have a bind and no
2016            # per-mapper/table binds, we're done
2017            return self.bind
2018
2019        # we don't have self.bind and either have self.__binds
2020        # or we don't have self.__binds (which is legacy).  Look at the
2021        # mapper and the clause
2022        if mapper is clause is None:
2023            if self.bind:
2024                return self.bind
2025            else:
2026                raise sa_exc.UnboundExecutionError(
2027                    "This session is not bound to a single Engine or "
2028                    "Connection, and no context was provided to locate "
2029                    "a binding."
2030                )
2031
2032        # look more closely at the mapper.
2033        if mapper is not None:
2034            try:
2035                mapper = inspect(mapper)
2036            except sa_exc.NoInspectionAvailable as err:
2037                if isinstance(mapper, type):
2038                    util.raise_(
2039                        exc.UnmappedClassError(mapper),
2040                        replace_context=err,
2041                    )
2042                else:
2043                    raise
2044
2045        # match up the mapper or clause in the __binds
2046        if self.__binds:
2047            # matching mappers and selectables to entries in the
2048            # binds dictionary; supported use case.
2049            if mapper:
2050                for cls in mapper.class_.__mro__:
2051                    if cls in self.__binds:
2052                        return self.__binds[cls]
2053                if clause is None:
2054                    clause = mapper.persist_selectable
2055
2056            if clause is not None:
2057                plugin_subject = clause._propagate_attrs.get(
2058                    "plugin_subject", None
2059                )
2060
2061                if plugin_subject is not None:
2062                    for cls in plugin_subject.mapper.class_.__mro__:
2063                        if cls in self.__binds:
2064                            return self.__binds[cls]
2065
2066                for obj in visitors.iterate(clause):
2067                    if obj in self.__binds:
2068                        return self.__binds[obj]
2069
2070        # none of the __binds matched, but we have a fallback bind.
2071        # return that
2072        if self.bind:
2073            return self.bind
2074
2075        # now we are in legacy territory.  looking for "bind" on tables
2076        # that are via bound metadata.   this goes away in 2.0.
2077
2078        future_msg = ""
2079        future_code = ""
2080
2081        if mapper and clause is None:
2082            clause = mapper.persist_selectable
2083
2084        if clause is not None:
2085            if clause.bind:
2086                if self.future:
2087                    future_msg = (
2088                        " A bind was located via legacy bound metadata, but "
2089                        "since future=True is set on this Session, this "
2090                        "bind is ignored."
2091                    )
2092                else:
2093                    util.warn_deprecated_20(
2094                        "This Session located a target engine via bound "
2095                        "metadata; as this functionality will be removed in "
2096                        "SQLAlchemy 2.0, an Engine object should be passed "
2097                        "to the Session() constructor directly."
2098                    )
2099                    return clause.bind
2100
2101        if mapper:
2102            if mapper.persist_selectable.bind:
2103                if self.future:
2104                    future_msg = (
2105                        " A bind was located via legacy bound metadata, but "
2106                        "since future=True is set on this Session, this "
2107                        "bind is ignored."
2108                    )
2109                else:
2110                    util.warn_deprecated_20(
2111                        "This Session located a target engine via bound "
2112                        "metadata; as this functionality will be removed in "
2113                        "SQLAlchemy 2.0, an Engine object should be passed "
2114                        "to the Session() constructor directly."
2115                    )
2116                    return mapper.persist_selectable.bind
2117
2118        context = []
2119        if mapper is not None:
2120            context.append("mapper %s" % mapper)
2121        if clause is not None:
2122            context.append("SQL expression")
2123
2124        raise sa_exc.UnboundExecutionError(
2125            "Could not locate a bind configured on %s or this Session.%s"
2126            % (", ".join(context), future_msg),
2127            code=future_code,
2128        )
2129
2130    def query(self, *entities, **kwargs):
2131        """Return a new :class:`_query.Query` object corresponding to this
2132        :class:`_orm.Session`.
2133
2134        """
2135
2136        return self._query_cls(entities, self, **kwargs)
2137
2138    def _identity_lookup(
2139        self,
2140        mapper,
2141        primary_key_identity,
2142        identity_token=None,
2143        passive=attributes.PASSIVE_OFF,
2144        lazy_loaded_from=None,
2145    ):
2146        """Locate an object in the identity map.
2147
2148        Given a primary key identity, constructs an identity key and then
2149        looks in the session's identity map.  If present, the object may
2150        be run through unexpiration rules (e.g. load unloaded attributes,
2151        check if was deleted).
2152
2153        e.g.::
2154
2155            obj = session._identity_lookup(inspect(SomeClass), (1, ))
2156
2157        :param mapper: mapper in use
2158        :param primary_key_identity: the primary key we are searching for, as
2159         a tuple.
2160        :param identity_token: identity token that should be used to create
2161         the identity key.  Used as is, however overriding subclasses can
2162         repurpose this in order to interpret the value in a special way,
2163         such as if None then look among multiple target tokens.
2164        :param passive: passive load flag passed to
2165         :func:`.loading.get_from_identity`, which impacts the behavior if
2166         the object is found; the object may be validated and/or unexpired
2167         if the flag allows for SQL to be emitted.
2168        :param lazy_loaded_from: an :class:`.InstanceState` that is
2169         specifically asking for this identity as a related identity.  Used
2170         for sharding schemes where there is a correspondence between an object
2171         and a related object being lazy-loaded (or otherwise
2172         relationship-loaded).
2173
2174        :return: None if the object is not found in the identity map, *or*
2175         if the object was unexpired and found to have been deleted.
2176         if passive flags disallow SQL and the object is expired, returns
2177         PASSIVE_NO_RESULT.   In all other cases the instance is returned.
2178
2179        .. versionchanged:: 1.4.0 - the :meth:`.Session._identity_lookup`
2180           method was moved from :class:`_query.Query` to
2181           :class:`.Session`, to avoid having to instantiate the
2182           :class:`_query.Query` object.
2183
2184
2185        """
2186
2187        key = mapper.identity_key_from_primary_key(
2188            primary_key_identity, identity_token=identity_token
2189        )
2190        return loading.get_from_identity(self, mapper, key, passive)
2191
2192    @property
2193    @util.contextmanager
2194    def no_autoflush(self):
2195        """Return a context manager that disables autoflush.
2196
2197        e.g.::
2198
2199            with session.no_autoflush:
2200
2201                some_object = SomeClass()
2202                session.add(some_object)
2203                # won't autoflush
2204                some_object.related_thing = session.query(SomeRelated).first()
2205
2206        Operations that proceed within the ``with:`` block
2207        will not be subject to flushes occurring upon query
2208        access.  This is useful when initializing a series
2209        of objects which involve existing database queries,
2210        where the uncompleted object should not yet be flushed.
2211
2212        """
2213        autoflush = self.autoflush
2214        self.autoflush = False
2215        try:
2216            yield self
2217        finally:
2218            self.autoflush = autoflush
2219
2220    def _autoflush(self):
2221        if self.autoflush and not self._flushing:
2222            try:
2223                self.flush()
2224            except sa_exc.StatementError as e:
2225                # note we are reraising StatementError as opposed to
2226                # raising FlushError with "chaining" to remain compatible
2227                # with code that catches StatementError, IntegrityError,
2228                # etc.
2229                e.add_detail(
2230                    "raised as a result of Query-invoked autoflush; "
2231                    "consider using a session.no_autoflush block if this "
2232                    "flush is occurring prematurely"
2233                )
2234                util.raise_(e, with_traceback=sys.exc_info()[2])
2235
2236    def refresh(self, instance, attribute_names=None, with_for_update=None):
2237        """Expire and refresh attributes on the given instance.
2238
2239        The selected attributes will first be expired as they would when using
2240        :meth:`_orm.Session.expire`; then a SELECT statement will be issued to
2241        the database to refresh column-oriented attributes with the current
2242        value available in the current transaction.
2243
2244        :func:`_orm.relationship` oriented attributes will also be immediately
2245        loaded if they were already eagerly loaded on the object, using the
2246        same eager loading strategy that they were loaded with originally.
2247        Unloaded relationship attributes will remain unloaded, as will
2248        relationship attributes that were originally lazy loaded.
2249
2250        .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method
2251           can also refresh eagerly loaded attributes.
2252
2253        .. tip::
2254
2255            While the :meth:`_orm.Session.refresh` method is capable of
2256            refreshing both column and relationship oriented attributes, its
2257            primary focus is on refreshing of local column-oriented attributes
2258            on a single instance. For more open ended "refresh" functionality,
2259            including the ability to refresh the attributes on many objects at
2260            once while having explicit control over relationship loader
2261            strategies, use the
2262            :ref:`populate existing <orm_queryguide_populate_existing>` feature
2263            instead.
2264
2265        Note that a highly isolated transaction will return the same values as
2266        were previously read in that same transaction, regardless of changes
2267        in database state outside of that transaction.   Refreshing
2268        attributes usually only makes sense at the start of a transaction
2269        where database rows have not yet been accessed.
2270
2271        :param attribute_names: optional.  An iterable collection of
2272          string attribute names indicating a subset of attributes to
2273          be refreshed.
2274
2275        :param with_for_update: optional boolean ``True`` indicating FOR UPDATE
2276          should be used, or may be a dictionary containing flags to
2277          indicate a more specific set of FOR UPDATE flags for the SELECT;
2278          flags should match the parameters of
2279          :meth:`_query.Query.with_for_update`.
2280          Supersedes the :paramref:`.Session.refresh.lockmode` parameter.
2281
2282        .. seealso::
2283
2284            :ref:`session_expire` - introductory material
2285
2286            :meth:`.Session.expire`
2287
2288            :meth:`.Session.expire_all`
2289
2290            :ref:`orm_queryguide_populate_existing` - allows any ORM query
2291            to refresh objects as they would be loaded normally.
2292
2293        """
2294        try:
2295            state = attributes.instance_state(instance)
2296        except exc.NO_STATE as err:
2297            util.raise_(
2298                exc.UnmappedInstanceError(instance),
2299                replace_context=err,
2300            )
2301
2302        self._expire_state(state, attribute_names)
2303
2304        if with_for_update == {}:
2305            raise sa_exc.ArgumentError(
2306                "with_for_update should be the boolean value "
2307                "True, or a dictionary with options.  "
2308                "A blank dictionary is ambiguous."
2309            )
2310
2311        with_for_update = query.ForUpdateArg._from_argument(with_for_update)
2312
2313        stmt = sql.select(object_mapper(instance))
2314        if (
2315            loading.load_on_ident(
2316                self,
2317                stmt,
2318                state.key,
2319                refresh_state=state,
2320                with_for_update=with_for_update,
2321                only_load_props=attribute_names,
2322            )
2323            is None
2324        ):
2325            raise sa_exc.InvalidRequestError(
2326                "Could not refresh instance '%s'" % instance_str(instance)
2327            )
2328
2329    def expire_all(self):
2330        """Expires all persistent instances within this Session.
2331
2332        When any attributes on a persistent instance is next accessed,
2333        a query will be issued using the
2334        :class:`.Session` object's current transactional context in order to
2335        load all expired attributes for the given instance.   Note that
2336        a highly isolated transaction will return the same values as were
2337        previously read in that same transaction, regardless of changes
2338        in database state outside of that transaction.
2339
2340        To expire individual objects and individual attributes
2341        on those objects, use :meth:`Session.expire`.
2342
2343        The :class:`.Session` object's default behavior is to
2344        expire all state whenever the :meth:`Session.rollback`
2345        or :meth:`Session.commit` methods are called, so that new
2346        state can be loaded for the new transaction.   For this reason,
2347        calling :meth:`Session.expire_all` should not be needed when
2348        autocommit is ``False``, assuming the transaction is isolated.
2349
2350        .. seealso::
2351
2352            :ref:`session_expire` - introductory material
2353
2354            :meth:`.Session.expire`
2355
2356            :meth:`.Session.refresh`
2357
2358            :meth:`_orm.Query.populate_existing`
2359
2360        """
2361        for state in self.identity_map.all_states():
2362            state._expire(state.dict, self.identity_map._modified)
2363
2364    def expire(self, instance, attribute_names=None):
2365        """Expire the attributes on an instance.
2366
2367        Marks the attributes of an instance as out of date. When an expired
2368        attribute is next accessed, a query will be issued to the
2369        :class:`.Session` object's current transactional context in order to
2370        load all expired attributes for the given instance.   Note that
2371        a highly isolated transaction will return the same values as were
2372        previously read in that same transaction, regardless of changes
2373        in database state outside of that transaction.
2374
2375        To expire all objects in the :class:`.Session` simultaneously,
2376        use :meth:`Session.expire_all`.
2377
2378        The :class:`.Session` object's default behavior is to
2379        expire all state whenever the :meth:`Session.rollback`
2380        or :meth:`Session.commit` methods are called, so that new
2381        state can be loaded for the new transaction.   For this reason,
2382        calling :meth:`Session.expire` only makes sense for the specific
2383        case that a non-ORM SQL statement was emitted in the current
2384        transaction.
2385
2386        :param instance: The instance to be refreshed.
2387        :param attribute_names: optional list of string attribute names
2388          indicating a subset of attributes to be expired.
2389
2390        .. seealso::
2391
2392            :ref:`session_expire` - introductory material
2393
2394            :meth:`.Session.expire`
2395
2396            :meth:`.Session.refresh`
2397
2398            :meth:`_orm.Query.populate_existing`
2399
2400        """
2401        try:
2402            state = attributes.instance_state(instance)
2403        except exc.NO_STATE as err:
2404            util.raise_(
2405                exc.UnmappedInstanceError(instance),
2406                replace_context=err,
2407            )
2408        self._expire_state(state, attribute_names)
2409
2410    def _expire_state(self, state, attribute_names):
2411        self._validate_persistent(state)
2412        if attribute_names:
2413            state._expire_attributes(state.dict, attribute_names)
2414        else:
2415            # pre-fetch the full cascade since the expire is going to
2416            # remove associations
2417            cascaded = list(
2418                state.manager.mapper.cascade_iterator("refresh-expire", state)
2419            )
2420            self._conditional_expire(state)
2421            for o, m, st_, dct_ in cascaded:
2422                self._conditional_expire(st_)
2423
2424    def _conditional_expire(self, state, autoflush=None):
2425        """Expire a state if persistent, else expunge if pending"""
2426
2427        if state.key:
2428            state._expire(state.dict, self.identity_map._modified)
2429        elif state in self._new:
2430            self._new.pop(state)
2431            state._detach(self)
2432
2433    def expunge(self, instance):
2434        """Remove the `instance` from this ``Session``.
2435
2436        This will free all internal references to the instance.  Cascading
2437        will be applied according to the *expunge* cascade rule.
2438
2439        """
2440        try:
2441            state = attributes.instance_state(instance)
2442        except exc.NO_STATE as err:
2443            util.raise_(
2444                exc.UnmappedInstanceError(instance),
2445                replace_context=err,
2446            )
2447        if state.session_id is not self.hash_key:
2448            raise sa_exc.InvalidRequestError(
2449                "Instance %s is not present in this Session" % state_str(state)
2450            )
2451
2452        cascaded = list(
2453            state.manager.mapper.cascade_iterator("expunge", state)
2454        )
2455        self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded])
2456
2457    def _expunge_states(self, states, to_transient=False):
2458        for state in states:
2459            if state in self._new:
2460                self._new.pop(state)
2461            elif self.identity_map.contains_state(state):
2462                self.identity_map.safe_discard(state)
2463                self._deleted.pop(state, None)
2464            elif self._transaction:
2465                # state is "detached" from being deleted, but still present
2466                # in the transaction snapshot
2467                self._transaction._deleted.pop(state, None)
2468        statelib.InstanceState._detach_states(
2469            states, self, to_transient=to_transient
2470        )
2471
2472    def _register_persistent(self, states):
2473        """Register all persistent objects from a flush.
2474
2475        This is used both for pending objects moving to the persistent
2476        state as well as already persistent objects.
2477
2478        """
2479
2480        pending_to_persistent = self.dispatch.pending_to_persistent or None
2481        for state in states:
2482            mapper = _state_mapper(state)
2483
2484            # prevent against last minute dereferences of the object
2485            obj = state.obj()
2486            if obj is not None:
2487
2488                instance_key = mapper._identity_key_from_state(state)
2489
2490                if (
2491                    _none_set.intersection(instance_key[1])
2492                    and not mapper.allow_partial_pks
2493                    or _none_set.issuperset(instance_key[1])
2494                ):
2495                    raise exc.FlushError(
2496                        "Instance %s has a NULL identity key.  If this is an "
2497                        "auto-generated value, check that the database table "
2498                        "allows generation of new primary key values, and "
2499                        "that the mapped Column object is configured to "
2500                        "expect these generated values.  Ensure also that "
2501                        "this flush() is not occurring at an inappropriate "
2502                        "time, such as within a load() event."
2503                        % state_str(state)
2504                    )
2505
2506                if state.key is None:
2507                    state.key = instance_key
2508                elif state.key != instance_key:
2509                    # primary key switch. use safe_discard() in case another
2510                    # state has already replaced this one in the identity
2511                    # map (see test/orm/test_naturalpks.py ReversePKsTest)
2512                    self.identity_map.safe_discard(state)
2513                    if state in self._transaction._key_switches:
2514                        orig_key = self._transaction._key_switches[state][0]
2515                    else:
2516                        orig_key = state.key
2517                    self._transaction._key_switches[state] = (
2518                        orig_key,
2519                        instance_key,
2520                    )
2521                    state.key = instance_key
2522
2523                # there can be an existing state in the identity map
2524                # that is replaced when the primary keys of two instances
2525                # are swapped; see test/orm/test_naturalpks.py -> test_reverse
2526                old = self.identity_map.replace(state)
2527                if (
2528                    old is not None
2529                    and mapper._identity_key_from_state(old) == instance_key
2530                    and old.obj() is not None
2531                ):
2532                    util.warn(
2533                        "Identity map already had an identity for %s, "
2534                        "replacing it with newly flushed object.   Are there "
2535                        "load operations occurring inside of an event handler "
2536                        "within the flush?" % (instance_key,)
2537                    )
2538                state._orphaned_outside_of_session = False
2539
2540        statelib.InstanceState._commit_all_states(
2541            ((state, state.dict) for state in states), self.identity_map
2542        )
2543
2544        self._register_altered(states)
2545
2546        if pending_to_persistent is not None:
2547            for state in states.intersection(self._new):
2548                pending_to_persistent(self, state)
2549
2550        # remove from new last, might be the last strong ref
2551        for state in set(states).intersection(self._new):
2552            self._new.pop(state)
2553
2554    def _register_altered(self, states):
2555        if self._transaction:
2556            for state in states:
2557                if state in self._new:
2558                    self._transaction._new[state] = True
2559                else:
2560                    self._transaction._dirty[state] = True
2561
2562    def _remove_newly_deleted(self, states):
2563        persistent_to_deleted = self.dispatch.persistent_to_deleted or None
2564        for state in states:
2565            if self._transaction:
2566                self._transaction._deleted[state] = True
2567
2568            if persistent_to_deleted is not None:
2569                # get a strong reference before we pop out of
2570                # self._deleted
2571                obj = state.obj()  # noqa
2572
2573            self.identity_map.safe_discard(state)
2574            self._deleted.pop(state, None)
2575            state._deleted = True
2576            # can't call state._detach() here, because this state
2577            # is still in the transaction snapshot and needs to be
2578            # tracked as part of that
2579            if persistent_to_deleted is not None:
2580                persistent_to_deleted(self, state)
2581
2582    def add(self, instance, _warn=True):
2583        """Place an object in the ``Session``.
2584
2585        Its state will be persisted to the database on the next flush
2586        operation.
2587
2588        Repeated calls to ``add()`` will be ignored. The opposite of ``add()``
2589        is ``expunge()``.
2590
2591        """
2592        if _warn and self._warn_on_events:
2593            self._flush_warning("Session.add()")
2594
2595        try:
2596            state = attributes.instance_state(instance)
2597        except exc.NO_STATE as err:
2598            util.raise_(
2599                exc.UnmappedInstanceError(instance),
2600                replace_context=err,
2601            )
2602
2603        self._save_or_update_state(state)
2604
2605    def add_all(self, instances):
2606        """Add the given collection of instances to this ``Session``."""
2607
2608        if self._warn_on_events:
2609            self._flush_warning("Session.add_all()")
2610
2611        for instance in instances:
2612            self.add(instance, _warn=False)
2613
2614    def _save_or_update_state(self, state):
2615        state._orphaned_outside_of_session = False
2616        self._save_or_update_impl(state)
2617
2618        mapper = _state_mapper(state)
2619        for o, m, st_, dct_ in mapper.cascade_iterator(
2620            "save-update", state, halt_on=self._contains_state
2621        ):
2622            self._save_or_update_impl(st_)
2623
2624    def delete(self, instance):
2625        """Mark an instance as deleted.
2626
2627        The database delete operation occurs upon ``flush()``.
2628
2629        """
2630        if self._warn_on_events:
2631            self._flush_warning("Session.delete()")
2632
2633        try:
2634            state = attributes.instance_state(instance)
2635        except exc.NO_STATE as err:
2636            util.raise_(
2637                exc.UnmappedInstanceError(instance),
2638                replace_context=err,
2639            )
2640
2641        self._delete_impl(state, instance, head=True)
2642
2643    def _delete_impl(self, state, obj, head):
2644
2645        if state.key is None:
2646            if head:
2647                raise sa_exc.InvalidRequestError(
2648                    "Instance '%s' is not persisted" % state_str(state)
2649                )
2650            else:
2651                return
2652
2653        to_attach = self._before_attach(state, obj)
2654
2655        if state in self._deleted:
2656            return
2657
2658        self.identity_map.add(state)
2659
2660        if to_attach:
2661            self._after_attach(state, obj)
2662
2663        if head:
2664            # grab the cascades before adding the item to the deleted list
2665            # so that autoflush does not delete the item
2666            # the strong reference to the instance itself is significant here
2667            cascade_states = list(
2668                state.manager.mapper.cascade_iterator("delete", state)
2669            )
2670
2671        self._deleted[state] = obj
2672
2673        if head:
2674            for o, m, st_, dct_ in cascade_states:
2675                self._delete_impl(st_, o, False)
2676
2677    def get(
2678        self,
2679        entity,
2680        ident,
2681        options=None,
2682        populate_existing=False,
2683        with_for_update=None,
2684        identity_token=None,
2685    ):
2686        """Return an instance based on the given primary key identifier,
2687        or ``None`` if not found.
2688
2689        E.g.::
2690
2691            my_user = session.get(User, 5)
2692
2693            some_object = session.get(VersionedFoo, (5, 10))
2694
2695            some_object = session.get(
2696                VersionedFoo,
2697                {"id": 5, "version_id": 10}
2698            )
2699
2700        .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved
2701           from the now deprecated :meth:`_orm.Query.get` method.
2702
2703        :meth:`_orm.Session.get` is special in that it provides direct
2704        access to the identity map of the :class:`.Session`.
2705        If the given primary key identifier is present
2706        in the local identity map, the object is returned
2707        directly from this collection and no SQL is emitted,
2708        unless the object has been marked fully expired.
2709        If not present,
2710        a SELECT is performed in order to locate the object.
2711
2712        :meth:`_orm.Session.get` also will perform a check if
2713        the object is present in the identity map and
2714        marked as expired - a SELECT
2715        is emitted to refresh the object as well as to
2716        ensure that the row is still present.
2717        If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
2718
2719        :param entity: a mapped class or :class:`.Mapper` indicating the
2720         type of entity to be loaded.
2721
2722        :param ident: A scalar, tuple, or dictionary representing the
2723         primary key.  For a composite (e.g. multiple column) primary key,
2724         a tuple or dictionary should be passed.
2725
2726         For a single-column primary key, the scalar calling form is typically
2727         the most expedient.  If the primary key of a row is the value "5",
2728         the call looks like::
2729
2730            my_object = session.get(SomeClass, 5)
2731
2732         The tuple form contains primary key values typically in
2733         the order in which they correspond to the mapped
2734         :class:`_schema.Table`
2735         object's primary key columns, or if the
2736         :paramref:`_orm.Mapper.primary_key` configuration parameter were
2737         used, in
2738         the order used for that parameter. For example, if the primary key
2739         of a row is represented by the integer
2740         digits "5, 10" the call would look like::
2741
2742             my_object = session.get(SomeClass, (5, 10))
2743
2744         The dictionary form should include as keys the mapped attribute names
2745         corresponding to each element of the primary key.  If the mapped class
2746         has the attributes ``id``, ``version_id`` as the attributes which
2747         store the object's primary key value, the call would look like::
2748
2749            my_object = session.get(SomeClass, {"id": 5, "version_id": 10})
2750
2751        :param options: optional sequence of loader options which will be
2752         applied to the query, if one is emitted.
2753
2754        :param populate_existing: causes the method to unconditionally emit
2755         a SQL query and refresh the object with the newly loaded data,
2756         regardless of whether or not the object is already present.
2757
2758        :param with_for_update: optional boolean ``True`` indicating FOR UPDATE
2759          should be used, or may be a dictionary containing flags to
2760          indicate a more specific set of FOR UPDATE flags for the SELECT;
2761          flags should match the parameters of
2762          :meth:`_query.Query.with_for_update`.
2763          Supersedes the :paramref:`.Session.refresh.lockmode` parameter.
2764
2765        :return: The object instance, or ``None``.
2766
2767        """
2768        return self._get_impl(
2769            entity,
2770            ident,
2771            loading.load_on_pk_identity,
2772            options,
2773            populate_existing=populate_existing,
2774            with_for_update=with_for_update,
2775            identity_token=identity_token,
2776        )
2777
2778    def _get_impl(
2779        self,
2780        entity,
2781        primary_key_identity,
2782        db_load_fn,
2783        options=None,
2784        populate_existing=False,
2785        with_for_update=None,
2786        identity_token=None,
2787        execution_options=None,
2788    ):
2789
2790        # convert composite types to individual args
2791        if hasattr(primary_key_identity, "__composite_values__"):
2792            primary_key_identity = primary_key_identity.__composite_values__()
2793
2794        mapper = inspect(entity)
2795
2796        if not mapper or not mapper.is_mapper:
2797            raise sa_exc.ArgumentError(
2798                "Expected mapped class or mapper, got: %r" % entity
2799            )
2800
2801        is_dict = isinstance(primary_key_identity, dict)
2802        if not is_dict:
2803            primary_key_identity = util.to_list(
2804                primary_key_identity, default=(None,)
2805            )
2806
2807        if len(primary_key_identity) != len(mapper.primary_key):
2808            raise sa_exc.InvalidRequestError(
2809                "Incorrect number of values in identifier to formulate "
2810                "primary key for session.get(); primary key columns "
2811                "are %s" % ",".join("'%s'" % c for c in mapper.primary_key)
2812            )
2813
2814        if is_dict:
2815            try:
2816                primary_key_identity = list(
2817                    primary_key_identity[prop.key]
2818                    for prop in mapper._identity_key_props
2819                )
2820
2821            except KeyError as err:
2822                util.raise_(
2823                    sa_exc.InvalidRequestError(
2824                        "Incorrect names of values in identifier to formulate "
2825                        "primary key for session.get(); primary key attribute "
2826                        "names are %s"
2827                        % ",".join(
2828                            "'%s'" % prop.key
2829                            for prop in mapper._identity_key_props
2830                        )
2831                    ),
2832                    replace_context=err,
2833                )
2834
2835        if (
2836            not populate_existing
2837            and not mapper.always_refresh
2838            and with_for_update is None
2839        ):
2840
2841            instance = self._identity_lookup(
2842                mapper, primary_key_identity, identity_token=identity_token
2843            )
2844
2845            if instance is not None:
2846                # reject calls for id in identity map but class
2847                # mismatch.
2848                if not issubclass(instance.__class__, mapper.class_):
2849                    return None
2850                return instance
2851            elif instance is attributes.PASSIVE_CLASS_MISMATCH:
2852                return None
2853
2854        # set_label_style() not strictly necessary, however this will ensure
2855        # that tablename_colname style is used which at the moment is
2856        # asserted in a lot of unit tests :)
2857
2858        load_options = context.QueryContext.default_load_options
2859
2860        if populate_existing:
2861            load_options += {"_populate_existing": populate_existing}
2862        statement = sql.select(mapper).set_label_style(
2863            LABEL_STYLE_TABLENAME_PLUS_COL
2864        )
2865        if with_for_update is not None:
2866            statement._for_update_arg = query.ForUpdateArg._from_argument(
2867                with_for_update
2868            )
2869
2870        if options:
2871            statement = statement.options(*options)
2872        if execution_options:
2873            statement = statement.execution_options(**execution_options)
2874        return db_load_fn(
2875            self,
2876            statement,
2877            primary_key_identity,
2878            load_options=load_options,
2879        )
2880
2881    def merge(self, instance, load=True, options=None):
2882        """Copy the state of a given instance into a corresponding instance
2883        within this :class:`.Session`.
2884
2885        :meth:`.Session.merge` examines the primary key attributes of the
2886        source instance, and attempts to reconcile it with an instance of the
2887        same primary key in the session.   If not found locally, it attempts
2888        to load the object from the database based on primary key, and if
2889        none can be located, creates a new instance.  The state of each
2890        attribute on the source instance is then copied to the target
2891        instance.  The resulting target instance is then returned by the
2892        method; the original source instance is left unmodified, and
2893        un-associated with the :class:`.Session` if not already.
2894
2895        This operation cascades to associated instances if the association is
2896        mapped with ``cascade="merge"``.
2897
2898        See :ref:`unitofwork_merging` for a detailed discussion of merging.
2899
2900        .. versionchanged:: 1.1 - :meth:`.Session.merge` will now reconcile
2901           pending objects with overlapping primary keys in the same way
2902           as persistent.  See :ref:`change_3601` for discussion.
2903
2904        :param instance: Instance to be merged.
2905        :param load: Boolean, when False, :meth:`.merge` switches into
2906         a "high performance" mode which causes it to forego emitting history
2907         events as well as all database access.  This flag is used for
2908         cases such as transferring graphs of objects into a :class:`.Session`
2909         from a second level cache, or to transfer just-loaded objects
2910         into the :class:`.Session` owned by a worker thread or process
2911         without re-querying the database.
2912
2913         The ``load=False`` use case adds the caveat that the given
2914         object has to be in a "clean" state, that is, has no pending changes
2915         to be flushed - even if the incoming object is detached from any
2916         :class:`.Session`.   This is so that when
2917         the merge operation populates local attributes and
2918         cascades to related objects and
2919         collections, the values can be "stamped" onto the
2920         target object as is, without generating any history or attribute
2921         events, and without the need to reconcile the incoming data with
2922         any existing related objects or collections that might not
2923         be loaded.  The resulting objects from ``load=False`` are always
2924         produced as "clean", so it is only appropriate that the given objects
2925         should be "clean" as well, else this suggests a mis-use of the
2926         method.
2927        :param options: optional sequence of loader options which will be
2928         applied to the :meth:`_orm.Session.get` method when the merge
2929         operation loads the existing version of the object from the database.
2930
2931         .. versionadded:: 1.4.24
2932
2933
2934        .. seealso::
2935
2936            :func:`.make_transient_to_detached` - provides for an alternative
2937            means of "merging" a single object into the :class:`.Session`
2938
2939        """
2940
2941        if self._warn_on_events:
2942            self._flush_warning("Session.merge()")
2943
2944        _recursive = {}
2945        _resolve_conflict_map = {}
2946
2947        if load:
2948            # flush current contents if we expect to load data
2949            self._autoflush()
2950
2951        object_mapper(instance)  # verify mapped
2952        autoflush = self.autoflush
2953        try:
2954            self.autoflush = False
2955            return self._merge(
2956                attributes.instance_state(instance),
2957                attributes.instance_dict(instance),
2958                load=load,
2959                options=options,
2960                _recursive=_recursive,
2961                _resolve_conflict_map=_resolve_conflict_map,
2962            )
2963        finally:
2964            self.autoflush = autoflush
2965
2966    def _merge(
2967        self,
2968        state,
2969        state_dict,
2970        load=True,
2971        options=None,
2972        _recursive=None,
2973        _resolve_conflict_map=None,
2974    ):
2975        mapper = _state_mapper(state)
2976        if state in _recursive:
2977            return _recursive[state]
2978
2979        new_instance = False
2980        key = state.key
2981
2982        if key is None:
2983            if state in self._new:
2984                util.warn(
2985                    "Instance %s is already pending in this Session yet is "
2986                    "being merged again; this is probably not what you want "
2987                    "to do" % state_str(state)
2988                )
2989
2990            if not load:
2991                raise sa_exc.InvalidRequestError(
2992                    "merge() with load=False option does not support "
2993                    "objects transient (i.e. unpersisted) objects.  flush() "
2994                    "all changes on mapped instances before merging with "
2995                    "load=False."
2996                )
2997            key = mapper._identity_key_from_state(state)
2998            key_is_persistent = attributes.NEVER_SET not in key[1] and (
2999                not _none_set.intersection(key[1])
3000                or (
3001                    mapper.allow_partial_pks
3002                    and not _none_set.issuperset(key[1])
3003                )
3004            )
3005        else:
3006            key_is_persistent = True
3007
3008        if key in self.identity_map:
3009            try:
3010                merged = self.identity_map[key]
3011            except KeyError:
3012                # object was GC'ed right as we checked for it
3013                merged = None
3014        else:
3015            merged = None
3016
3017        if merged is None:
3018            if key_is_persistent and key in _resolve_conflict_map:
3019                merged = _resolve_conflict_map[key]
3020
3021            elif not load:
3022                if state.modified:
3023                    raise sa_exc.InvalidRequestError(
3024                        "merge() with load=False option does not support "
3025                        "objects marked as 'dirty'.  flush() all changes on "
3026                        "mapped instances before merging with load=False."
3027                    )
3028                merged = mapper.class_manager.new_instance()
3029                merged_state = attributes.instance_state(merged)
3030                merged_state.key = key
3031                self._update_impl(merged_state)
3032                new_instance = True
3033
3034            elif key_is_persistent:
3035                merged = self.get(
3036                    mapper.class_,
3037                    key[1],
3038                    identity_token=key[2],
3039                    options=options,
3040                )
3041
3042        if merged is None:
3043            merged = mapper.class_manager.new_instance()
3044            merged_state = attributes.instance_state(merged)
3045            merged_dict = attributes.instance_dict(merged)
3046            new_instance = True
3047            self._save_or_update_state(merged_state)
3048        else:
3049            merged_state = attributes.instance_state(merged)
3050            merged_dict = attributes.instance_dict(merged)
3051
3052        _recursive[state] = merged
3053        _resolve_conflict_map[key] = merged
3054
3055        # check that we didn't just pull the exact same
3056        # state out.
3057        if state is not merged_state:
3058            # version check if applicable
3059            if mapper.version_id_col is not None:
3060                existing_version = mapper._get_state_attr_by_column(
3061                    state,
3062                    state_dict,
3063                    mapper.version_id_col,
3064                    passive=attributes.PASSIVE_NO_INITIALIZE,
3065                )
3066
3067                merged_version = mapper._get_state_attr_by_column(
3068                    merged_state,
3069                    merged_dict,
3070                    mapper.version_id_col,
3071                    passive=attributes.PASSIVE_NO_INITIALIZE,
3072                )
3073
3074                if (
3075                    existing_version is not attributes.PASSIVE_NO_RESULT
3076                    and merged_version is not attributes.PASSIVE_NO_RESULT
3077                    and existing_version != merged_version
3078                ):
3079                    raise exc.StaleDataError(
3080                        "Version id '%s' on merged state %s "
3081                        "does not match existing version '%s'. "
3082                        "Leave the version attribute unset when "
3083                        "merging to update the most recent version."
3084                        % (
3085                            existing_version,
3086                            state_str(merged_state),
3087                            merged_version,
3088                        )
3089                    )
3090
3091            merged_state.load_path = state.load_path
3092            merged_state.load_options = state.load_options
3093
3094            # since we are copying load_options, we need to copy
3095            # the callables_ that would have been generated by those
3096            # load_options.
3097            # assumes that the callables we put in state.callables_
3098            # are not instance-specific (which they should not be)
3099            merged_state._copy_callables(state)
3100
3101            for prop in mapper.iterate_properties:
3102                prop.merge(
3103                    self,
3104                    state,
3105                    state_dict,
3106                    merged_state,
3107                    merged_dict,
3108                    load,
3109                    _recursive,
3110                    _resolve_conflict_map,
3111                )
3112
3113        if not load:
3114            # remove any history
3115            merged_state._commit_all(merged_dict, self.identity_map)
3116
3117        if new_instance:
3118            merged_state.manager.dispatch.load(merged_state, None)
3119        return merged
3120
3121    def _validate_persistent(self, state):
3122        if not self.identity_map.contains_state(state):
3123            raise sa_exc.InvalidRequestError(
3124                "Instance '%s' is not persistent within this Session"
3125                % state_str(state)
3126            )
3127
3128    def _save_impl(self, state):
3129        if state.key is not None:
3130            raise sa_exc.InvalidRequestError(
3131                "Object '%s' already has an identity - "
3132                "it can't be registered as pending" % state_str(state)
3133            )
3134
3135        obj = state.obj()
3136        to_attach = self._before_attach(state, obj)
3137        if state not in self._new:
3138            self._new[state] = obj
3139            state.insert_order = len(self._new)
3140        if to_attach:
3141            self._after_attach(state, obj)
3142
3143    def _update_impl(self, state, revert_deletion=False):
3144        if state.key is None:
3145            raise sa_exc.InvalidRequestError(
3146                "Instance '%s' is not persisted" % state_str(state)
3147            )
3148
3149        if state._deleted:
3150            if revert_deletion:
3151                if not state._attached:
3152                    return
3153                del state._deleted
3154            else:
3155                raise sa_exc.InvalidRequestError(
3156                    "Instance '%s' has been deleted.  "
3157                    "Use the make_transient() "
3158                    "function to send this object back "
3159                    "to the transient state." % state_str(state)
3160                )
3161
3162        obj = state.obj()
3163
3164        # check for late gc
3165        if obj is None:
3166            return
3167
3168        to_attach = self._before_attach(state, obj)
3169
3170        self._deleted.pop(state, None)
3171        if revert_deletion:
3172            self.identity_map.replace(state)
3173        else:
3174            self.identity_map.add(state)
3175
3176        if to_attach:
3177            self._after_attach(state, obj)
3178        elif revert_deletion:
3179            self.dispatch.deleted_to_persistent(self, state)
3180
3181    def _save_or_update_impl(self, state):
3182        if state.key is None:
3183            self._save_impl(state)
3184        else:
3185            self._update_impl(state)
3186
3187    def enable_relationship_loading(self, obj):
3188        """Associate an object with this :class:`.Session` for related
3189        object loading.
3190
3191        .. warning::
3192
3193            :meth:`.enable_relationship_loading` exists to serve special
3194            use cases and is not recommended for general use.
3195
3196        Accesses of attributes mapped with :func:`_orm.relationship`
3197        will attempt to load a value from the database using this
3198        :class:`.Session` as the source of connectivity.  The values
3199        will be loaded based on foreign key and primary key values
3200        present on this object - if not present, then those relationships
3201        will be unavailable.
3202
3203        The object will be attached to this session, but will
3204        **not** participate in any persistence operations; its state
3205        for almost all purposes will remain either "transient" or
3206        "detached", except for the case of relationship loading.
3207
3208        Also note that backrefs will often not work as expected.
3209        Altering a relationship-bound attribute on the target object
3210        may not fire off a backref event, if the effective value
3211        is what was already loaded from a foreign-key-holding value.
3212
3213        The :meth:`.Session.enable_relationship_loading` method is
3214        similar to the ``load_on_pending`` flag on :func:`_orm.relationship`.
3215        Unlike that flag, :meth:`.Session.enable_relationship_loading` allows
3216        an object to remain transient while still being able to load
3217        related items.
3218
3219        To make a transient object associated with a :class:`.Session`
3220        via :meth:`.Session.enable_relationship_loading` pending, add
3221        it to the :class:`.Session` using :meth:`.Session.add` normally.
3222        If the object instead represents an existing identity in the database,
3223        it should be merged using :meth:`.Session.merge`.
3224
3225        :meth:`.Session.enable_relationship_loading` does not improve
3226        behavior when the ORM is used normally - object references should be
3227        constructed at the object level, not at the foreign key level, so
3228        that they are present in an ordinary way before flush()
3229        proceeds.  This method is not intended for general use.
3230
3231        .. seealso::
3232
3233            :paramref:`_orm.relationship.load_on_pending` - this flag
3234            allows per-relationship loading of many-to-ones on items that
3235            are pending.
3236
3237            :func:`.make_transient_to_detached` - allows for an object to
3238            be added to a :class:`.Session` without SQL emitted, which then
3239            will unexpire attributes on access.
3240
3241        """
3242        try:
3243            state = attributes.instance_state(obj)
3244        except exc.NO_STATE as err:
3245            util.raise_(
3246                exc.UnmappedInstanceError(obj),
3247                replace_context=err,
3248            )
3249
3250        to_attach = self._before_attach(state, obj)
3251        state._load_pending = True
3252        if to_attach:
3253            self._after_attach(state, obj)
3254
3255    def _before_attach(self, state, obj):
3256        self._autobegin()
3257
3258        if state.session_id == self.hash_key:
3259            return False
3260
3261        if state.session_id and state.session_id in _sessions:
3262            raise sa_exc.InvalidRequestError(
3263                "Object '%s' is already attached to session '%s' "
3264                "(this is '%s')"
3265                % (state_str(state), state.session_id, self.hash_key)
3266            )
3267
3268        self.dispatch.before_attach(self, state)
3269
3270        return True
3271
3272    def _after_attach(self, state, obj):
3273        state.session_id = self.hash_key
3274        if state.modified and state._strong_obj is None:
3275            state._strong_obj = obj
3276        self.dispatch.after_attach(self, state)
3277
3278        if state.key:
3279            self.dispatch.detached_to_persistent(self, state)
3280        else:
3281            self.dispatch.transient_to_pending(self, state)
3282
3283    def __contains__(self, instance):
3284        """Return True if the instance is associated with this session.
3285
3286        The instance may be pending or persistent within the Session for a
3287        result of True.
3288
3289        """
3290        try:
3291            state = attributes.instance_state(instance)
3292        except exc.NO_STATE as err:
3293            util.raise_(
3294                exc.UnmappedInstanceError(instance),
3295                replace_context=err,
3296            )
3297        return self._contains_state(state)
3298
3299    def __iter__(self):
3300        """Iterate over all pending or persistent instances within this
3301        Session.
3302
3303        """
3304        return iter(
3305            list(self._new.values()) + list(self.identity_map.values())
3306        )
3307
3308    def _contains_state(self, state):
3309        return state in self._new or self.identity_map.contains_state(state)
3310
3311    def flush(self, objects=None):
3312        """Flush all the object changes to the database.
3313
3314        Writes out all pending object creations, deletions and modifications
3315        to the database as INSERTs, DELETEs, UPDATEs, etc.  Operations are
3316        automatically ordered by the Session's unit of work dependency
3317        solver.
3318
3319        Database operations will be issued in the current transactional
3320        context and do not affect the state of the transaction, unless an
3321        error occurs, in which case the entire transaction is rolled back.
3322        You may flush() as often as you like within a transaction to move
3323        changes from Python to the database's transaction buffer.
3324
3325        For ``autocommit`` Sessions with no active manual transaction, flush()
3326        will create a transaction on the fly that surrounds the entire set of
3327        operations into the flush.
3328
3329        :param objects: Optional; restricts the flush operation to operate
3330          only on elements that are in the given collection.
3331
3332          This feature is for an extremely narrow set of use cases where
3333          particular objects may need to be operated upon before the
3334          full flush() occurs.  It is not intended for general use.
3335
3336        """
3337
3338        if self._flushing:
3339            raise sa_exc.InvalidRequestError("Session is already flushing")
3340
3341        if self._is_clean():
3342            return
3343        try:
3344            self._flushing = True
3345            self._flush(objects)
3346        finally:
3347            self._flushing = False
3348
3349    def _flush_warning(self, method):
3350        util.warn(
3351            "Usage of the '%s' operation is not currently supported "
3352            "within the execution stage of the flush process. "
3353            "Results may not be consistent.  Consider using alternative "
3354            "event listeners or connection-level operations instead." % method
3355        )
3356
3357    def _is_clean(self):
3358        return (
3359            not self.identity_map.check_modified()
3360            and not self._deleted
3361            and not self._new
3362        )
3363
3364    def _flush(self, objects=None):
3365
3366        dirty = self._dirty_states
3367        if not dirty and not self._deleted and not self._new:
3368            self.identity_map._modified.clear()
3369            return
3370
3371        flush_context = UOWTransaction(self)
3372
3373        if self.dispatch.before_flush:
3374            self.dispatch.before_flush(self, flush_context, objects)
3375            # re-establish "dirty states" in case the listeners
3376            # added
3377            dirty = self._dirty_states
3378
3379        deleted = set(self._deleted)
3380        new = set(self._new)
3381
3382        dirty = set(dirty).difference(deleted)
3383
3384        # create the set of all objects we want to operate upon
3385        if objects:
3386            # specific list passed in
3387            objset = set()
3388            for o in objects:
3389                try:
3390                    state = attributes.instance_state(o)
3391
3392                except exc.NO_STATE as err:
3393                    util.raise_(
3394                        exc.UnmappedInstanceError(o),
3395                        replace_context=err,
3396                    )
3397                objset.add(state)
3398        else:
3399            objset = None
3400
3401        # store objects whose fate has been decided
3402        processed = set()
3403
3404        # put all saves/updates into the flush context.  detect top-level
3405        # orphans and throw them into deleted.
3406        if objset:
3407            proc = new.union(dirty).intersection(objset).difference(deleted)
3408        else:
3409            proc = new.union(dirty).difference(deleted)
3410
3411        for state in proc:
3412            is_orphan = _state_mapper(state)._is_orphan(state)
3413
3414            is_persistent_orphan = is_orphan and state.has_identity
3415
3416            if (
3417                is_orphan
3418                and not is_persistent_orphan
3419                and state._orphaned_outside_of_session
3420            ):
3421                self._expunge_states([state])
3422            else:
3423                _reg = flush_context.register_object(
3424                    state, isdelete=is_persistent_orphan
3425                )
3426                assert _reg, "Failed to add object to the flush context!"
3427                processed.add(state)
3428
3429        # put all remaining deletes into the flush context.
3430        if objset:
3431            proc = deleted.intersection(objset).difference(processed)
3432        else:
3433            proc = deleted.difference(processed)
3434        for state in proc:
3435            _reg = flush_context.register_object(state, isdelete=True)
3436            assert _reg, "Failed to add object to the flush context!"
3437
3438        if not flush_context.has_work:
3439            return
3440
3441        flush_context.transaction = transaction = self.begin(_subtrans=True)
3442        try:
3443            self._warn_on_events = True
3444            try:
3445                flush_context.execute()
3446            finally:
3447                self._warn_on_events = False
3448
3449            self.dispatch.after_flush(self, flush_context)
3450
3451            flush_context.finalize_flush_changes()
3452
3453            if not objects and self.identity_map._modified:
3454                len_ = len(self.identity_map._modified)
3455
3456                statelib.InstanceState._commit_all_states(
3457                    [
3458                        (state, state.dict)
3459                        for state in self.identity_map._modified
3460                    ],
3461                    instance_dict=self.identity_map,
3462                )
3463                util.warn(
3464                    "Attribute history events accumulated on %d "
3465                    "previously clean instances "
3466                    "within inner-flush event handlers have been "
3467                    "reset, and will not result in database updates. "
3468                    "Consider using set_committed_value() within "
3469                    "inner-flush event handlers to avoid this warning." % len_
3470                )
3471
3472            # useful assertions:
3473            # if not objects:
3474            #    assert not self.identity_map._modified
3475            # else:
3476            #    assert self.identity_map._modified == \
3477            #            self.identity_map._modified.difference(objects)
3478
3479            self.dispatch.after_flush_postexec(self, flush_context)
3480
3481            transaction.commit()
3482
3483        except:
3484            with util.safe_reraise():
3485                transaction.rollback(_capture_exception=True)
3486
3487    def bulk_save_objects(
3488        self,
3489        objects,
3490        return_defaults=False,
3491        update_changed_only=True,
3492        preserve_order=True,
3493    ):
3494        """Perform a bulk save of the given list of objects.
3495
3496        The bulk save feature allows mapped objects to be used as the
3497        source of simple INSERT and UPDATE operations which can be more easily
3498        grouped together into higher performing "executemany"
3499        operations; the extraction of data from the objects is also performed
3500        using a lower-latency process that ignores whether or not attributes
3501        have actually been modified in the case of UPDATEs, and also ignores
3502        SQL expressions.
3503
3504        The objects as given are not added to the session and no additional
3505        state is established on them. If the
3506        :paramref:`_orm.Session.bulk_save_objects.return_defaults` flag is set,
3507        then server-generated primary key values will be assigned to the
3508        returned objects, but **not server side defaults**; this is a
3509        limitation in the implementation. If stateful objects are desired,
3510        please use the standard :meth:`_orm.Session.add_all` approach or
3511        as an alternative newer mass-insert features such as
3512        :ref:`orm_dml_returning_objects`.
3513
3514        .. warning::
3515
3516            The bulk save feature allows for a lower-latency INSERT/UPDATE
3517            of rows at the expense of most other unit-of-work features.
3518            Features such as object management, relationship handling,
3519            and SQL clause support are **silently omitted** in favor of raw
3520            INSERT/UPDATES of records.
3521
3522            Please note that newer versions of SQLAlchemy are **greatly
3523            improving the efficiency** of the standard flush process. It is
3524            **strongly recommended** to not use the bulk methods as they
3525            represent a forking of SQLAlchemy's functionality and are slowly
3526            being moved into legacy status.  New features such as
3527            :ref:`orm_dml_returning_objects` are both more efficient than
3528            the "bulk" methods and provide more predictable functionality.
3529
3530            **Please read the list of caveats at**
3531            :ref:`bulk_operations_caveats` **before using this method, and
3532            fully test and confirm the functionality of all code developed
3533            using these systems.**
3534
3535        :param objects: a sequence of mapped object instances.  The mapped
3536         objects are persisted as is, and are **not** associated with the
3537         :class:`.Session` afterwards.
3538
3539         For each object, whether the object is sent as an INSERT or an
3540         UPDATE is dependent on the same rules used by the :class:`.Session`
3541         in traditional operation; if the object has the
3542         :attr:`.InstanceState.key`
3543         attribute set, then the object is assumed to be "detached" and
3544         will result in an UPDATE.  Otherwise, an INSERT is used.
3545
3546         In the case of an UPDATE, statements are grouped based on which
3547         attributes have changed, and are thus to be the subject of each
3548         SET clause.  If ``update_changed_only`` is False, then all
3549         attributes present within each object are applied to the UPDATE
3550         statement, which may help in allowing the statements to be grouped
3551         together into a larger executemany(), and will also reduce the
3552         overhead of checking history on attributes.
3553
3554        :param return_defaults: when True, rows that are missing values which
3555         generate defaults, namely integer primary key defaults and sequences,
3556         will be inserted **one at a time**, so that the primary key value
3557         is available.  In particular this will allow joined-inheritance
3558         and other multi-table mappings to insert correctly without the need
3559         to provide primary key values ahead of time; however,
3560         :paramref:`.Session.bulk_save_objects.return_defaults` **greatly
3561         reduces the performance gains** of the method overall.  It is strongly
3562         advised to please use the standard :meth:`_orm.Session.add_all`
3563         approach.
3564
3565        :param update_changed_only: when True, UPDATE statements are rendered
3566         based on those attributes in each state that have logged changes.
3567         When False, all attributes present are rendered into the SET clause
3568         with the exception of primary key attributes.
3569
3570        :param preserve_order: when True, the order of inserts and updates
3571         matches exactly the order in which the objects are given.   When
3572         False, common types of objects are grouped into inserts
3573         and updates, to allow for more batching opportunities.
3574
3575         .. versionadded:: 1.3
3576
3577        .. seealso::
3578
3579            :ref:`bulk_operations`
3580
3581            :meth:`.Session.bulk_insert_mappings`
3582
3583            :meth:`.Session.bulk_update_mappings`
3584
3585        """
3586
3587        def key(state):
3588            return (state.mapper, state.key is not None)
3589
3590        obj_states = (attributes.instance_state(obj) for obj in objects)
3591        if not preserve_order:
3592            obj_states = sorted(obj_states, key=key)
3593
3594        for (mapper, isupdate), states in itertools.groupby(obj_states, key):
3595            self._bulk_save_mappings(
3596                mapper,
3597                states,
3598                isupdate,
3599                True,
3600                return_defaults,
3601                update_changed_only,
3602                False,
3603            )
3604
3605    def bulk_insert_mappings(
3606        self, mapper, mappings, return_defaults=False, render_nulls=False
3607    ):
3608        """Perform a bulk insert of the given list of mapping dictionaries.
3609
3610        The bulk insert feature allows plain Python dictionaries to be used as
3611        the source of simple INSERT operations which can be more easily
3612        grouped together into higher performing "executemany"
3613        operations.  Using dictionaries, there is no "history" or session
3614        state management features in use, reducing latency when inserting
3615        large numbers of simple rows.
3616
3617        The values within the dictionaries as given are typically passed
3618        without modification into Core :meth:`_expression.Insert` constructs,
3619        after
3620        organizing the values within them across the tables to which
3621        the given mapper is mapped.
3622
3623        .. versionadded:: 1.0.0
3624
3625        .. warning::
3626
3627            The bulk insert feature allows for a lower-latency INSERT
3628            of rows at the expense of most other unit-of-work features.
3629            Features such as object management, relationship handling,
3630            and SQL clause support are **silently omitted** in favor of raw
3631            INSERT of records.
3632
3633            Please note that newer versions of SQLAlchemy are **greatly
3634            improving the efficiency** of the standard flush process. It is
3635            **strongly recommended** to not use the bulk methods as they
3636            represent a forking of SQLAlchemy's functionality and are slowly
3637            being moved into legacy status.  New features such as
3638            :ref:`orm_dml_returning_objects` are both more efficient than
3639            the "bulk" methods and provide more predictable functionality.
3640
3641            **Please read the list of caveats at**
3642            :ref:`bulk_operations_caveats` **before using this method, and
3643            fully test and confirm the functionality of all code developed
3644            using these systems.**
3645
3646        :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
3647         object,
3648         representing the single kind of object represented within the mapping
3649         list.
3650
3651        :param mappings: a sequence of dictionaries, each one containing the
3652         state of the mapped row to be inserted, in terms of the attribute
3653         names on the mapped class.   If the mapping refers to multiple tables,
3654         such as a joined-inheritance mapping, each dictionary must contain all
3655         keys to be populated into all tables.
3656
3657        :param return_defaults: when True, rows that are missing values which
3658         generate defaults, namely integer primary key defaults and sequences,
3659         will be inserted **one at a time**, so that the primary key value
3660         is available.  In particular this will allow joined-inheritance
3661         and other multi-table mappings to insert correctly without the need
3662         to provide primary
3663         key values ahead of time; however,
3664         :paramref:`.Session.bulk_insert_mappings.return_defaults`
3665         **greatly reduces the performance gains** of the method overall.
3666         If the rows
3667         to be inserted only refer to a single table, then there is no
3668         reason this flag should be set as the returned default information
3669         is not used.
3670
3671        :param render_nulls: When True, a value of ``None`` will result
3672         in a NULL value being included in the INSERT statement, rather
3673         than the column being omitted from the INSERT.   This allows all
3674         the rows being INSERTed to have the identical set of columns which
3675         allows the full set of rows to be batched to the DBAPI.  Normally,
3676         each column-set that contains a different combination of NULL values
3677         than the previous row must omit a different series of columns from
3678         the rendered INSERT statement, which means it must be emitted as a
3679         separate statement.   By passing this flag, the full set of rows
3680         are guaranteed to be batchable into one batch; the cost however is
3681         that server-side defaults which are invoked by an omitted column will
3682         be skipped, so care must be taken to ensure that these are not
3683         necessary.
3684
3685         .. warning::
3686
3687            When this flag is set, **server side default SQL values will
3688            not be invoked** for those columns that are inserted as NULL;
3689            the NULL value will be sent explicitly.   Care must be taken
3690            to ensure that no server-side default functions need to be
3691            invoked for the operation as a whole.
3692
3693         .. versionadded:: 1.1
3694
3695        .. seealso::
3696
3697            :ref:`bulk_operations`
3698
3699            :meth:`.Session.bulk_save_objects`
3700
3701            :meth:`.Session.bulk_update_mappings`
3702
3703        """
3704        self._bulk_save_mappings(
3705            mapper,
3706            mappings,
3707            False,
3708            False,
3709            return_defaults,
3710            False,
3711            render_nulls,
3712        )
3713
3714    def bulk_update_mappings(self, mapper, mappings):
3715        """Perform a bulk update of the given list of mapping dictionaries.
3716
3717        The bulk update feature allows plain Python dictionaries to be used as
3718        the source of simple UPDATE operations which can be more easily
3719        grouped together into higher performing "executemany"
3720        operations.  Using dictionaries, there is no "history" or session
3721        state management features in use, reducing latency when updating
3722        large numbers of simple rows.
3723
3724        .. versionadded:: 1.0.0
3725
3726        .. warning::
3727
3728            The bulk update feature allows for a lower-latency UPDATE
3729            of rows at the expense of most other unit-of-work features.
3730            Features such as object management, relationship handling,
3731            and SQL clause support are **silently omitted** in favor of raw
3732            UPDATES of records.
3733
3734            Please note that newer versions of SQLAlchemy are **greatly
3735            improving the efficiency** of the standard flush process. It is
3736            **strongly recommended** to not use the bulk methods as they
3737            represent a forking of SQLAlchemy's functionality and are slowly
3738            being moved into legacy status.  New features such as
3739            :ref:`orm_dml_returning_objects` are both more efficient than
3740            the "bulk" methods and provide more predictable functionality.
3741
3742            **Please read the list of caveats at**
3743            :ref:`bulk_operations_caveats` **before using this method, and
3744            fully test and confirm the functionality of all code developed
3745            using these systems.**
3746
3747        :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
3748         object,
3749         representing the single kind of object represented within the mapping
3750         list.
3751
3752        :param mappings: a sequence of dictionaries, each one containing the
3753         state of the mapped row to be updated, in terms of the attribute names
3754         on the mapped class.   If the mapping refers to multiple tables, such
3755         as a joined-inheritance mapping, each dictionary may contain keys
3756         corresponding to all tables.   All those keys which are present and
3757         are not part of the primary key are applied to the SET clause of the
3758         UPDATE statement; the primary key values, which are required, are
3759         applied to the WHERE clause.
3760
3761
3762        .. seealso::
3763
3764            :ref:`bulk_operations`
3765
3766            :meth:`.Session.bulk_insert_mappings`
3767
3768            :meth:`.Session.bulk_save_objects`
3769
3770        """
3771        self._bulk_save_mappings(
3772            mapper, mappings, True, False, False, False, False
3773        )
3774
3775    def _bulk_save_mappings(
3776        self,
3777        mapper,
3778        mappings,
3779        isupdate,
3780        isstates,
3781        return_defaults,
3782        update_changed_only,
3783        render_nulls,
3784    ):
3785        mapper = _class_to_mapper(mapper)
3786        self._flushing = True
3787
3788        transaction = self.begin(_subtrans=True)
3789        try:
3790            if isupdate:
3791                persistence._bulk_update(
3792                    mapper,
3793                    mappings,
3794                    transaction,
3795                    isstates,
3796                    update_changed_only,
3797                )
3798            else:
3799                persistence._bulk_insert(
3800                    mapper,
3801                    mappings,
3802                    transaction,
3803                    isstates,
3804                    return_defaults,
3805                    render_nulls,
3806                )
3807            transaction.commit()
3808
3809        except:
3810            with util.safe_reraise():
3811                transaction.rollback(_capture_exception=True)
3812        finally:
3813            self._flushing = False
3814
3815    def is_modified(self, instance, include_collections=True):
3816        r"""Return ``True`` if the given instance has locally
3817        modified attributes.
3818
3819        This method retrieves the history for each instrumented
3820        attribute on the instance and performs a comparison of the current
3821        value to its previously committed value, if any.
3822
3823        It is in effect a more expensive and accurate
3824        version of checking for the given instance in the
3825        :attr:`.Session.dirty` collection; a full test for
3826        each attribute's net "dirty" status is performed.
3827
3828        E.g.::
3829
3830            return session.is_modified(someobject)
3831
3832        A few caveats to this method apply:
3833
3834        * Instances present in the :attr:`.Session.dirty` collection may
3835          report ``False`` when tested with this method.  This is because
3836          the object may have received change events via attribute mutation,
3837          thus placing it in :attr:`.Session.dirty`, but ultimately the state
3838          is the same as that loaded from the database, resulting in no net
3839          change here.
3840        * Scalar attributes may not have recorded the previously set
3841          value when a new value was applied, if the attribute was not loaded,
3842          or was expired, at the time the new value was received - in these
3843          cases, the attribute is assumed to have a change, even if there is
3844          ultimately no net change against its database value. SQLAlchemy in
3845          most cases does not need the "old" value when a set event occurs, so
3846          it skips the expense of a SQL call if the old value isn't present,
3847          based on the assumption that an UPDATE of the scalar value is
3848          usually needed, and in those few cases where it isn't, is less
3849          expensive on average than issuing a defensive SELECT.
3850
3851          The "old" value is fetched unconditionally upon set only if the
3852          attribute container has the ``active_history`` flag set to ``True``.
3853          This flag is set typically for primary key attributes and scalar
3854          object references that are not a simple many-to-one.  To set this
3855          flag for any arbitrary mapped column, use the ``active_history``
3856          argument with :func:`.column_property`.
3857
3858        :param instance: mapped instance to be tested for pending changes.
3859        :param include_collections: Indicates if multivalued collections
3860         should be included in the operation.  Setting this to ``False`` is a
3861         way to detect only local-column based properties (i.e. scalar columns
3862         or many-to-one foreign keys) that would result in an UPDATE for this
3863         instance upon flush.
3864
3865        """
3866        state = object_state(instance)
3867
3868        if not state.modified:
3869            return False
3870
3871        dict_ = state.dict
3872
3873        for attr in state.manager.attributes:
3874            if (
3875                not include_collections
3876                and hasattr(attr.impl, "get_collection")
3877            ) or not hasattr(attr.impl, "get_history"):
3878                continue
3879
3880            (added, unchanged, deleted) = attr.impl.get_history(
3881                state, dict_, passive=attributes.NO_CHANGE
3882            )
3883
3884            if added or deleted:
3885                return True
3886        else:
3887            return False
3888
3889    @property
3890    def is_active(self):
3891        """True if this :class:`.Session` not in "partial rollback" state.
3892
3893        .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins
3894           a new transaction immediately, so this attribute will be False
3895           when the :class:`_orm.Session` is first instantiated.
3896
3897        "partial rollback" state typically indicates that the flush process
3898        of the :class:`_orm.Session` has failed, and that the
3899        :meth:`_orm.Session.rollback` method must be emitted in order to
3900        fully roll back the transaction.
3901
3902        If this :class:`_orm.Session` is not in a transaction at all, the
3903        :class:`_orm.Session` will autobegin when it is first used, so in this
3904        case :attr:`_orm.Session.is_active` will return True.
3905
3906        Otherwise, if this :class:`_orm.Session` is within a transaction,
3907        and that transaction has not been rolled back internally, the
3908        :attr:`_orm.Session.is_active` will also return True.
3909
3910        .. seealso::
3911
3912            :ref:`faq_session_rollback`
3913
3914            :meth:`_orm.Session.in_transaction`
3915
3916        """
3917        if self.autocommit:
3918            return (
3919                self._transaction is not None and self._transaction.is_active
3920            )
3921        else:
3922            return self._transaction is None or self._transaction.is_active
3923
3924    identity_map = None
3925    """A mapping of object identities to objects themselves.
3926
3927    Iterating through ``Session.identity_map.values()`` provides
3928    access to the full set of persistent objects (i.e., those
3929    that have row identity) currently in the session.
3930
3931    .. seealso::
3932
3933        :func:`.identity_key` - helper function to produce the keys used
3934        in this dictionary.
3935
3936    """
3937
3938    @property
3939    def _dirty_states(self):
3940        """The set of all persistent states considered dirty.
3941
3942        This method returns all states that were modified including
3943        those that were possibly deleted.
3944
3945        """
3946        return self.identity_map._dirty_states()
3947
3948    @property
3949    def dirty(self):
3950        """The set of all persistent instances considered dirty.
3951
3952        E.g.::
3953
3954            some_mapped_object in session.dirty
3955
3956        Instances are considered dirty when they were modified but not
3957        deleted.
3958
3959        Note that this 'dirty' calculation is 'optimistic'; most
3960        attribute-setting or collection modification operations will
3961        mark an instance as 'dirty' and place it in this set, even if
3962        there is no net change to the attribute's value.  At flush
3963        time, the value of each attribute is compared to its
3964        previously saved value, and if there's no net change, no SQL
3965        operation will occur (this is a more expensive operation so
3966        it's only done at flush time).
3967
3968        To check if an instance has actionable net changes to its
3969        attributes, use the :meth:`.Session.is_modified` method.
3970
3971        """
3972        return util.IdentitySet(
3973            [
3974                state.obj()
3975                for state in self._dirty_states
3976                if state not in self._deleted
3977            ]
3978        )
3979
3980    @property
3981    def deleted(self):
3982        "The set of all instances marked as 'deleted' within this ``Session``"
3983
3984        return util.IdentitySet(list(self._deleted.values()))
3985
3986    @property
3987    def new(self):
3988        "The set of all instances marked as 'new' within this ``Session``."
3989
3990        return util.IdentitySet(list(self._new.values()))
3991
3992
3993class sessionmaker(_SessionClassMethods):
3994    """A configurable :class:`.Session` factory.
3995
3996    The :class:`.sessionmaker` factory generates new
3997    :class:`.Session` objects when called, creating them given
3998    the configurational arguments established here.
3999
4000    e.g.::
4001
4002        from sqlalchemy import create_engine
4003        from sqlalchemy.orm import sessionmaker
4004
4005        # an Engine, which the Session will use for connection
4006        # resources
4007        engine = create_engine('postgresql://scott:tiger@localhost/')
4008
4009        Session = sessionmaker(engine)
4010
4011        with Session() as session:
4012            session.add(some_object)
4013            session.add(some_other_object)
4014            session.commit()
4015
4016    Context manager use is optional; otherwise, the returned
4017    :class:`_orm.Session` object may be closed explicitly via the
4018    :meth:`_orm.Session.close` method.   Using a
4019    ``try:/finally:`` block is optional, however will ensure that the close
4020    takes place even if there are database errors::
4021
4022        session = Session()
4023        try:
4024            session.add(some_object)
4025            session.add(some_other_object)
4026            session.commit()
4027        finally:
4028            session.close()
4029
4030    :class:`.sessionmaker` acts as a factory for :class:`_orm.Session`
4031    objects in the same way as an :class:`_engine.Engine` acts as a factory
4032    for :class:`_engine.Connection` objects.  In this way it also includes
4033    a :meth:`_orm.sessionmaker.begin` method, that provides a context
4034    manager which both begins and commits a transaction, as well as closes
4035    out the :class:`_orm.Session` when complete, rolling back the transaction
4036    if any errors occur::
4037
4038        Session = sessionmaker(engine)
4039
4040        with Session.begin() as session:
4041            session.add(some_object)
4042            session.add(some_other_object)
4043        # commits transaction, closes session
4044
4045    .. versionadded:: 1.4
4046
4047    When calling upon :class:`_orm.sessionmaker` to construct a
4048    :class:`_orm.Session`, keyword arguments may also be passed to the
4049    method; these arguments will override that of the globally configured
4050    parameters.  Below we use a :class:`_orm.sessionmaker` bound to a certain
4051    :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead
4052    bound to a specific :class:`_engine.Connection` procured from that engine::
4053
4054        Session = sessionmaker(engine)
4055
4056        # bind an individual session to a connection
4057
4058        with engine.connect() as connection:
4059            with Session(bind=connection) as session:
4060                # work with session
4061
4062    The class also includes a method :meth:`_orm.sessionmaker.configure`, which
4063    can be used to specify additional keyword arguments to the factory, which
4064    will take effect for subsequent :class:`.Session` objects generated. This
4065    is usually used to associate one or more :class:`_engine.Engine` objects
4066    with an existing
4067    :class:`.sessionmaker` factory before it is first used::
4068
4069        # application starts, sessionmaker does not have
4070        # an engine bound yet
4071        Session = sessionmaker()
4072
4073        # ... later, when an engine URL is read from a configuration
4074        # file or other events allow the engine to be created
4075        engine = create_engine('sqlite:///foo.db')
4076        Session.configure(bind=engine)
4077
4078        sess = Session()
4079        # work with session
4080
4081    .. seealso::
4082
4083        :ref:`session_getting` - introductory text on creating
4084        sessions using :class:`.sessionmaker`.
4085
4086    """
4087
4088    def __init__(
4089        self,
4090        bind=None,
4091        class_=Session,
4092        autoflush=True,
4093        autocommit=False,
4094        expire_on_commit=True,
4095        info=None,
4096        **kw
4097    ):
4098        r"""Construct a new :class:`.sessionmaker`.
4099
4100        All arguments here except for ``class_`` correspond to arguments
4101        accepted by :class:`.Session` directly.  See the
4102        :meth:`.Session.__init__` docstring for more details on parameters.
4103
4104        :param bind: a :class:`_engine.Engine` or other :class:`.Connectable`
4105         with
4106         which newly created :class:`.Session` objects will be associated.
4107        :param class\_: class to use in order to create new :class:`.Session`
4108         objects.  Defaults to :class:`.Session`.
4109        :param autoflush: The autoflush setting to use with newly created
4110         :class:`.Session` objects.
4111        :param autocommit: The autocommit setting to use with newly created
4112         :class:`.Session` objects.
4113        :param expire_on_commit=True: the
4114         :paramref:`_orm.Session.expire_on_commit` setting to use
4115         with newly created :class:`.Session` objects.
4116
4117        :param info: optional dictionary of information that will be available
4118         via :attr:`.Session.info`.  Note this dictionary is *updated*, not
4119         replaced, when the ``info`` parameter is specified to the specific
4120         :class:`.Session` construction operation.
4121
4122        :param \**kw: all other keyword arguments are passed to the
4123         constructor of newly created :class:`.Session` objects.
4124
4125        """
4126        kw["bind"] = bind
4127        kw["autoflush"] = autoflush
4128        kw["autocommit"] = autocommit
4129        kw["expire_on_commit"] = expire_on_commit
4130        if info is not None:
4131            kw["info"] = info
4132        self.kw = kw
4133        # make our own subclass of the given class, so that
4134        # events can be associated with it specifically.
4135        self.class_ = type(class_.__name__, (class_,), {})
4136
4137    def begin(self):
4138        """Produce a context manager that both provides a new
4139        :class:`_orm.Session` as well as a transaction that commits.
4140
4141
4142        e.g.::
4143
4144            Session = sessionmaker(some_engine)
4145
4146            with Session.begin() as session:
4147                session.add(some_object)
4148
4149            # commits transaction, closes session
4150
4151        .. versionadded:: 1.4
4152
4153
4154        """
4155
4156        session = self()
4157        return session._maker_context_manager()
4158
4159    def __call__(self, **local_kw):
4160        """Produce a new :class:`.Session` object using the configuration
4161        established in this :class:`.sessionmaker`.
4162
4163        In Python, the ``__call__`` method is invoked on an object when
4164        it is "called" in the same way as a function::
4165
4166            Session = sessionmaker()
4167            session = Session()  # invokes sessionmaker.__call__()
4168
4169        """
4170        for k, v in self.kw.items():
4171            if k == "info" and "info" in local_kw:
4172                d = v.copy()
4173                d.update(local_kw["info"])
4174                local_kw["info"] = d
4175            else:
4176                local_kw.setdefault(k, v)
4177        return self.class_(**local_kw)
4178
4179    def configure(self, **new_kw):
4180        """(Re)configure the arguments for this sessionmaker.
4181
4182        e.g.::
4183
4184            Session = sessionmaker()
4185
4186            Session.configure(bind=create_engine('sqlite://'))
4187        """
4188        self.kw.update(new_kw)
4189
4190    def __repr__(self):
4191        return "%s(class_=%r, %s)" % (
4192            self.__class__.__name__,
4193            self.class_.__name__,
4194            ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()),
4195        )
4196
4197
4198def close_all_sessions():
4199    """Close all sessions in memory.
4200
4201    This function consults a global registry of all :class:`.Session` objects
4202    and calls :meth:`.Session.close` on them, which resets them to a clean
4203    state.
4204
4205    This function is not for general use but may be useful for test suites
4206    within the teardown scheme.
4207
4208    .. versionadded:: 1.3
4209
4210    """
4211
4212    for sess in _sessions.values():
4213        sess.close()
4214
4215
4216def make_transient(instance):
4217    """Alter the state of the given instance so that it is :term:`transient`.
4218
4219    .. note::
4220
4221        :func:`.make_transient` is a special-case function for
4222        advanced use cases only.
4223
4224    The given mapped instance is assumed to be in the :term:`persistent` or
4225    :term:`detached` state.   The function will remove its association with any
4226    :class:`.Session` as well as its :attr:`.InstanceState.identity`. The
4227    effect is that the object will behave as though it were newly constructed,
4228    except retaining any attribute / collection values that were loaded at the
4229    time of the call.   The :attr:`.InstanceState.deleted` flag is also reset
4230    if this object had been deleted as a result of using
4231    :meth:`.Session.delete`.
4232
4233    .. warning::
4234
4235        :func:`.make_transient` does **not** "unexpire" or otherwise eagerly
4236        load ORM-mapped attributes that are not currently loaded at the time
4237        the function is called.   This includes attributes which:
4238
4239        * were expired via :meth:`.Session.expire`
4240
4241        * were expired as the natural effect of committing a session
4242          transaction, e.g. :meth:`.Session.commit`
4243
4244        * are normally :term:`lazy loaded` but are not currently loaded
4245
4246        * are "deferred" via :ref:`deferred` and are not yet loaded
4247
4248        * were not present in the query which loaded this object, such as that
4249          which is common in joined table inheritance and other scenarios.
4250
4251        After :func:`.make_transient` is called, unloaded attributes such
4252        as those above will normally resolve to the value ``None`` when
4253        accessed, or an empty collection for a collection-oriented attribute.
4254        As the object is transient and un-associated with any database
4255        identity, it will no longer retrieve these values.
4256
4257    .. seealso::
4258
4259        :func:`.make_transient_to_detached`
4260
4261    """
4262    state = attributes.instance_state(instance)
4263    s = _state_session(state)
4264    if s:
4265        s._expunge_states([state])
4266
4267    # remove expired state
4268    state.expired_attributes.clear()
4269
4270    # remove deferred callables
4271    if state.callables:
4272        del state.callables
4273
4274    if state.key:
4275        del state.key
4276    if state._deleted:
4277        del state._deleted
4278
4279
4280def make_transient_to_detached(instance):
4281    """Make the given transient instance :term:`detached`.
4282
4283    .. note::
4284
4285        :func:`.make_transient_to_detached` is a special-case function for
4286        advanced use cases only.
4287
4288    All attribute history on the given instance
4289    will be reset as though the instance were freshly loaded
4290    from a query.  Missing attributes will be marked as expired.
4291    The primary key attributes of the object, which are required, will be made
4292    into the "key" of the instance.
4293
4294    The object can then be added to a session, or merged
4295    possibly with the load=False flag, at which point it will look
4296    as if it were loaded that way, without emitting SQL.
4297
4298    This is a special use case function that differs from a normal
4299    call to :meth:`.Session.merge` in that a given persistent state
4300    can be manufactured without any SQL calls.
4301
4302    .. seealso::
4303
4304        :func:`.make_transient`
4305
4306        :meth:`.Session.enable_relationship_loading`
4307
4308    """
4309    state = attributes.instance_state(instance)
4310    if state.session_id or state.key:
4311        raise sa_exc.InvalidRequestError("Given object must be transient")
4312    state.key = state.mapper._identity_key_from_state(state)
4313    if state._deleted:
4314        del state._deleted
4315    state._commit_all(state.dict)
4316    state._expire_attributes(state.dict, state.unloaded_expirable)
4317
4318
4319def object_session(instance):
4320    """Return the :class:`.Session` to which the given instance belongs.
4321
4322    This is essentially the same as the :attr:`.InstanceState.session`
4323    accessor.  See that attribute for details.
4324
4325    """
4326
4327    try:
4328        state = attributes.instance_state(instance)
4329    except exc.NO_STATE as err:
4330        util.raise_(
4331            exc.UnmappedInstanceError(instance),
4332            replace_context=err,
4333        )
4334    else:
4335        return _state_session(state)
4336
4337
4338_new_sessionid = util.counter()
4339