Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1473 statements  

1# orm/session.py 

2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7 

8"""Provides the Session class and related utilities.""" 

9 

10from __future__ import annotations 

11 

12import contextlib 

13from enum import Enum 

14import itertools 

15import sys 

16import typing 

17from typing import Any 

18from typing import Callable 

19from typing import cast 

20from typing import Dict 

21from typing import Generic 

22from typing import Iterable 

23from typing import Iterator 

24from typing import List 

25from typing import Literal 

26from typing import Mapping 

27from typing import NoReturn 

28from typing import Optional 

29from typing import overload 

30from typing import Protocol 

31from typing import Sequence 

32from typing import Set 

33from typing import Tuple 

34from typing import Type 

35from typing import TYPE_CHECKING 

36from typing import TypeVar 

37from typing import Union 

38import weakref 

39 

40from . import attributes 

41from . import bulk_persistence 

42from . import context 

43from . import descriptor_props 

44from . import exc 

45from . import identity 

46from . import loading 

47from . import query 

48from . import state as statelib 

49from ._typing import _O 

50from ._typing import insp_is_mapper 

51from ._typing import is_composite_class 

52from ._typing import is_orm_option 

53from ._typing import is_user_defined_option 

54from .base import _class_to_mapper 

55from .base import _none_set 

56from .base import _state_mapper 

57from .base import instance_str 

58from .base import LoaderCallableStatus 

59from .base import object_mapper 

60from .base import object_state 

61from .base import PassiveFlag 

62from .base import state_str 

63from .context import _ORMCompileState 

64from .context import FromStatement 

65from .identity import IdentityMap 

66from .query import Query 

67from .state import InstanceState 

68from .state_changes import _StateChange 

69from .state_changes import _StateChangeState 

70from .state_changes import _StateChangeStates 

71from .unitofwork import UOWTransaction 

72from .. import engine 

73from .. import exc as sa_exc 

74from .. import sql 

75from .. import util 

76from ..engine import Connection 

77from ..engine import Engine 

78from ..engine.util import TransactionalContext 

79from ..event import dispatcher 

80from ..event import EventTarget 

81from ..inspection import inspect 

82from ..inspection import Inspectable 

83from ..sql import coercions 

84from ..sql import dml 

85from ..sql import roles 

86from ..sql import Select 

87from ..sql import TableClause 

88from ..sql import visitors 

89from ..sql.base import _NoArg 

90from ..sql.base import CompileState 

91from ..sql.schema import Table 

92from ..sql.selectable import ForUpdateArg 

93from ..util import deprecated_params 

94from ..util import IdentitySet 

95from ..util.typing import Never 

96from ..util.typing import TupleAny 

97from ..util.typing import TypeVarTuple 

98from ..util.typing import Unpack 

99 

100if typing.TYPE_CHECKING: 

101 from ._typing import _EntityType 

102 from ._typing import _IdentityKeyType 

103 from ._typing import _InstanceDict 

104 from ._typing import OrmExecuteOptionsParameter 

105 from .interfaces import ORMOption 

106 from .interfaces import UserDefinedOption 

107 from .mapper import Mapper 

108 from .path_registry import PathRegistry 

109 from .query import RowReturningQuery 

110 from ..engine import Result 

111 from ..engine import Row 

112 from ..engine import RowMapping 

113 from ..engine.base import Transaction 

114 from ..engine.base import TwoPhaseTransaction 

115 from ..engine.interfaces import _CoreAnyExecuteParams 

116 from ..engine.interfaces import _CoreSingleExecuteParams 

117 from ..engine.interfaces import _ExecuteOptions 

118 from ..engine.interfaces import CoreExecuteOptionsParameter 

119 from ..engine.result import ScalarResult 

120 from ..event import _InstanceLevelDispatch 

121 from ..sql._typing import _ColumnsClauseArgument 

122 from ..sql._typing import _InfoType 

123 from ..sql._typing import _T0 

124 from ..sql._typing import _T1 

125 from ..sql._typing import _T2 

126 from ..sql._typing import _T3 

127 from ..sql._typing import _T4 

128 from ..sql._typing import _T5 

129 from ..sql._typing import _T6 

130 from ..sql._typing import _T7 

131 from ..sql._typing import _TypedColumnClauseArgument as _TCCA 

132 from ..sql.base import Executable 

133 from ..sql.base import ExecutableOption 

134 from ..sql.elements import ClauseElement 

135 from ..sql.roles import TypedColumnsClauseRole 

136 from ..sql.selectable import ForUpdateParameter 

137 from ..sql.selectable import TypedReturnsRows 

138 

139_T = TypeVar("_T", bound=Any) 

140_Ts = TypeVarTuple("_Ts") 

141 

142__all__ = [ 

143 "Session", 

144 "SessionTransaction", 

145 "sessionmaker", 

146 "ORMExecuteState", 

147 "close_all_sessions", 

148 "make_transient", 

149 "make_transient_to_detached", 

150 "object_session", 

151] 

152 

153_sessions: weakref.WeakValueDictionary[int, Session] = ( 

154 weakref.WeakValueDictionary() 

155) 

156"""Weak-referencing dictionary of :class:`.Session` objects. 

157""" 

158 

159statelib._sessions = _sessions 

160 

161_PKIdentityArgument = Union[Any, Tuple[Any, ...]] 

162 

163_BindArguments = Dict[str, Any] 

164 

165_EntityBindKey = Union[Type[_O], "Mapper[_O]"] 

166_SessionBindKey = Union[Type[Any], "Mapper[Any]", "TableClause", str] 

167_SessionBind = Union["Engine", "Connection"] 

168 

169JoinTransactionMode = Literal[ 

170 "conditional_savepoint", 

171 "rollback_only", 

172 "control_fully", 

173 "create_savepoint", 

174] 

175 

176 

177class _ConnectionCallableProto(Protocol): 

178 """a callable that returns a :class:`.Connection` given an instance. 

179 

180 This callable, when present on a :class:`.Session`, is called only from the 

181 ORM's persistence mechanism (i.e. the unit of work flush process) to allow 

182 for connection-per-instance schemes (i.e. horizontal sharding) to be used 

183 as persistence time. 

184 

185 This callable is not present on a plain :class:`.Session`, however 

186 is established when using the horizontal sharding extension. 

187 

188 """ 

189 

190 def __call__( 

191 self, 

192 mapper: Optional[Mapper[Any]] = None, 

193 instance: Optional[object] = None, 

194 **kw: Any, 

195 ) -> Connection: ... 

196 

197 

198def _state_session(state: InstanceState[Any]) -> Optional[Session]: 

199 """Given an :class:`.InstanceState`, return the :class:`.Session` 

200 associated, if any. 

201 """ 

202 return state.session 

203 

204 

205class _SessionClassMethods: 

206 """Class-level methods for :class:`.Session`, :class:`.sessionmaker`.""" 

207 

208 @classmethod 

209 @util.preload_module("sqlalchemy.orm.util") 

210 def identity_key( 

211 cls, 

212 class_: Optional[Type[Any]] = None, 

213 ident: Union[Any, Tuple[Any, ...]] = None, 

214 *, 

215 instance: Optional[Any] = None, 

216 row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]] = None, 

217 identity_token: Optional[Any] = None, 

218 ) -> _IdentityKeyType[Any]: 

219 """Return an identity key. 

220 

221 This is an alias of :func:`.util.identity_key`. 

222 

223 """ 

224 return util.preloaded.orm_util.identity_key( 

225 class_, 

226 ident, 

227 instance=instance, 

228 row=row, 

229 identity_token=identity_token, 

230 ) 

231 

232 @classmethod 

233 def object_session(cls, instance: object) -> Optional[Session]: 

234 """Return the :class:`.Session` to which an object belongs. 

235 

236 This is an alias of :func:`.object_session`. 

237 

238 """ 

239 

240 return object_session(instance) 

241 

242 

243class SessionTransactionState(_StateChangeState): 

244 ACTIVE = 1 

245 PREPARED = 2 

246 COMMITTED = 3 

247 DEACTIVE = 4 

248 CLOSED = 5 

249 PROVISIONING_CONNECTION = 6 

250 

251 

252# backwards compatibility 

253ACTIVE, PREPARED, COMMITTED, DEACTIVE, CLOSED, PROVISIONING_CONNECTION = tuple( 

254 SessionTransactionState 

255) 

256 

257 

258class ORMExecuteState(util.MemoizedSlots): 

259 """Represents a call to the :meth:`_orm.Session.execute` method, as passed 

260 to the :meth:`.SessionEvents.do_orm_execute` event hook. 

261 

262 .. versionadded:: 1.4 

263 

264 .. seealso:: 

265 

266 :ref:`session_execute_events` - top level documentation on how 

267 to use :meth:`_orm.SessionEvents.do_orm_execute` 

268 

269 """ 

270 

271 __slots__ = ( 

272 "session", 

273 "statement", 

274 "parameters", 

275 "execution_options", 

276 "local_execution_options", 

277 "bind_arguments", 

278 "identity_token", 

279 "_compile_state_cls", 

280 "_starting_event_idx", 

281 "_events_todo", 

282 "_update_execution_options", 

283 ) 

284 

285 session: Session 

286 """The :class:`_orm.Session` in use.""" 

287 

288 statement: Executable 

289 """The SQL statement being invoked. 

290 

291 For an ORM selection as would 

292 be retrieved from :class:`_orm.Query`, this is an instance of 

293 :class:`_sql.select` that was generated from the ORM query. 

294 """ 

295 

296 parameters: Optional[_CoreAnyExecuteParams] 

297 """Optional mapping or list of mappings of parameters that was passed to 

298 :meth:`_orm.Session.execute`. 

299 

300 May be mutated or re-assigned in place, which will take effect as the 

301 effective parameters passed to the method. 

302 

303 .. versionchanged:: 2.1 :attr:`.ORMExecuteState.parameters` may now be 

304 mutated or replaced. 

305 

306 """ 

307 

308 execution_options: _ExecuteOptions 

309 """The complete dictionary of current execution options. 

310 

311 This is a merge of the statement level options with the 

312 locally passed execution options. 

313 

314 .. seealso:: 

315 

316 :attr:`_orm.ORMExecuteState.local_execution_options` 

317 

318 :meth:`_sql.Executable.execution_options` 

319 

320 :ref:`orm_queryguide_execution_options` 

321 

322 """ 

323 

324 local_execution_options: _ExecuteOptions 

325 """Dictionary view of the execution options passed to the 

326 :meth:`.Session.execute` method. 

327 

328 This does not include options that may be associated with the statement 

329 being invoked. 

330 

331 .. seealso:: 

332 

333 :attr:`_orm.ORMExecuteState.execution_options` 

334 

335 """ 

336 

337 bind_arguments: _BindArguments 

338 """The dictionary passed as the 

339 :paramref:`_orm.Session.execute.bind_arguments` dictionary. 

340 

341 This dictionary may be used by extensions to :class:`_orm.Session` to pass 

342 arguments that will assist in determining amongst a set of database 

343 connections which one should be used to invoke this statement. 

344 

345 """ 

346 

347 _compile_state_cls: Optional[Type[_ORMCompileState]] 

348 _starting_event_idx: int 

349 _events_todo: List[Any] 

350 _update_execution_options: _ExecuteOptions 

351 

352 def __init__( 

353 self, 

354 session: Session, 

355 statement: Executable, 

356 parameters: Optional[_CoreAnyExecuteParams], 

357 execution_options: _ExecuteOptions, 

358 bind_arguments: _BindArguments, 

359 compile_state_cls: Optional[Type[_ORMCompileState]], 

360 events_todo: List[_InstanceLevelDispatch[Session]], 

361 ): 

362 """Construct a new :class:`_orm.ORMExecuteState`. 

363 

364 this object is constructed internally. 

365 

366 """ 

367 self.session = session 

368 self.statement = statement 

369 self.parameters = parameters 

370 self.local_execution_options = execution_options 

371 self.execution_options = statement._execution_options.union( 

372 execution_options 

373 ) 

374 self.bind_arguments = bind_arguments 

375 self._compile_state_cls = compile_state_cls 

376 self._events_todo = list(events_todo) 

377 self._update_execution_options = util.EMPTY_DICT 

378 

379 def _remaining_events(self) -> List[_InstanceLevelDispatch[Session]]: 

380 return self._events_todo[self._starting_event_idx + 1 :] 

381 

382 def invoke_statement( 

383 self, 

384 statement: Optional[Executable] = None, 

385 params: Optional[_CoreAnyExecuteParams] = None, 

386 execution_options: Optional[OrmExecuteOptionsParameter] = None, 

387 bind_arguments: Optional[_BindArguments] = None, 

388 ) -> Result[Unpack[TupleAny]]: 

389 """Execute the statement represented by this 

390 :class:`.ORMExecuteState`, without re-invoking events that have 

391 already proceeded. 

392 

393 This method essentially performs a re-entrant execution of the current 

394 statement for which the :meth:`.SessionEvents.do_orm_execute` event is 

395 being currently invoked. The use case for this is for event handlers 

396 that want to override how the ultimate 

397 :class:`_engine.Result` object is returned, such as for schemes that 

398 retrieve results from an offline cache or which concatenate results 

399 from multiple executions. 

400 

401 When the :class:`_engine.Result` object is returned by the actual 

402 handler function within :meth:`_orm.SessionEvents.do_orm_execute` and 

403 is propagated to the calling 

404 :meth:`_orm.Session.execute` method, the remainder of the 

405 :meth:`_orm.Session.execute` method is preempted and the 

406 :class:`_engine.Result` object is returned to the caller of 

407 :meth:`_orm.Session.execute` immediately. 

408 

409 :param statement: optional statement to be invoked, in place of the 

410 statement currently represented by :attr:`.ORMExecuteState.statement`. 

411 

412 :param params: optional dictionary of parameters or list of parameters 

413 which will be merged into the existing 

414 :attr:`.ORMExecuteState.parameters` of this :class:`.ORMExecuteState`. 

415 

416 .. versionchanged:: 2.0 a list of parameter dictionaries is accepted 

417 for executemany executions. 

418 

419 :param execution_options: optional dictionary of execution options 

420 will be merged into the existing 

421 :attr:`.ORMExecuteState.execution_options` of this 

422 :class:`.ORMExecuteState`. 

423 

424 :param bind_arguments: optional dictionary of bind_arguments 

425 which will be merged amongst the current 

426 :attr:`.ORMExecuteState.bind_arguments` 

427 of this :class:`.ORMExecuteState`. 

428 

429 :return: a :class:`_engine.Result` object with ORM-level results. 

430 

431 .. seealso:: 

432 

433 :ref:`do_orm_execute_re_executing` - background and examples on the 

434 appropriate usage of :meth:`_orm.ORMExecuteState.invoke_statement`. 

435 

436 

437 """ 

438 

439 if statement is None: 

440 statement = self.statement 

441 

442 _bind_arguments = dict(self.bind_arguments) 

443 if bind_arguments: 

444 _bind_arguments.update(bind_arguments) 

445 _bind_arguments["_sa_skip_events"] = True 

446 

447 _params: Optional[_CoreAnyExecuteParams] 

448 if params: 

449 if self.is_executemany: 

450 _params = [] 

451 exec_many_parameters = cast( 

452 "List[Dict[str, Any]]", self.parameters 

453 ) 

454 for _existing_params, _new_params in itertools.zip_longest( 

455 exec_many_parameters, 

456 cast("List[Dict[str, Any]]", params), 

457 ): 

458 if _existing_params is None or _new_params is None: 

459 raise sa_exc.InvalidRequestError( 

460 f"Can't apply executemany parameters to " 

461 f"statement; number of parameter sets passed to " 

462 f"Session.execute() ({len(exec_many_parameters)}) " 

463 f"does not match number of parameter sets given " 

464 f"to ORMExecuteState.invoke_statement() " 

465 f"({len(params)})" 

466 ) 

467 _existing_params = dict(_existing_params) 

468 _existing_params.update(_new_params) 

469 _params.append(_existing_params) 

470 else: 

471 _params = dict(cast("Dict[str, Any]", self.parameters)) 

472 _params.update(cast("Dict[str, Any]", params)) 

473 else: 

474 _params = self.parameters 

475 

476 _execution_options = self.local_execution_options 

477 if execution_options: 

478 _execution_options = _execution_options.union(execution_options) 

479 

480 return self.session._execute_internal( 

481 statement, 

482 _params, 

483 execution_options=_execution_options, 

484 bind_arguments=_bind_arguments, 

485 _parent_execute_state=self, 

486 ) 

487 

488 @property 

489 def bind_mapper(self) -> Optional[Mapper[Any]]: 

490 """Return the :class:`_orm.Mapper` that is the primary "bind" mapper. 

491 

492 For an :class:`_orm.ORMExecuteState` object invoking an ORM 

493 statement, that is, the :attr:`_orm.ORMExecuteState.is_orm_statement` 

494 attribute is ``True``, this attribute will return the 

495 :class:`_orm.Mapper` that is considered to be the "primary" mapper 

496 of the statement. The term "bind mapper" refers to the fact that 

497 a :class:`_orm.Session` object may be "bound" to multiple 

498 :class:`_engine.Engine` objects keyed to mapped classes, and the 

499 "bind mapper" determines which of those :class:`_engine.Engine` objects 

500 would be selected. 

501 

502 For a statement that is invoked against a single mapped class, 

503 :attr:`_orm.ORMExecuteState.bind_mapper` is intended to be a reliable 

504 way of getting this mapper. 

505 

506 .. versionadded:: 1.4.0b2 

507 

508 .. seealso:: 

509 

510 :attr:`_orm.ORMExecuteState.all_mappers` 

511 

512 

513 """ 

514 mp: Optional[Mapper[Any]] = self.bind_arguments.get("mapper", None) 

515 return mp 

516 

517 @property 

518 def all_mappers(self) -> Sequence[Mapper[Any]]: 

519 """Return a sequence of all :class:`_orm.Mapper` objects that are 

520 involved at the top level of this statement. 

521 

522 By "top level" we mean those :class:`_orm.Mapper` objects that would 

523 be represented in the result set rows for a :func:`_sql.select` 

524 query, or for a :func:`_dml.update` or :func:`_dml.delete` query, 

525 the mapper that is the main subject of the UPDATE or DELETE. 

526 

527 .. versionadded:: 1.4.0b2 

528 

529 .. seealso:: 

530 

531 :attr:`_orm.ORMExecuteState.bind_mapper` 

532 

533 

534 

535 """ 

536 if not self.is_orm_statement: 

537 return [] 

538 elif isinstance(self.statement, (Select, FromStatement)): 

539 result = [] 

540 seen = set() 

541 for d in self.statement.column_descriptions: 

542 ent = d["entity"] 

543 if ent: 

544 insp = inspect(ent, raiseerr=False) 

545 if insp and insp.mapper and insp.mapper not in seen: 

546 seen.add(insp.mapper) 

547 result.append(insp.mapper) 

548 return result 

549 elif self.statement.is_dml and self.bind_mapper: 

550 return [self.bind_mapper] 

551 else: 

552 return [] 

553 

554 @property 

555 def is_orm_statement(self) -> bool: 

556 """return True if the operation is an ORM statement. 

557 

558 This indicates that the select(), insert(), update(), or delete() 

559 being invoked contains ORM entities as subjects. For a statement 

560 that does not have ORM entities and instead refers only to 

561 :class:`.Table` metadata, it is invoked as a Core SQL statement 

562 and no ORM-level automation takes place. 

563 

564 """ 

565 return self._compile_state_cls is not None 

566 

567 @property 

568 def is_executemany(self) -> bool: 

569 """return True if the parameters are a multi-element list of 

570 dictionaries with more than one dictionary. 

571 

572 .. versionadded:: 2.0 

573 

574 """ 

575 return isinstance(self.parameters, list) 

576 

577 @property 

578 def is_select(self) -> bool: 

579 """return True if this is a SELECT operation. 

580 

581 .. versionchanged:: 2.0.30 - the attribute is also True for a 

582 :meth:`_sql.Select.from_statement` construct that is itself against 

583 a :class:`_sql.Select` construct, such as 

584 ``select(Entity).from_statement(select(..))`` 

585 

586 """ 

587 return self.statement.is_select 

588 

589 @property 

590 def is_from_statement(self) -> bool: 

591 """return True if this operation is a 

592 :meth:`_sql.Select.from_statement` operation. 

593 

594 This is independent from :attr:`_orm.ORMExecuteState.is_select`, as a 

595 ``select().from_statement()`` construct can be used with 

596 INSERT/UPDATE/DELETE RETURNING types of statements as well. 

597 :attr:`_orm.ORMExecuteState.is_select` will only be set if the 

598 :meth:`_sql.Select.from_statement` is itself against a 

599 :class:`_sql.Select` construct. 

600 

601 .. versionadded:: 2.0.30 

602 

603 """ 

604 return self.statement.is_from_statement 

605 

606 @property 

607 def is_insert(self) -> bool: 

608 """return True if this is an INSERT operation. 

609 

610 .. versionchanged:: 2.0.30 - the attribute is also True for a 

611 :meth:`_sql.Select.from_statement` construct that is itself against 

612 a :class:`_sql.Insert` construct, such as 

613 ``select(Entity).from_statement(insert(..))`` 

614 

615 """ 

616 return self.statement.is_dml and self.statement.is_insert 

617 

618 @property 

619 def is_update(self) -> bool: 

620 """return True if this is an UPDATE operation. 

621 

622 .. versionchanged:: 2.0.30 - the attribute is also True for a 

623 :meth:`_sql.Select.from_statement` construct that is itself against 

624 a :class:`_sql.Update` construct, such as 

625 ``select(Entity).from_statement(update(..))`` 

626 

627 """ 

628 return self.statement.is_dml and self.statement.is_update 

629 

630 @property 

631 def is_delete(self) -> bool: 

632 """return True if this is a DELETE operation. 

633 

634 .. versionchanged:: 2.0.30 - the attribute is also True for a 

635 :meth:`_sql.Select.from_statement` construct that is itself against 

636 a :class:`_sql.Delete` construct, such as 

637 ``select(Entity).from_statement(delete(..))`` 

638 

639 """ 

640 return self.statement.is_dml and self.statement.is_delete 

641 

642 @property 

643 def _is_crud(self) -> bool: 

644 return isinstance(self.statement, (dml.Update, dml.Delete)) 

645 

646 def update_execution_options(self, **opts: Any) -> None: 

647 """Update the local execution options with new values.""" 

648 self.local_execution_options = self.local_execution_options.union(opts) 

649 self._update_execution_options = self._update_execution_options.union( 

650 opts 

651 ) 

652 

653 def _orm_compile_options( 

654 self, 

655 ) -> Optional[ 

656 Union[ 

657 context._ORMCompileState.default_compile_options, 

658 Type[context._ORMCompileState.default_compile_options], 

659 ] 

660 ]: 

661 if not self.is_select: 

662 return None 

663 try: 

664 opts = self.statement._compile_options 

665 except AttributeError: 

666 return None 

667 

668 if opts is not None and opts.isinstance( 

669 context._ORMCompileState.default_compile_options 

670 ): 

671 return opts # type: ignore[return-value] 

672 else: 

673 return None 

674 

675 @property 

676 def lazy_loaded_from(self) -> Optional[InstanceState[Any]]: 

677 """An :class:`.InstanceState` that is using this statement execution 

678 for a lazy load operation. 

679 

680 The primary rationale for this attribute is to support the horizontal 

681 sharding extension, where it is available within specific query 

682 execution time hooks created by this extension. To that end, the 

683 attribute is only intended to be meaningful at **query execution 

684 time**, and importantly not any time prior to that, including query 

685 compilation time. 

686 

687 """ 

688 return self.load_options._lazy_loaded_from 

689 

690 @property 

691 def loader_strategy_path(self) -> Optional[PathRegistry]: 

692 """Return the :class:`.PathRegistry` for the current load path. 

693 

694 This object represents the "path" in a query along relationships 

695 when a particular object or collection is being loaded. 

696 

697 """ 

698 opts = self._orm_compile_options() 

699 if opts is not None: 

700 return opts._current_path 

701 else: 

702 return None 

703 

704 @property 

705 def is_column_load(self) -> bool: 

706 """Return True if the operation is refreshing column-oriented 

707 attributes on an existing ORM object. 

708 

709 This occurs during operations such as :meth:`_orm.Session.refresh`, 

710 as well as when an attribute deferred by :func:`_orm.defer` is 

711 being loaded, or an attribute that was expired either directly 

712 by :meth:`_orm.Session.expire` or via a commit operation is being 

713 loaded. 

714 

715 Handlers will very likely not want to add any options to queries 

716 when such an operation is occurring as the query should be a straight 

717 primary key fetch which should not have any additional WHERE criteria, 

718 and loader options travelling with the instance 

719 will have already been added to the query. 

720 

721 .. versionadded:: 1.4.0b2 

722 

723 .. seealso:: 

724 

725 :attr:`_orm.ORMExecuteState.is_relationship_load` 

726 

727 """ 

728 opts = self._orm_compile_options() 

729 return opts is not None and opts._for_refresh_state 

730 

731 @property 

732 def is_relationship_load(self) -> bool: 

733 """Return True if this load is loading objects on behalf of a 

734 relationship. 

735 

736 This means, the loader in effect is either a LazyLoader, 

737 SelectInLoader, SubqueryLoader, or similar, and the entire 

738 SELECT statement being emitted is on behalf of a relationship 

739 load. 

740 

741 Handlers will very likely not want to add any options to queries 

742 when such an operation is occurring, as loader options are already 

743 capable of being propagated to relationship loaders and should 

744 be already present. 

745 

746 .. seealso:: 

747 

748 :attr:`_orm.ORMExecuteState.is_column_load` 

749 

750 """ 

751 opts = self._orm_compile_options() 

752 if opts is None: 

753 return False 

754 path = self.loader_strategy_path 

755 return path is not None and not path.is_root 

756 

757 @property 

758 def load_options( 

759 self, 

760 ) -> Union[ 

761 context.QueryContext.default_load_options, 

762 Type[context.QueryContext.default_load_options], 

763 ]: 

764 """Return the load_options that will be used for this execution.""" 

765 

766 if not self.is_select: 

767 raise sa_exc.InvalidRequestError( 

768 "This ORM execution is not against a SELECT statement " 

769 "so there are no load options." 

770 ) 

771 

772 lo: Union[ 

773 context.QueryContext.default_load_options, 

774 Type[context.QueryContext.default_load_options], 

775 ] = self.execution_options.get( 

776 "_sa_orm_load_options", context.QueryContext.default_load_options 

777 ) 

778 return lo 

779 

780 @property 

781 def update_delete_options( 

782 self, 

783 ) -> Union[ 

784 bulk_persistence._BulkUDCompileState.default_update_options, 

785 Type[bulk_persistence._BulkUDCompileState.default_update_options], 

786 ]: 

787 """Return the update_delete_options that will be used for this 

788 execution.""" 

789 

790 if not self._is_crud: 

791 raise sa_exc.InvalidRequestError( 

792 "This ORM execution is not against an UPDATE or DELETE " 

793 "statement so there are no update options." 

794 ) 

795 uo: Union[ 

796 bulk_persistence._BulkUDCompileState.default_update_options, 

797 Type[bulk_persistence._BulkUDCompileState.default_update_options], 

798 ] = self.execution_options.get( 

799 "_sa_orm_update_options", 

800 bulk_persistence._BulkUDCompileState.default_update_options, 

801 ) 

802 return uo 

803 

804 @property 

805 def _non_compile_orm_options(self) -> Sequence[ORMOption]: 

806 return [ 

807 opt 

808 for opt in self.statement._with_options 

809 if is_orm_option(opt) and not opt._is_compile_state 

810 ] 

811 

812 @property 

813 def user_defined_options(self) -> Sequence[UserDefinedOption]: 

814 """The sequence of :class:`.UserDefinedOptions` that have been 

815 associated with the statement being invoked. 

816 

817 .. versionchanged:: 2.1 - the returned option take into 

818 consideration any options added before calling 

819 :meth:`_sql.Select.with_only_columns` or 

820 :meth:`_orm.Query.with_entities`. 

821 

822 """ 

823 items = [ 

824 self.statement, 

825 *getattr(self.statement, "_memoized_select_entities", ()), 

826 ] 

827 return [ 

828 opt 

829 for item in items 

830 for opt in item._with_options 

831 if is_user_defined_option(opt) 

832 ] 

833 

834 

835class SessionTransactionOrigin(Enum): 

836 """indicates the origin of a :class:`.SessionTransaction`. 

837 

838 This enumeration is present on the 

839 :attr:`.SessionTransaction.origin` attribute of any 

840 :class:`.SessionTransaction` object. 

841 

842 .. versionadded:: 2.0 

843 

844 """ 

845 

846 AUTOBEGIN = 0 

847 """transaction were started by autobegin""" 

848 

849 BEGIN = 1 

850 """transaction were started by calling :meth:`_orm.Session.begin`""" 

851 

852 BEGIN_NESTED = 2 

853 """transaction were started by :meth:`_orm.Session.begin_nested`""" 

854 

855 SUBTRANSACTION = 3 

856 """transaction is an internal "subtransaction" """ 

857 

858 

859class SessionTransaction(_StateChange, TransactionalContext): 

860 """A :class:`.Session`-level transaction. 

861 

862 :class:`.SessionTransaction` is produced from the 

863 :meth:`_orm.Session.begin` 

864 and :meth:`_orm.Session.begin_nested` methods. It's largely an internal 

865 object that in modern use provides a context manager for session 

866 transactions. 

867 

868 Documentation on interacting with :class:`_orm.SessionTransaction` is 

869 at: :ref:`unitofwork_transaction`. 

870 

871 

872 .. versionchanged:: 1.4 The scoping and API methods to work with the 

873 :class:`_orm.SessionTransaction` object directly have been simplified. 

874 

875 .. seealso:: 

876 

877 :ref:`unitofwork_transaction` 

878 

879 :meth:`.Session.begin` 

880 

881 :meth:`.Session.begin_nested` 

882 

883 :meth:`.Session.rollback` 

884 

885 :meth:`.Session.commit` 

886 

887 :meth:`.Session.in_transaction` 

888 

889 :meth:`.Session.in_nested_transaction` 

890 

891 :meth:`.Session.get_transaction` 

892 

893 :meth:`.Session.get_nested_transaction` 

894 

895 

896 """ 

897 

898 _rollback_exception: Optional[BaseException] = None 

899 

900 _connections: Dict[ 

901 Union[Engine, Connection], Tuple[Connection, Transaction, bool, bool] 

902 ] 

903 session: Session 

904 _parent: Optional[SessionTransaction] 

905 

906 _state: SessionTransactionState 

907 

908 _new: weakref.WeakKeyDictionary[InstanceState[Any], object] 

909 _deleted: weakref.WeakKeyDictionary[InstanceState[Any], object] 

910 _dirty: weakref.WeakKeyDictionary[InstanceState[Any], object] 

911 _key_switches: weakref.WeakKeyDictionary[ 

912 InstanceState[Any], Tuple[Any, Any] 

913 ] 

914 

915 origin: SessionTransactionOrigin 

916 """Origin of this :class:`_orm.SessionTransaction`. 

917 

918 Refers to a :class:`.SessionTransactionOrigin` instance which is an 

919 enumeration indicating the source event that led to constructing 

920 this :class:`_orm.SessionTransaction`. 

921 

922 .. versionadded:: 2.0 

923 

924 """ 

925 

926 nested: bool = False 

927 """Indicates if this is a nested, or SAVEPOINT, transaction. 

928 

929 When :attr:`.SessionTransaction.nested` is True, it is expected 

930 that :attr:`.SessionTransaction.parent` will be present as well, 

931 linking to the enclosing :class:`.SessionTransaction`. 

932 

933 .. seealso:: 

934 

935 :attr:`.SessionTransaction.origin` 

936 

937 """ 

938 

939 def __init__( 

940 self, 

941 session: Session, 

942 origin: SessionTransactionOrigin, 

943 parent: Optional[SessionTransaction] = None, 

944 ): 

945 TransactionalContext._trans_ctx_check(session) 

946 

947 self.session = session 

948 self._connections = {} 

949 self._parent = parent 

950 self.nested = nested = origin is SessionTransactionOrigin.BEGIN_NESTED 

951 self.origin = origin 

952 

953 if session._close_state is _SessionCloseState.CLOSED: 

954 raise sa_exc.InvalidRequestError( 

955 "This Session has been permanently closed and is unable " 

956 "to handle any more transaction requests." 

957 ) 

958 

959 if nested: 

960 if not parent: 

961 raise sa_exc.InvalidRequestError( 

962 "Can't start a SAVEPOINT transaction when no existing " 

963 "transaction is in progress" 

964 ) 

965 

966 self._previous_nested_transaction = session._nested_transaction 

967 elif origin is SessionTransactionOrigin.SUBTRANSACTION: 

968 assert parent is not None 

969 else: 

970 assert parent is None 

971 

972 self._state = SessionTransactionState.ACTIVE 

973 

974 self._take_snapshot() 

975 

976 # make sure transaction is assigned before we call the 

977 # dispatch 

978 self.session._transaction = self 

979 

980 self.session.dispatch.after_transaction_create(self.session, self) 

981 

982 def _raise_for_prerequisite_state( 

983 self, operation_name: str, state: _StateChangeState 

984 ) -> NoReturn: 

985 if state is SessionTransactionState.DEACTIVE: 

986 if self._rollback_exception: 

987 raise sa_exc.PendingRollbackError( 

988 "This Session's transaction has been rolled back " 

989 "due to a previous exception during flush." 

990 " To begin a new transaction with this Session, " 

991 "first issue Session.rollback()." 

992 f" Original exception was: {self._rollback_exception}", 

993 code="7s2a", 

994 ) 

995 else: 

996 raise sa_exc.InvalidRequestError( 

997 "This session is in 'inactive' state, due to the " 

998 "SQL transaction being rolled back; no further SQL " 

999 "can be emitted within this transaction." 

1000 ) 

1001 elif state is SessionTransactionState.CLOSED: 

1002 raise sa_exc.ResourceClosedError("This transaction is closed") 

1003 elif state is SessionTransactionState.PROVISIONING_CONNECTION: 

1004 raise sa_exc.InvalidRequestError( 

1005 "This session is provisioning a new connection; concurrent " 

1006 "operations are not permitted", 

1007 code="isce", 

1008 ) 

1009 else: 

1010 raise sa_exc.InvalidRequestError( 

1011 f"This session is in '{state.name.lower()}' state; no " 

1012 "further SQL can be emitted within this transaction." 

1013 ) 

1014 

1015 @property 

1016 def parent(self) -> Optional[SessionTransaction]: 

1017 """The parent :class:`.SessionTransaction` of this 

1018 :class:`.SessionTransaction`. 

1019 

1020 If this attribute is ``None``, indicates this 

1021 :class:`.SessionTransaction` is at the top of the stack, and 

1022 corresponds to a real "COMMIT"/"ROLLBACK" 

1023 block. If non-``None``, then this is either a "subtransaction" 

1024 (an internal marker object used by the flush process) or a 

1025 "nested" / SAVEPOINT transaction. If the 

1026 :attr:`.SessionTransaction.nested` attribute is ``True``, then 

1027 this is a SAVEPOINT, and if ``False``, indicates this a subtransaction. 

1028 

1029 """ 

1030 return self._parent 

1031 

1032 @property 

1033 def is_active(self) -> bool: 

1034 return ( 

1035 self.session is not None 

1036 and self._state is SessionTransactionState.ACTIVE 

1037 ) 

1038 

1039 @property 

1040 def _is_transaction_boundary(self) -> bool: 

1041 return self.nested or not self._parent 

1042 

1043 @_StateChange.declare_states( 

1044 (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 

1045 ) 

1046 def connection( 

1047 self, 

1048 bindkey: Optional[Mapper[Any]], 

1049 execution_options: Optional[_ExecuteOptions] = None, 

1050 **kwargs: Any, 

1051 ) -> Connection: 

1052 bind = self.session.get_bind(bindkey, **kwargs) 

1053 return self._connection_for_bind(bind, execution_options) 

1054 

1055 @_StateChange.declare_states( 

1056 (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 

1057 ) 

1058 def _begin(self, nested: bool = False) -> SessionTransaction: 

1059 return SessionTransaction( 

1060 self.session, 

1061 ( 

1062 SessionTransactionOrigin.BEGIN_NESTED 

1063 if nested 

1064 else SessionTransactionOrigin.SUBTRANSACTION 

1065 ), 

1066 self, 

1067 ) 

1068 

1069 def _iterate_self_and_parents( 

1070 self, upto: Optional[SessionTransaction] = None 

1071 ) -> Iterable[SessionTransaction]: 

1072 current = self 

1073 result: Tuple[SessionTransaction, ...] = () 

1074 while current: 

1075 result += (current,) 

1076 if current._parent is upto: 

1077 break 

1078 elif current._parent is None: 

1079 raise sa_exc.InvalidRequestError( 

1080 "Transaction %s is not on the active transaction list" 

1081 % (upto) 

1082 ) 

1083 else: 

1084 current = current._parent 

1085 

1086 return result 

1087 

1088 def _take_snapshot(self) -> None: 

1089 if not self._is_transaction_boundary: 

1090 parent = self._parent 

1091 assert parent is not None 

1092 self._new = parent._new 

1093 self._deleted = parent._deleted 

1094 self._dirty = parent._dirty 

1095 self._key_switches = parent._key_switches 

1096 return 

1097 

1098 is_begin = self.origin in ( 

1099 SessionTransactionOrigin.BEGIN, 

1100 SessionTransactionOrigin.AUTOBEGIN, 

1101 ) 

1102 if not is_begin and not self.session._flushing: 

1103 self.session.flush() 

1104 

1105 self._new = weakref.WeakKeyDictionary() 

1106 self._deleted = weakref.WeakKeyDictionary() 

1107 self._dirty = weakref.WeakKeyDictionary() 

1108 self._key_switches = weakref.WeakKeyDictionary() 

1109 

1110 def _restore_snapshot(self, dirty_only: bool = False) -> None: 

1111 """Restore the restoration state taken before a transaction began. 

1112 

1113 Corresponds to a rollback. 

1114 

1115 """ 

1116 assert self._is_transaction_boundary 

1117 

1118 to_expunge = set(self._new).union(self.session._new) 

1119 self.session._expunge_states(to_expunge, to_transient=True) 

1120 

1121 for s, (oldkey, newkey) in self._key_switches.items(): 

1122 # we probably can do this conditionally based on 

1123 # if we expunged or not, but safe_discard does that anyway 

1124 self.session.identity_map.safe_discard(s) 

1125 

1126 # restore the old key 

1127 s.key = oldkey 

1128 

1129 # now restore the object, but only if we didn't expunge 

1130 if s not in to_expunge: 

1131 self.session.identity_map.replace(s) 

1132 

1133 for s in set(self._deleted).union(self.session._deleted): 

1134 self.session._update_impl(s, revert_deletion=True) 

1135 

1136 assert not self.session._deleted 

1137 

1138 for s in self.session.identity_map.all_states(): 

1139 if not dirty_only or s.modified or s in self._dirty: 

1140 s._expire(s.dict, self.session.identity_map._modified) 

1141 

1142 def _remove_snapshot(self) -> None: 

1143 """Remove the restoration state taken before a transaction began. 

1144 

1145 Corresponds to a commit. 

1146 

1147 """ 

1148 assert self._is_transaction_boundary 

1149 

1150 if not self.nested and self.session.expire_on_commit: 

1151 for s in self.session.identity_map.all_states(): 

1152 s._expire(s.dict, self.session.identity_map._modified) 

1153 

1154 statelib.InstanceState._detach_states( 

1155 list(self._deleted), self.session 

1156 ) 

1157 self._deleted.clear() 

1158 elif self.nested: 

1159 parent = self._parent 

1160 assert parent is not None 

1161 parent._new.update(self._new) 

1162 parent._dirty.update(self._dirty) 

1163 parent._deleted.update(self._deleted) 

1164 parent._key_switches.update(self._key_switches) 

1165 

1166 @_StateChange.declare_states( 

1167 (SessionTransactionState.ACTIVE,), _StateChangeStates.NO_CHANGE 

1168 ) 

1169 def _connection_for_bind( 

1170 self, 

1171 bind: _SessionBind, 

1172 execution_options: Optional[CoreExecuteOptionsParameter], 

1173 ) -> Connection: 

1174 if bind in self._connections: 

1175 if execution_options: 

1176 util.warn( 

1177 "Connection is already established for the " 

1178 "given bind; execution_options ignored" 

1179 ) 

1180 return self._connections[bind][0] 

1181 

1182 self._state = SessionTransactionState.PROVISIONING_CONNECTION 

1183 

1184 local_connect = False 

1185 should_commit = True 

1186 

1187 try: 

1188 if self._parent: 

1189 conn = self._parent._connection_for_bind( 

1190 bind, execution_options 

1191 ) 

1192 if not self.nested: 

1193 return conn 

1194 else: 

1195 if isinstance(bind, engine.Connection): 

1196 conn = bind 

1197 if conn.engine in self._connections: 

1198 raise sa_exc.InvalidRequestError( 

1199 "Session already has a Connection associated " 

1200 "for the given Connection's Engine" 

1201 ) 

1202 else: 

1203 conn = bind.connect() 

1204 local_connect = True 

1205 

1206 try: 

1207 conn_exec_opts: Dict[str, Any] = {} 

1208 if self.session.execution_options: 

1209 conn_exec_opts.update(self.session.execution_options) 

1210 if execution_options: 

1211 conn_exec_opts.update(execution_options) 

1212 if conn_exec_opts: 

1213 conn = conn.execution_options(**conn_exec_opts) 

1214 

1215 transaction: Transaction 

1216 if self.session.twophase and self._parent is None: 

1217 # TODO: shouldn't we only be here if not 

1218 # conn.in_transaction() ? 

1219 # if twophase is set and conn.in_transaction(), validate 

1220 # that it is in fact twophase. 

1221 transaction = conn.begin_twophase() 

1222 elif self.nested: 

1223 transaction = conn.begin_nested() 

1224 elif conn.in_transaction(): 

1225 

1226 if local_connect: 

1227 _trans = conn.get_transaction() 

1228 assert _trans is not None 

1229 transaction = _trans 

1230 else: 

1231 join_transaction_mode = ( 

1232 self.session.join_transaction_mode 

1233 ) 

1234 

1235 if join_transaction_mode == "conditional_savepoint": 

1236 if conn.in_nested_transaction(): 

1237 join_transaction_mode = "create_savepoint" 

1238 else: 

1239 join_transaction_mode = "rollback_only" 

1240 

1241 if join_transaction_mode in ( 

1242 "control_fully", 

1243 "rollback_only", 

1244 ): 

1245 if conn.in_nested_transaction(): 

1246 transaction = ( 

1247 conn._get_required_nested_transaction() 

1248 ) 

1249 else: 

1250 transaction = conn._get_required_transaction() 

1251 if join_transaction_mode == "rollback_only": 

1252 should_commit = False 

1253 elif join_transaction_mode == "create_savepoint": 

1254 transaction = conn.begin_nested() 

1255 else: 

1256 assert False, join_transaction_mode 

1257 else: 

1258 transaction = conn.begin() 

1259 except: 

1260 # connection will not not be associated with this Session; 

1261 # close it immediately so that it isn't closed under GC 

1262 if local_connect: 

1263 conn.close() 

1264 raise 

1265 else: 

1266 bind_is_connection = isinstance(bind, engine.Connection) 

1267 

1268 self._connections[conn] = self._connections[conn.engine] = ( 

1269 conn, 

1270 transaction, 

1271 should_commit, 

1272 not bind_is_connection, 

1273 ) 

1274 self.session.dispatch.after_begin(self.session, self, conn) 

1275 return conn 

1276 finally: 

1277 self._state = SessionTransactionState.ACTIVE 

1278 

1279 def prepare(self) -> None: 

1280 if self._parent is not None or not self.session.twophase: 

1281 raise sa_exc.InvalidRequestError( 

1282 "'twophase' mode not enabled, or not root transaction; " 

1283 "can't prepare." 

1284 ) 

1285 self._prepare_impl() 

1286 

1287 @_StateChange.declare_states( 

1288 (SessionTransactionState.ACTIVE,), SessionTransactionState.PREPARED 

1289 ) 

1290 def _prepare_impl(self) -> None: 

1291 if self._parent is None or self.nested: 

1292 self.session.dispatch.before_commit(self.session) 

1293 

1294 stx = self.session._transaction 

1295 assert stx is not None 

1296 if stx is not self: 

1297 for subtransaction in stx._iterate_self_and_parents(upto=self): 

1298 subtransaction.commit() 

1299 

1300 if not self.session._flushing: 

1301 for _flush_guard in range(100): 

1302 if self.session._is_clean(): 

1303 break 

1304 self.session.flush() 

1305 else: 

1306 raise exc.FlushError( 

1307 "Over 100 subsequent flushes have occurred within " 

1308 "session.commit() - is an after_flush() hook " 

1309 "creating new objects?" 

1310 ) 

1311 

1312 if self._parent is None and self.session.twophase: 

1313 try: 

1314 for t in set(self._connections.values()): 

1315 cast("TwoPhaseTransaction", t[1]).prepare() 

1316 except: 

1317 with util.safe_reraise(): 

1318 with self._expect_state(SessionTransactionState.CLOSED): 

1319 self.rollback() 

1320 

1321 self._state = SessionTransactionState.PREPARED 

1322 

1323 @_StateChange.declare_states( 

1324 (SessionTransactionState.ACTIVE, SessionTransactionState.PREPARED), 

1325 SessionTransactionState.CLOSED, 

1326 ) 

1327 def commit(self, _to_root: bool = False) -> None: 

1328 if self._state is not SessionTransactionState.PREPARED: 

1329 with self._expect_state(SessionTransactionState.PREPARED): 

1330 self._prepare_impl() 

1331 

1332 if self._parent is None or self.nested: 

1333 for conn, trans, should_commit, autoclose in set( 

1334 self._connections.values() 

1335 ): 

1336 if should_commit: 

1337 trans.commit() 

1338 

1339 self._state = SessionTransactionState.COMMITTED 

1340 self.session.dispatch.after_commit(self.session) 

1341 

1342 self._remove_snapshot() 

1343 

1344 with self._expect_state(SessionTransactionState.CLOSED): 

1345 self.close() 

1346 

1347 if _to_root and self._parent: 

1348 self._parent.commit(_to_root=True) 

1349 

1350 @_StateChange.declare_states( 

1351 ( 

1352 SessionTransactionState.ACTIVE, 

1353 SessionTransactionState.DEACTIVE, 

1354 SessionTransactionState.PREPARED, 

1355 ), 

1356 SessionTransactionState.CLOSED, 

1357 ) 

1358 def rollback( 

1359 self, _capture_exception: bool = False, _to_root: bool = False 

1360 ) -> None: 

1361 stx = self.session._transaction 

1362 assert stx is not None 

1363 if stx is not self: 

1364 for subtransaction in stx._iterate_self_and_parents(upto=self): 

1365 subtransaction.close() 

1366 

1367 boundary = self 

1368 rollback_err = None 

1369 if self._state in ( 

1370 SessionTransactionState.ACTIVE, 

1371 SessionTransactionState.PREPARED, 

1372 ): 

1373 for transaction in self._iterate_self_and_parents(): 

1374 if transaction._parent is None or transaction.nested: 

1375 try: 

1376 for t in set(transaction._connections.values()): 

1377 t[1].rollback() 

1378 

1379 transaction._state = SessionTransactionState.DEACTIVE 

1380 self.session.dispatch.after_rollback(self.session) 

1381 except: 

1382 rollback_err = sys.exc_info() 

1383 finally: 

1384 transaction._state = SessionTransactionState.DEACTIVE 

1385 transaction._restore_snapshot( 

1386 dirty_only=transaction.nested 

1387 ) 

1388 boundary = transaction 

1389 break 

1390 else: 

1391 transaction._state = SessionTransactionState.DEACTIVE 

1392 

1393 sess = self.session 

1394 

1395 if not rollback_err and not sess._is_clean(): 

1396 # if items were added, deleted, or mutated 

1397 # here, we need to re-restore the snapshot 

1398 util.warn( 

1399 "Session's state has been changed on " 

1400 "a non-active transaction - this state " 

1401 "will be discarded." 

1402 ) 

1403 boundary._restore_snapshot(dirty_only=boundary.nested) 

1404 

1405 with self._expect_state(SessionTransactionState.CLOSED): 

1406 self.close() 

1407 

1408 if self._parent and _capture_exception: 

1409 self._parent._rollback_exception = sys.exc_info()[1] 

1410 

1411 if rollback_err and rollback_err[1]: 

1412 raise rollback_err[1].with_traceback(rollback_err[2]) 

1413 

1414 sess.dispatch.after_soft_rollback(sess, self) 

1415 

1416 if _to_root and self._parent: 

1417 self._parent.rollback(_to_root=True) 

1418 

1419 @_StateChange.declare_states( 

1420 _StateChangeStates.ANY, SessionTransactionState.CLOSED 

1421 ) 

1422 def close(self, invalidate: bool = False) -> None: 

1423 if self.nested: 

1424 self.session._nested_transaction = ( 

1425 self._previous_nested_transaction 

1426 ) 

1427 

1428 self.session._transaction = self._parent 

1429 

1430 for connection, transaction, should_commit, autoclose in set( 

1431 self._connections.values() 

1432 ): 

1433 if invalidate and self._parent is None: 

1434 connection.invalidate() 

1435 if should_commit and transaction.is_active: 

1436 transaction.close() 

1437 if autoclose and self._parent is None: 

1438 connection.close() 

1439 

1440 self._state = SessionTransactionState.CLOSED 

1441 sess = self.session 

1442 

1443 # TODO: these two None sets were historically after the 

1444 # event hook below, and in 2.0 I changed it this way for some reason, 

1445 # and I remember there being a reason, but not what it was. 

1446 # Why do we need to get rid of them at all? test_memusage::CycleTest 

1447 # passes with these commented out. 

1448 # self.session = None # type: ignore 

1449 # self._connections = None # type: ignore 

1450 

1451 sess.dispatch.after_transaction_end(sess, self) 

1452 

1453 def _get_subject(self) -> Session: 

1454 return self.session 

1455 

1456 def _transaction_is_active(self) -> bool: 

1457 return self._state is SessionTransactionState.ACTIVE 

1458 

1459 def _transaction_is_closed(self) -> bool: 

1460 return self._state is SessionTransactionState.CLOSED 

1461 

1462 def _rollback_can_be_called(self) -> bool: 

1463 return self._state not in (COMMITTED, CLOSED) 

1464 

1465 

1466class _SessionCloseState(Enum): 

1467 ACTIVE = 1 

1468 CLOSED = 2 

1469 CLOSE_IS_RESET = 3 

1470 

1471 

1472class Session(_SessionClassMethods, EventTarget): 

1473 """Manages persistence operations for ORM-mapped objects. 

1474 

1475 The :class:`_orm.Session` is **not safe for use in concurrent threads.**. 

1476 See :ref:`session_faq_threadsafe` for background. 

1477 

1478 The Session's usage paradigm is described at :doc:`/orm/session`. 

1479 

1480 

1481 """ 

1482 

1483 _is_asyncio = False 

1484 

1485 dispatch: dispatcher[Session] 

1486 

1487 identity_map: IdentityMap 

1488 """A mapping of object identities to objects themselves. 

1489 

1490 Iterating through ``Session.identity_map.values()`` provides 

1491 access to the full set of persistent objects (i.e., those 

1492 that have row identity) currently in the session. 

1493 

1494 .. seealso:: 

1495 

1496 :func:`.identity_key` - helper function to produce the keys used 

1497 in this dictionary. 

1498 

1499 """ 

1500 

1501 binds: Mapping[_SessionBindKey, _SessionBind] 

1502 """An immutable mapping of bind targets to :class:`_engine.Engine` or 

1503 :class:`_engine.Connection` objects. 

1504 

1505 This collection is established from the 

1506 :paramref:`_orm.Session.binds` parameter as well as the 

1507 :meth:`_orm.Session.bind_mapper` and :meth:`_orm.Session.bind_table` 

1508 methods, and is consulted by :meth:`_orm.Session.get_bind`. The keys are 

1509 normalized from what was originally passed; a mapper or mapped class is 

1510 entered both under the mapped class and under each of its selectables. 

1511 

1512 The collection is replaced, rather than mutated, whenever a new bind is 

1513 added, so a reference to it will not observe subsequent changes. 

1514 

1515 .. versionadded:: 2.1 Previously this collection was stored 

1516 privately. 

1517 

1518 """ 

1519 

1520 _new: Dict[InstanceState[Any], Any] 

1521 _deleted: Dict[InstanceState[Any], Any] 

1522 bind: Optional[Union[Engine, Connection]] 

1523 _flushing: bool 

1524 _warn_on_events: bool 

1525 _transaction: Optional[SessionTransaction] 

1526 _nested_transaction: Optional[SessionTransaction] 

1527 hash_key: int 

1528 autoflush: bool 

1529 expire_on_commit: bool 

1530 enable_baked_queries: bool 

1531 twophase: bool 

1532 join_transaction_mode: JoinTransactionMode 

1533 execution_options: _ExecuteOptions = util.EMPTY_DICT 

1534 _query_cls: Type[Query[Any]] 

1535 _close_state: _SessionCloseState 

1536 

1537 def __init__( 

1538 self, 

1539 bind: Optional[_SessionBind] = None, 

1540 *, 

1541 autoflush: bool = True, 

1542 future: Literal[True] = True, 

1543 expire_on_commit: bool = True, 

1544 autobegin: bool = True, 

1545 twophase: bool = False, 

1546 binds: Optional[Dict[_SessionBindKey, _SessionBind]] = None, 

1547 enable_baked_queries: bool = True, 

1548 info: Optional[_InfoType] = None, 

1549 query_cls: Optional[Type[Query[Any]]] = None, 

1550 autocommit: Literal[False] = False, 

1551 join_transaction_mode: JoinTransactionMode = "conditional_savepoint", 

1552 close_resets_only: Union[bool, _NoArg] = _NoArg.NO_ARG, 

1553 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

1554 ): 

1555 r"""Construct a new :class:`_orm.Session`. 

1556 

1557 See also the :class:`.sessionmaker` function which is used to 

1558 generate a :class:`.Session`-producing callable with a given 

1559 set of arguments. 

1560 

1561 :param autoflush: When ``True``, all query operations will issue a 

1562 :meth:`~.Session.flush` call to this ``Session`` before proceeding. 

1563 This is a convenience feature so that :meth:`~.Session.flush` need 

1564 not be called repeatedly in order for database queries to retrieve 

1565 results. 

1566 

1567 .. seealso:: 

1568 

1569 :ref:`session_flushing` - additional background on autoflush 

1570 

1571 :param autobegin: Automatically start transactions (i.e. equivalent to 

1572 invoking :meth:`_orm.Session.begin`) when database access is 

1573 requested by an operation. Defaults to ``True``. Set to 

1574 ``False`` to prevent a :class:`_orm.Session` from implicitly 

1575 beginning transactions after construction, as well as after any of 

1576 the :meth:`_orm.Session.rollback`, :meth:`_orm.Session.commit`, 

1577 or :meth:`_orm.Session.close` methods are called. 

1578 

1579 .. versionadded:: 2.0 

1580 

1581 .. seealso:: 

1582 

1583 :ref:`session_autobegin_disable` 

1584 

1585 :param bind: An optional :class:`_engine.Engine` or 

1586 :class:`_engine.Connection` to 

1587 which this ``Session`` should be bound. When specified, all SQL 

1588 operations performed by this session will execute via this 

1589 connectable. 

1590 

1591 :param binds: A dictionary which may specify any number of 

1592 :class:`_engine.Engine` or :class:`_engine.Connection` 

1593 objects as the source of 

1594 connectivity for SQL operations on a per-entity basis. The keys 

1595 of the dictionary consist of any series of mapped classes, 

1596 arbitrary Python classes that are bases for mapped classes, 

1597 :class:`_schema.Table` objects and :class:`_orm.Mapper` objects. 

1598 The 

1599 values of the dictionary are then instances of 

1600 :class:`_engine.Engine` 

1601 or less commonly :class:`_engine.Connection` objects. 

1602 Operations which 

1603 proceed relative to a particular mapped class will consult this 

1604 dictionary for the closest matching entity in order to determine 

1605 which :class:`_engine.Engine` should be used for a particular SQL 

1606 operation. The complete heuristics for resolution are 

1607 described at :meth:`.Session.get_bind`. Usage looks like:: 

1608 

1609 Session = sessionmaker( 

1610 binds={ 

1611 SomeMappedClass: create_engine("postgresql+psycopg2://engine1"), 

1612 SomeDeclarativeBase: create_engine( 

1613 "postgresql+psycopg2://engine2" 

1614 ), 

1615 some_mapper: create_engine("postgresql+psycopg2://engine3"), 

1616 some_table: create_engine("postgresql+psycopg2://engine4"), 

1617 } 

1618 ) 

1619 

1620 .. seealso:: 

1621 

1622 :ref:`session_partitioning` 

1623 

1624 :meth:`.Session.bind_mapper` 

1625 

1626 :meth:`.Session.bind_table` 

1627 

1628 :meth:`.Session.get_bind` 

1629 

1630 

1631 :param \class_: Specify an alternate class other than 

1632 ``sqlalchemy.orm.session.Session`` which should be used by the 

1633 returned class. This is the only argument that is local to the 

1634 :class:`.sessionmaker` function, and is not sent directly to the 

1635 constructor for ``Session``. 

1636 

1637 :param enable_baked_queries: legacy; defaults to ``True``. 

1638 A parameter consumed 

1639 by the :mod:`sqlalchemy.ext.baked` extension to determine if 

1640 "baked queries" should be cached, as is the normal operation 

1641 of this extension. When set to ``False``, caching as used by 

1642 this particular extension is disabled. 

1643 

1644 .. versionchanged:: 1.4 The ``sqlalchemy.ext.baked`` extension is 

1645 legacy and is not used by any of SQLAlchemy's internals. This 

1646 flag therefore only affects applications that are making explicit 

1647 use of this extension within their own code. 

1648 

1649 :param execution_options: optional dictionary of execution options 

1650 that will be applied to the :class:`_engine.Connection` when first 

1651 procured for a transaction, as well as to all explicit query 

1652 executions such as :meth:`_orm.Session.execute`, 

1653 :meth:`_orm.Session.scalars`, and similar. This includes 

1654 flush (INSERT/UPDATE/DELETE) operations and is visible within 

1655 event hooks such as 

1656 :meth:`_events.ConnectionEvents.before_cursor_execute`. 

1657 

1658 Execution options present in statements as well as options passed 

1659 to methods like :meth:`_orm.Session.execute` explicitly take 

1660 precedence over the session-wide options. 

1661 

1662 .. versionadded:: 2.1 

1663 

1664 .. versionchanged:: 2.1.0b3 

1665 Session-level execution options are now applied to the 

1666 :class:`_engine.Connection` at procurement time, so that 

1667 they take effect for flush operations as well as explicit 

1668 query executions. Previously, options were only applied to 

1669 explicit calls such as :meth:`_orm.Session.execute`. 

1670 

1671 :param expire_on_commit: Defaults to ``True``. When ``True``, all 

1672 instances will be fully expired after each :meth:`~.commit`, 

1673 so that all attribute/object access subsequent to a completed 

1674 transaction will load from the most recent database state. 

1675 

1676 .. seealso:: 

1677 

1678 :ref:`session_committing` 

1679 

1680 :param future: Deprecated; this flag is always True. 

1681 

1682 .. seealso:: 

1683 

1684 :ref:`migration_20_toplevel` 

1685 

1686 :param info: optional dictionary of arbitrary data to be associated 

1687 with this :class:`.Session`. Is available via the 

1688 :attr:`.Session.info` attribute. Note the dictionary is copied at 

1689 construction time so that modifications to the per- 

1690 :class:`.Session` dictionary will be local to that 

1691 :class:`.Session`. 

1692 

1693 :param query_cls: Class which should be used to create new Query 

1694 objects, as returned by the :meth:`~.Session.query` method. 

1695 Defaults to :class:`_query.Query`. 

1696 

1697 :param twophase: When ``True``, all transactions will be started as 

1698 a "two phase" transaction, i.e. using the "two phase" semantics 

1699 of the database in use along with an XID. During a 

1700 :meth:`~.commit`, after :meth:`~.flush` has been issued for all 

1701 attached databases, the :meth:`~.TwoPhaseTransaction.prepare` 

1702 method on each database's :class:`.TwoPhaseTransaction` will be 

1703 called. This allows each database to roll back the entire 

1704 transaction, before each transaction is committed. 

1705 

1706 :param autocommit: the "autocommit" keyword is present for backwards 

1707 compatibility but must remain at its default value of ``False``. 

1708 

1709 :param join_transaction_mode: Describes the transactional behavior to 

1710 take when a given bind is a :class:`_engine.Connection` that 

1711 has already begun a transaction outside the scope of this 

1712 :class:`_orm.Session`; in other words the 

1713 :meth:`_engine.Connection.in_transaction()` method returns True. 

1714 

1715 The following behaviors only take effect when the :class:`_orm.Session` 

1716 **actually makes use of the connection given**; that is, a method 

1717 such as :meth:`_orm.Session.execute`, :meth:`_orm.Session.connection`, 

1718 etc. are actually invoked: 

1719 

1720 * ``"conditional_savepoint"`` - this is the default. if the given 

1721 :class:`_engine.Connection` is begun within a transaction but 

1722 does not have a SAVEPOINT, then ``"rollback_only"`` is used. 

1723 If the :class:`_engine.Connection` is additionally within 

1724 a SAVEPOINT, in other words 

1725 :meth:`_engine.Connection.in_nested_transaction()` method returns 

1726 True, then ``"create_savepoint"`` is used. 

1727 

1728 ``"conditional_savepoint"`` behavior attempts to make use of 

1729 savepoints in order to keep the state of the existing transaction 

1730 unchanged, but only if there is already a savepoint in progress; 

1731 otherwise, it is not assumed that the backend in use has adequate 

1732 support for SAVEPOINT, as availability of this feature varies. 

1733 ``"conditional_savepoint"`` also seeks to establish approximate 

1734 backwards compatibility with previous :class:`_orm.Session` 

1735 behavior, for applications that are not setting a specific mode. It 

1736 is recommended that one of the explicit settings be used. 

1737 

1738 * ``"create_savepoint"`` - the :class:`_orm.Session` will use 

1739 :meth:`_engine.Connection.begin_nested()` in all cases to create 

1740 its own transaction. This transaction by its nature rides 

1741 "on top" of any existing transaction that's opened on the given 

1742 :class:`_engine.Connection`; if the underlying database and 

1743 the driver in use has full, non-broken support for SAVEPOINT, the 

1744 external transaction will remain unaffected throughout the 

1745 lifespan of the :class:`_orm.Session`. 

1746 

1747 The ``"create_savepoint"`` mode is the most useful for integrating 

1748 a :class:`_orm.Session` into a test suite where an externally 

1749 initiated transaction should remain unaffected; however, it relies 

1750 on proper SAVEPOINT support from the underlying driver and 

1751 database. 

1752 

1753 .. tip:: When using SQLite, the SQLite driver included through 

1754 Python 3.11 does not handle SAVEPOINTs correctly in all cases 

1755 without workarounds. See the sections 

1756 :ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable` 

1757 for details on current workarounds. 

1758 

1759 * ``"control_fully"`` - the :class:`_orm.Session` will take 

1760 control of the given transaction as its own; 

1761 :meth:`_orm.Session.commit` will call ``.commit()`` on the 

1762 transaction, :meth:`_orm.Session.rollback` will call 

1763 ``.rollback()`` on the transaction, :meth:`_orm.Session.close` will 

1764 call ``.rollback`` on the transaction. 

1765 

1766 .. tip:: This mode of use is equivalent to how SQLAlchemy 1.4 would 

1767 handle a :class:`_engine.Connection` given with an existing 

1768 SAVEPOINT (i.e. :meth:`_engine.Connection.begin_nested`); the 

1769 :class:`_orm.Session` would take full control of the existing 

1770 SAVEPOINT. 

1771 

1772 * ``"rollback_only"`` - the :class:`_orm.Session` will take control 

1773 of the given transaction for ``.rollback()`` calls only; 

1774 ``.commit()`` calls will not be propagated to the given 

1775 transaction. ``.close()`` calls will have no effect on the 

1776 given transaction. 

1777 

1778 .. tip:: This mode of use is equivalent to how SQLAlchemy 1.4 would 

1779 handle a :class:`_engine.Connection` given with an existing 

1780 regular database transaction (i.e. 

1781 :meth:`_engine.Connection.begin`); the :class:`_orm.Session` 

1782 would propagate :meth:`_orm.Session.rollback` calls to the 

1783 underlying transaction, but not :meth:`_orm.Session.commit` or 

1784 :meth:`_orm.Session.close` calls. 

1785 

1786 .. versionadded:: 2.0.0rc1 

1787 

1788 :param close_resets_only: Defaults to ``True``. Determines if 

1789 the session should reset itself after calling ``.close()`` 

1790 or should pass in a no longer usable state, disabling reuse. 

1791 

1792 .. versionadded:: 2.0.22 added flag ``close_resets_only``. 

1793 A future SQLAlchemy version may change the default value of 

1794 this flag to ``False``. 

1795 

1796 .. seealso:: 

1797 

1798 :ref:`session_closing` - Detail on the semantics of 

1799 :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 

1800 

1801 """ # noqa 

1802 

1803 # considering allowing the "autocommit" keyword to still be accepted 

1804 # as long as it's False, so that external test suites, oslo.db etc 

1805 # continue to function as the argument appears to be passed in lots 

1806 # of cases including in our own test suite 

1807 if autocommit: 

1808 raise sa_exc.ArgumentError( 

1809 "autocommit=True is no longer supported" 

1810 ) 

1811 self.identity_map = identity._WeakInstanceDict() 

1812 

1813 if not future: 

1814 raise sa_exc.ArgumentError( 

1815 "The 'future' parameter passed to " 

1816 "Session() may only be set to True." 

1817 ) 

1818 

1819 self._new = {} # InstanceState->object, strong refs object 

1820 self._deleted = {} # same 

1821 self.bind = bind 

1822 self.binds = util.EMPTY_DICT 

1823 self._flushing = False 

1824 self._warn_on_events = False 

1825 self._transaction = None 

1826 self._nested_transaction = None 

1827 self.hash_key = _new_sessionid() 

1828 self.autobegin = autobegin 

1829 self.autoflush = autoflush 

1830 self.expire_on_commit = expire_on_commit 

1831 self.enable_baked_queries = enable_baked_queries 

1832 if execution_options: 

1833 self.execution_options = self.execution_options.union( 

1834 execution_options 

1835 ) 

1836 

1837 # the idea is that at some point NO_ARG will warn that in the future 

1838 # the default will switch to close_resets_only=False. 

1839 if close_resets_only in (True, _NoArg.NO_ARG): 

1840 self._close_state = _SessionCloseState.CLOSE_IS_RESET 

1841 else: 

1842 self._close_state = _SessionCloseState.ACTIVE 

1843 if ( 

1844 join_transaction_mode 

1845 and join_transaction_mode 

1846 not in JoinTransactionMode.__args__ # type: ignore[attr-defined] 

1847 ): 

1848 raise sa_exc.ArgumentError( 

1849 f"invalid selection for join_transaction_mode: " 

1850 f'"{join_transaction_mode}"' 

1851 ) 

1852 self.join_transaction_mode = join_transaction_mode 

1853 

1854 self.twophase = twophase 

1855 self._query_cls = query_cls if query_cls else query.Query 

1856 if info: 

1857 self.info.update(info) 

1858 

1859 if binds is not None: 

1860 for key, bind in binds.items(): 

1861 self._add_bind(key, bind) 

1862 

1863 _sessions[self.hash_key] = self 

1864 

1865 # used by sqlalchemy.engine.util.TransactionalContext 

1866 _trans_context_manager: Optional[TransactionalContext] = None 

1867 

1868 connection_callable: Optional[_ConnectionCallableProto] = None 

1869 

1870 def __enter__(self: _S) -> _S: 

1871 return self 

1872 

1873 def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 

1874 self.close() 

1875 

1876 @contextlib.contextmanager 

1877 def _maker_context_manager(self: _S) -> Iterator[_S]: 

1878 with self: 

1879 with self.begin(): 

1880 yield self 

1881 

1882 def in_transaction(self) -> bool: 

1883 """Return True if this :class:`_orm.Session` has begun a transaction. 

1884 

1885 .. versionadded:: 1.4 

1886 

1887 .. seealso:: 

1888 

1889 :attr:`_orm.Session.is_active` 

1890 

1891 

1892 """ 

1893 return self._transaction is not None 

1894 

1895 def in_nested_transaction(self) -> bool: 

1896 """Return True if this :class:`_orm.Session` has begun a nested 

1897 transaction, e.g. SAVEPOINT. 

1898 

1899 .. versionadded:: 1.4 

1900 

1901 """ 

1902 return self._nested_transaction is not None 

1903 

1904 def get_transaction(self) -> Optional[SessionTransaction]: 

1905 """Return the current root transaction in progress, if any. 

1906 

1907 .. versionadded:: 1.4 

1908 

1909 """ 

1910 trans = self._transaction 

1911 while trans is not None and trans._parent is not None: 

1912 trans = trans._parent 

1913 return trans 

1914 

1915 def get_nested_transaction(self) -> Optional[SessionTransaction]: 

1916 """Return the current nested transaction in progress, if any. 

1917 

1918 .. versionadded:: 1.4 

1919 

1920 """ 

1921 

1922 return self._nested_transaction 

1923 

1924 @util.memoized_property 

1925 def info(self) -> _InfoType: 

1926 """A user-modifiable dictionary. 

1927 

1928 The initial value of this dictionary can be populated using the 

1929 ``info`` argument to the :class:`.Session` constructor or 

1930 :class:`.sessionmaker` constructor or factory methods. The dictionary 

1931 here is always local to this :class:`.Session` and can be modified 

1932 independently of all other :class:`.Session` objects. 

1933 

1934 """ 

1935 return {} 

1936 

1937 def _autobegin_t(self, begin: bool = False) -> SessionTransaction: 

1938 if self._transaction is None: 

1939 if not begin and not self.autobegin: 

1940 raise sa_exc.InvalidRequestError( 

1941 "Autobegin is disabled on this Session; please call " 

1942 "session.begin() to start a new transaction" 

1943 ) 

1944 trans = SessionTransaction( 

1945 self, 

1946 ( 

1947 SessionTransactionOrigin.BEGIN 

1948 if begin 

1949 else SessionTransactionOrigin.AUTOBEGIN 

1950 ), 

1951 ) 

1952 assert self._transaction is trans 

1953 return trans 

1954 

1955 return self._transaction 

1956 

1957 def begin(self, nested: bool = False) -> SessionTransaction: 

1958 """Begin a transaction, or nested transaction, 

1959 on this :class:`.Session`, if one is not already begun. 

1960 

1961 The :class:`_orm.Session` object features **autobegin** behavior, 

1962 so that normally it is not necessary to call the 

1963 :meth:`_orm.Session.begin` 

1964 method explicitly. However, it may be used in order to control 

1965 the scope of when the transactional state is begun. 

1966 

1967 When used to begin the outermost transaction, an error is raised 

1968 if this :class:`.Session` is already inside of a transaction. 

1969 

1970 :param nested: if True, begins a SAVEPOINT transaction and is 

1971 equivalent to calling :meth:`~.Session.begin_nested`. For 

1972 documentation on SAVEPOINT transactions, please see 

1973 :ref:`session_begin_nested`. 

1974 

1975 :return: the :class:`.SessionTransaction` object. Note that 

1976 :class:`.SessionTransaction` 

1977 acts as a Python context manager, allowing :meth:`.Session.begin` 

1978 to be used in a "with" block. See :ref:`session_explicit_begin` for 

1979 an example. 

1980 

1981 .. seealso:: 

1982 

1983 :ref:`session_autobegin` 

1984 

1985 :ref:`unitofwork_transaction` 

1986 

1987 :meth:`.Session.begin_nested` 

1988 

1989 

1990 """ 

1991 

1992 trans = self._transaction 

1993 if trans is None: 

1994 trans = self._autobegin_t(begin=True) 

1995 

1996 if not nested: 

1997 return trans 

1998 

1999 assert trans is not None 

2000 

2001 if nested: 

2002 trans = trans._begin(nested=nested) 

2003 assert self._transaction is trans 

2004 self._nested_transaction = trans 

2005 else: 

2006 raise sa_exc.InvalidRequestError( 

2007 "A transaction is already begun on this Session." 

2008 ) 

2009 

2010 return trans # needed for __enter__/__exit__ hook 

2011 

2012 def begin_nested(self) -> SessionTransaction: 

2013 """Begin a "nested" transaction on this Session, e.g. SAVEPOINT. 

2014 

2015 The target database(s) and associated drivers must support SQL 

2016 SAVEPOINT for this method to function correctly. 

2017 

2018 For documentation on SAVEPOINT 

2019 transactions, please see :ref:`session_begin_nested`. 

2020 

2021 :return: the :class:`.SessionTransaction` object. Note that 

2022 :class:`.SessionTransaction` acts as a context manager, allowing 

2023 :meth:`.Session.begin_nested` to be used in a "with" block. 

2024 See :ref:`session_begin_nested` for a usage example. 

2025 

2026 .. seealso:: 

2027 

2028 :ref:`session_begin_nested` 

2029 

2030 :ref:`pysqlite_serializable` - special workarounds required 

2031 with the SQLite driver in order for SAVEPOINT to work 

2032 correctly. For asyncio use cases, see the section 

2033 :ref:`aiosqlite_serializable`. 

2034 

2035 """ 

2036 return self.begin(nested=True) 

2037 

2038 def rollback(self) -> None: 

2039 """Rollback the current transaction in progress. 

2040 

2041 If no transaction is in progress, this method is a pass-through. 

2042 

2043 The method always rolls back 

2044 the topmost database transaction, discarding any nested 

2045 transactions that may be in progress. 

2046 

2047 .. seealso:: 

2048 

2049 :ref:`session_rollback` 

2050 

2051 :ref:`unitofwork_transaction` 

2052 

2053 """ 

2054 if self._transaction is None: 

2055 pass 

2056 else: 

2057 self._transaction.rollback(_to_root=True) 

2058 

2059 def commit(self) -> None: 

2060 """Flush pending changes and commit the current transaction. 

2061 

2062 When the COMMIT operation is complete, all objects are fully 

2063 :term:`expired`, erasing their internal contents, which will be 

2064 automatically re-loaded when the objects are next accessed. In the 

2065 interim, these objects are in an expired state and will not function if 

2066 they are :term:`detached` from the :class:`.Session`. Additionally, 

2067 this re-load operation is not supported when using asyncio-oriented 

2068 APIs. The :paramref:`.Session.expire_on_commit` parameter may be used 

2069 to disable this behavior. 

2070 

2071 When there is no transaction in place for the :class:`.Session`, 

2072 indicating that no operations were invoked on this :class:`.Session` 

2073 since the previous call to :meth:`.Session.commit`, the method will 

2074 begin and commit an internal-only "logical" transaction, that does not 

2075 normally affect the database unless pending flush changes were 

2076 detected, but will still invoke event handlers and object expiration 

2077 rules. 

2078 

2079 The outermost database transaction is committed unconditionally, 

2080 automatically releasing any SAVEPOINTs in effect. 

2081 

2082 .. seealso:: 

2083 

2084 :ref:`session_committing` 

2085 

2086 :ref:`unitofwork_transaction` 

2087 

2088 :ref:`asyncio_orm_avoid_lazyloads` 

2089 

2090 """ 

2091 trans = self._transaction 

2092 if trans is None: 

2093 trans = self._autobegin_t() 

2094 

2095 trans.commit(_to_root=True) 

2096 

2097 def prepare(self) -> None: 

2098 """Prepare the current transaction in progress for two phase commit. 

2099 

2100 If no transaction is in progress, this method raises an 

2101 :exc:`~sqlalchemy.exc.InvalidRequestError`. 

2102 

2103 Only root transactions of two phase sessions can be prepared. If the 

2104 current transaction is not such, an 

2105 :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. 

2106 

2107 """ 

2108 trans = self._transaction 

2109 if trans is None: 

2110 trans = self._autobegin_t() 

2111 

2112 trans.prepare() 

2113 

2114 def connection( 

2115 self, 

2116 bind_arguments: Optional[_BindArguments] = None, 

2117 execution_options: Optional[CoreExecuteOptionsParameter] = None, 

2118 ) -> Connection: 

2119 r"""Return a :class:`_engine.Connection` object corresponding to this 

2120 :class:`.Session` object's transactional state. 

2121 

2122 Either the :class:`_engine.Connection` corresponding to the current 

2123 transaction is returned, or if no transaction is in progress, a new 

2124 one is begun and the :class:`_engine.Connection` 

2125 returned (note that no 

2126 transactional state is established with the DBAPI until the first 

2127 SQL statement is emitted). 

2128 

2129 Ambiguity in multi-bind or unbound :class:`.Session` objects can be 

2130 resolved through any of the optional keyword arguments. This 

2131 ultimately makes usage of the :meth:`.get_bind` method for resolution. 

2132 

2133 :param bind_arguments: dictionary of bind arguments. May include 

2134 "mapper", "bind", "clause", other custom arguments that are passed 

2135 to :meth:`.Session.get_bind`. 

2136 

2137 :param execution_options: a dictionary of execution options that will 

2138 be passed to :meth:`_engine.Connection.execution_options`, **when the 

2139 connection is first procured only**. If the connection is already 

2140 present within the :class:`.Session`, a warning is emitted and 

2141 the arguments are ignored. 

2142 

2143 .. seealso:: 

2144 

2145 :ref:`session_transaction_isolation` 

2146 

2147 """ 

2148 

2149 if bind_arguments: 

2150 bind = bind_arguments.pop("bind", None) 

2151 

2152 if bind is None: 

2153 bind = self.get_bind(**bind_arguments) 

2154 else: 

2155 bind = self.get_bind() 

2156 

2157 return self._connection_for_bind( 

2158 bind, 

2159 execution_options=execution_options, 

2160 ) 

2161 

2162 def _connection_for_bind( 

2163 self, 

2164 engine: _SessionBind, 

2165 execution_options: Optional[CoreExecuteOptionsParameter] = None, 

2166 **kw: Any, 

2167 ) -> Connection: 

2168 TransactionalContext._trans_ctx_check(self) 

2169 

2170 trans = self._transaction 

2171 if trans is None: 

2172 trans = self._autobegin_t() 

2173 return trans._connection_for_bind(engine, execution_options) 

2174 

2175 @overload 

2176 def _execute_internal( 

2177 self, 

2178 statement: Executable, 

2179 params: Optional[_CoreSingleExecuteParams] = None, 

2180 *, 

2181 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2182 bind_arguments: Optional[_BindArguments] = None, 

2183 _parent_execute_state: Optional[Any] = None, 

2184 _add_event: Optional[Any] = None, 

2185 _scalar_result: Literal[True] = ..., 

2186 ) -> Any: ... 

2187 

2188 @overload 

2189 def _execute_internal( 

2190 self, 

2191 statement: Executable, 

2192 params: Optional[_CoreAnyExecuteParams] = None, 

2193 *, 

2194 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2195 bind_arguments: Optional[_BindArguments] = None, 

2196 _parent_execute_state: Optional[Any] = None, 

2197 _add_event: Optional[Any] = None, 

2198 _scalar_result: bool = ..., 

2199 ) -> Result[Unpack[TupleAny]]: ... 

2200 

2201 def _execute_internal( 

2202 self, 

2203 statement: Executable, 

2204 params: Optional[_CoreAnyExecuteParams] = None, 

2205 *, 

2206 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2207 bind_arguments: Optional[_BindArguments] = None, 

2208 _parent_execute_state: Optional[Any] = None, 

2209 _add_event: Optional[Any] = None, 

2210 _scalar_result: bool = False, 

2211 ) -> Any: 

2212 statement = coercions.expect(roles.StatementRole, statement) 

2213 

2214 if not bind_arguments: 

2215 bind_arguments = {} 

2216 else: 

2217 bind_arguments = dict(bind_arguments) 

2218 

2219 if ( 

2220 statement._propagate_attrs.get("compile_state_plugin", None) 

2221 == "orm" 

2222 ): 

2223 compile_state_cls = CompileState._get_plugin_class_for_plugin( 

2224 statement, "orm" 

2225 ) 

2226 if TYPE_CHECKING: 

2227 assert isinstance( 

2228 compile_state_cls, context._AbstractORMCompileState 

2229 ) 

2230 else: 

2231 compile_state_cls = None 

2232 bind_arguments.setdefault("clause", statement) 

2233 

2234 combined_execution_options: util.immutabledict[str, Any] = ( 

2235 util.coerce_to_immutabledict(execution_options) 

2236 ) 

2237 if self.execution_options: 

2238 # merge given execution options with session-wide execution 

2239 # options. if the statement also has execution_options, 

2240 # maintain priority of session.execution_options -> 

2241 # statement.execution_options -> method passed execution_options 

2242 # by omitting from the base execution options those keys that 

2243 # will come from the statement 

2244 if statement._execution_options: 

2245 combined_execution_options = util.immutabledict( 

2246 { 

2247 k: v 

2248 for k, v in self.execution_options.items() 

2249 if k not in statement._execution_options 

2250 } 

2251 ).union(combined_execution_options) 

2252 else: 

2253 combined_execution_options = self.execution_options.union( 

2254 combined_execution_options 

2255 ) 

2256 

2257 if _parent_execute_state: 

2258 events_todo = _parent_execute_state._remaining_events() 

2259 else: 

2260 events_todo = self.dispatch.do_orm_execute 

2261 if _add_event: 

2262 events_todo = list(events_todo) + [_add_event] 

2263 

2264 if events_todo: 

2265 # save the original execution options before 

2266 # orm_pre_session_exec processes them, so that we can pass 

2267 # the unprocessed options (plus any explicit updates from event 

2268 # hooks) to the second orm_pre_session_exec call. This 

2269 # prevents internal state like _sa_orm_load_options and 

2270 # yield_per from the first call leaking into the second call, 

2271 # which would otherwise cause issues like yield_per incorrectly 

2272 # propagating into post-load (selectinload etc.) queries. 

2273 # part of #13301. 

2274 original_execution_options = combined_execution_options 

2275 

2276 if compile_state_cls is not None: 

2277 # for event handlers, do the orm_pre_session_exec 

2278 # pass ahead of the event handlers, so that things like 

2279 # .load_options, .update_delete_options etc. are populated. 

2280 # is_pre_event=True allows the hook to hold off on things 

2281 # it doesn't want to do twice, including autoflush as well 

2282 # as "pre fetch" for DML, etc. 

2283 ( 

2284 statement, 

2285 combined_execution_options, 

2286 params, 

2287 ) = compile_state_cls.orm_pre_session_exec( 

2288 self, 

2289 statement, 

2290 params, 

2291 combined_execution_options, 

2292 bind_arguments, 

2293 True, 

2294 ) 

2295 

2296 orm_exec_state = ORMExecuteState( 

2297 self, 

2298 statement, 

2299 params, 

2300 combined_execution_options, 

2301 bind_arguments, 

2302 compile_state_cls, 

2303 events_todo, 

2304 ) 

2305 for idx, fn in enumerate(events_todo): 

2306 orm_exec_state._starting_event_idx = idx 

2307 fn_result: Optional[Result[Unpack[TupleAny]]] = fn( 

2308 orm_exec_state 

2309 ) 

2310 if fn_result: 

2311 if _scalar_result: 

2312 return fn_result.scalar() 

2313 else: 

2314 return fn_result 

2315 

2316 statement = orm_exec_state.statement 

2317 params = orm_exec_state.parameters 

2318 

2319 # use the original execution options plus only the explicit 

2320 # updates from event hooks, not the processed options from 

2321 # the first orm_pre_session_exec call 

2322 combined_execution_options = original_execution_options.union( 

2323 orm_exec_state._update_execution_options 

2324 ) 

2325 

2326 if compile_state_cls is not None: 

2327 # now run orm_pre_session_exec() "for real". if there were 

2328 # event hooks, this will re-run the steps that interpret 

2329 # new execution_options into load_options / update_delete_options, 

2330 # which we assume the event hook might have updated. 

2331 # autoflush will also be invoked in this step if enabled. 

2332 ( 

2333 statement, 

2334 combined_execution_options, 

2335 params, 

2336 ) = compile_state_cls.orm_pre_session_exec( 

2337 self, 

2338 statement, 

2339 params, 

2340 combined_execution_options, 

2341 bind_arguments, 

2342 False, 

2343 ) 

2344 else: 

2345 # Issue #9809: unconditionally autoflush for Core statements 

2346 self._autoflush() 

2347 

2348 bind = self.get_bind(**bind_arguments) 

2349 

2350 conn = self._connection_for_bind(bind) 

2351 

2352 if _scalar_result and not compile_state_cls: 

2353 if TYPE_CHECKING: 

2354 params = cast(_CoreSingleExecuteParams, params) 

2355 return conn.scalar( 

2356 statement, 

2357 params or {}, 

2358 execution_options=combined_execution_options, 

2359 ) 

2360 

2361 if compile_state_cls: 

2362 result: Result[Unpack[TupleAny]] = ( 

2363 compile_state_cls.orm_execute_statement( 

2364 self, 

2365 statement, 

2366 params or {}, 

2367 combined_execution_options, 

2368 bind_arguments, 

2369 conn, 

2370 ) 

2371 ) 

2372 else: 

2373 result = conn.execute( 

2374 statement, params, execution_options=combined_execution_options 

2375 ) 

2376 

2377 if _scalar_result: 

2378 return result.scalar() 

2379 else: 

2380 return result 

2381 

2382 @overload 

2383 def execute( 

2384 self, 

2385 statement: TypedReturnsRows[Unpack[_Ts]], 

2386 params: Optional[_CoreAnyExecuteParams] = None, 

2387 *, 

2388 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2389 bind_arguments: Optional[_BindArguments] = None, 

2390 _parent_execute_state: Optional[Any] = None, 

2391 _add_event: Optional[Any] = None, 

2392 ) -> Result[Unpack[_Ts]]: ... 

2393 

2394 @overload 

2395 def execute( 

2396 self, 

2397 statement: Executable, 

2398 params: Optional[_CoreAnyExecuteParams] = None, 

2399 *, 

2400 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2401 bind_arguments: Optional[_BindArguments] = None, 

2402 _parent_execute_state: Optional[Any] = None, 

2403 _add_event: Optional[Any] = None, 

2404 ) -> Result[Unpack[TupleAny]]: ... 

2405 

2406 def execute( 

2407 self, 

2408 statement: Executable, 

2409 params: Optional[_CoreAnyExecuteParams] = None, 

2410 *, 

2411 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2412 bind_arguments: Optional[_BindArguments] = None, 

2413 _parent_execute_state: Optional[Any] = None, 

2414 _add_event: Optional[Any] = None, 

2415 ) -> Result[Unpack[TupleAny]]: 

2416 r"""Execute a SQL expression construct. 

2417 

2418 Returns a :class:`_engine.Result` object representing 

2419 results of the statement execution. 

2420 

2421 E.g.:: 

2422 

2423 from sqlalchemy import select 

2424 

2425 result = session.execute(select(User).where(User.id == 5)) 

2426 

2427 The API contract of :meth:`_orm.Session.execute` is similar to that 

2428 of :meth:`_engine.Connection.execute`, the :term:`2.0 style` version 

2429 of :class:`_engine.Connection`. 

2430 

2431 .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is 

2432 now the primary point of ORM statement execution when using 

2433 :term:`2.0 style` ORM usage. 

2434 

2435 :param statement: 

2436 An executable statement (i.e. an :class:`.Executable` expression 

2437 such as :func:`_expression.select`). 

2438 

2439 :param params: 

2440 Optional dictionary, or list of dictionaries, containing 

2441 bound parameter values. If a single dictionary, single-row 

2442 execution occurs; if a list of dictionaries, an 

2443 "executemany" will be invoked. The keys in each dictionary 

2444 must correspond to parameter names present in the statement. 

2445 

2446 :param execution_options: optional dictionary of execution options, 

2447 which will be associated with the statement execution. This 

2448 dictionary can provide a subset of the options that are accepted 

2449 by :meth:`_engine.Connection.execution_options`, and may also 

2450 provide additional options understood only in an ORM context. 

2451 

2452 The execution_options are passed along to methods like 

2453 :meth:`.Connection.execute` on :class:`.Connection` giving the 

2454 highest priority to execution_options that are passed to this 

2455 method explicitly, then the options that are present on the 

2456 statement object if any, and finally those options present 

2457 session-wide. 

2458 

2459 .. seealso:: 

2460 

2461 :ref:`orm_queryguide_execution_options` - ORM-specific execution 

2462 options 

2463 

2464 :param bind_arguments: dictionary of additional arguments to determine 

2465 the bind. May include "mapper", "bind", or other custom arguments. 

2466 Contents of this dictionary are passed to the 

2467 :meth:`.Session.get_bind` method. 

2468 

2469 :return: a :class:`_engine.Result` object. 

2470 

2471 

2472 """ 

2473 return self._execute_internal( 

2474 statement, 

2475 params, 

2476 execution_options=execution_options, 

2477 bind_arguments=bind_arguments, 

2478 _parent_execute_state=_parent_execute_state, 

2479 _add_event=_add_event, 

2480 ) 

2481 

2482 # special case to handle mypy issue: 

2483 # https://github.com/python/mypy/issues/20651 

2484 @overload 

2485 def scalar( 

2486 self, 

2487 statement: TypedReturnsRows[Never], 

2488 params: Optional[_CoreSingleExecuteParams] = None, 

2489 *, 

2490 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2491 bind_arguments: Optional[_BindArguments] = None, 

2492 **kw: Any, 

2493 ) -> Optional[Any]: ... 

2494 

2495 @overload 

2496 def scalar( 

2497 self, 

2498 statement: TypedReturnsRows[_T], 

2499 params: Optional[_CoreSingleExecuteParams] = None, 

2500 *, 

2501 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2502 bind_arguments: Optional[_BindArguments] = None, 

2503 **kw: Any, 

2504 ) -> Optional[_T]: ... 

2505 

2506 @overload 

2507 def scalar( 

2508 self, 

2509 statement: Executable, 

2510 params: Optional[_CoreSingleExecuteParams] = None, 

2511 *, 

2512 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2513 bind_arguments: Optional[_BindArguments] = None, 

2514 **kw: Any, 

2515 ) -> Any: ... 

2516 

2517 def scalar( 

2518 self, 

2519 statement: Executable, 

2520 params: Optional[_CoreSingleExecuteParams] = None, 

2521 *, 

2522 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2523 bind_arguments: Optional[_BindArguments] = None, 

2524 **kw: Any, 

2525 ) -> Any: 

2526 """Execute a statement and return a scalar result. 

2527 

2528 Usage and parameters are the same as that of 

2529 :meth:`_orm.Session.execute`; the return result is a scalar Python 

2530 value. 

2531 

2532 """ 

2533 

2534 return self._execute_internal( 

2535 statement, 

2536 params, 

2537 execution_options=execution_options, 

2538 bind_arguments=bind_arguments, 

2539 _scalar_result=True, 

2540 **kw, 

2541 ) 

2542 

2543 @overload 

2544 def scalars( 

2545 self, 

2546 statement: TypedReturnsRows[_T], 

2547 params: Optional[_CoreAnyExecuteParams] = None, 

2548 *, 

2549 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2550 bind_arguments: Optional[_BindArguments] = None, 

2551 **kw: Any, 

2552 ) -> ScalarResult[_T]: ... 

2553 

2554 @overload 

2555 def scalars( 

2556 self, 

2557 statement: Executable, 

2558 params: Optional[_CoreAnyExecuteParams] = None, 

2559 *, 

2560 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2561 bind_arguments: Optional[_BindArguments] = None, 

2562 **kw: Any, 

2563 ) -> ScalarResult[Any]: ... 

2564 

2565 def scalars( 

2566 self, 

2567 statement: Executable, 

2568 params: Optional[_CoreAnyExecuteParams] = None, 

2569 *, 

2570 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

2571 bind_arguments: Optional[_BindArguments] = None, 

2572 **kw: Any, 

2573 ) -> ScalarResult[Any]: 

2574 """Execute a statement and return the results as scalars. 

2575 

2576 Usage and parameters are the same as that of 

2577 :meth:`_orm.Session.execute`; the return result is a 

2578 :class:`_result.ScalarResult` filtering object which 

2579 will return single elements rather than :class:`_row.Row` objects. 

2580 

2581 :return: a :class:`_result.ScalarResult` object 

2582 

2583 .. versionadded:: 1.4.24 Added :meth:`_orm.Session.scalars` 

2584 

2585 .. versionadded:: 1.4.26 Added :meth:`_orm.scoped_session.scalars` 

2586 

2587 .. seealso:: 

2588 

2589 :ref:`orm_queryguide_select_orm_entities` - contrasts the behavior 

2590 of :meth:`_orm.Session.execute` to :meth:`_orm.Session.scalars` 

2591 

2592 """ 

2593 

2594 return self._execute_internal( 

2595 statement, 

2596 params=params, 

2597 execution_options=execution_options, 

2598 bind_arguments=bind_arguments, 

2599 _scalar_result=False, # mypy appreciates this 

2600 **kw, 

2601 ).scalars() 

2602 

2603 def close(self) -> None: 

2604 """Close out the transactional resources and ORM objects used by this 

2605 :class:`_orm.Session`. 

2606 

2607 This expunges all ORM objects associated with this 

2608 :class:`_orm.Session`, ends any transaction in progress and 

2609 :term:`releases` any :class:`_engine.Connection` objects which this 

2610 :class:`_orm.Session` itself has checked out from associated 

2611 :class:`_engine.Engine` objects. The operation then leaves the 

2612 :class:`_orm.Session` in a state which it may be used again. 

2613 

2614 .. tip:: 

2615 

2616 In the default running mode the :meth:`_orm.Session.close` 

2617 method **does not prevent the Session from being used again**. 

2618 The :class:`_orm.Session` itself does not actually have a 

2619 distinct "closed" state; it merely means 

2620 the :class:`_orm.Session` will release all database connections 

2621 and ORM objects. 

2622 

2623 Setting the parameter :paramref:`_orm.Session.close_resets_only` 

2624 to ``False`` will instead make the ``close`` final, meaning that 

2625 any further action on the session will be forbidden. 

2626 

2627 .. versionchanged:: 1.4 The :meth:`.Session.close` method does not 

2628 immediately create a new :class:`.SessionTransaction` object; 

2629 instead, the new :class:`.SessionTransaction` is created only if 

2630 the :class:`.Session` is used again for a database operation. 

2631 

2632 .. seealso:: 

2633 

2634 :ref:`session_closing` - detail on the semantics of 

2635 :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 

2636 

2637 :meth:`_orm.Session.reset` - a similar method that behaves like 

2638 ``close()`` with the parameter 

2639 :paramref:`_orm.Session.close_resets_only` set to ``True``. 

2640 

2641 """ 

2642 self._close_impl(invalidate=False) 

2643 

2644 def reset(self) -> None: 

2645 """Close out the transactional resources and ORM objects used by this 

2646 :class:`_orm.Session`, resetting the session to its initial state. 

2647 

2648 This method provides for same "reset-only" behavior that the 

2649 :meth:`_orm.Session.close` method has provided historically, where the 

2650 state of the :class:`_orm.Session` is reset as though the object were 

2651 brand new, and ready to be used again. 

2652 This method may then be useful for :class:`_orm.Session` objects 

2653 which set :paramref:`_orm.Session.close_resets_only` to ``False``, 

2654 so that "reset only" behavior is still available. 

2655 

2656 .. versionadded:: 2.0.22 

2657 

2658 .. seealso:: 

2659 

2660 :ref:`session_closing` - detail on the semantics of 

2661 :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`. 

2662 

2663 :meth:`_orm.Session.close` - a similar method will additionally 

2664 prevent reuse of the Session when the parameter 

2665 :paramref:`_orm.Session.close_resets_only` is set to ``False``. 

2666 """ 

2667 self._close_impl(invalidate=False, is_reset=True) 

2668 

2669 def invalidate(self) -> None: 

2670 """Close this Session, using connection invalidation. 

2671 

2672 This is a variant of :meth:`.Session.close` that will additionally 

2673 ensure that the :meth:`_engine.Connection.invalidate` 

2674 method will be called on each :class:`_engine.Connection` object 

2675 that is currently in use for a transaction (typically there is only 

2676 one connection unless the :class:`_orm.Session` is used with 

2677 multiple engines). 

2678 

2679 This can be called when the database is known to be in a state where 

2680 the connections are no longer safe to be used. 

2681 

2682 Below illustrates a scenario when using `gevent 

2683 <https://www.gevent.org/>`_, which can produce ``Timeout`` exceptions 

2684 that may mean the underlying connection should be discarded:: 

2685 

2686 import gevent 

2687 

2688 try: 

2689 sess = Session() 

2690 sess.add(User()) 

2691 sess.commit() 

2692 except gevent.Timeout: 

2693 sess.invalidate() 

2694 raise 

2695 except: 

2696 sess.rollback() 

2697 raise 

2698 

2699 The method additionally does everything that :meth:`_orm.Session.close` 

2700 does, including that all ORM objects are expunged. 

2701 

2702 """ 

2703 self._close_impl(invalidate=True) 

2704 

2705 def _close_impl(self, invalidate: bool, is_reset: bool = False) -> None: 

2706 if not is_reset and self._close_state is _SessionCloseState.ACTIVE: 

2707 self._close_state = _SessionCloseState.CLOSED 

2708 self.expunge_all() 

2709 if self._transaction is not None: 

2710 for transaction in self._transaction._iterate_self_and_parents(): 

2711 transaction.close(invalidate) 

2712 

2713 def expunge_all(self) -> None: 

2714 """Remove all object instances from this ``Session``. 

2715 

2716 This is equivalent to calling ``expunge(obj)`` on all objects in this 

2717 ``Session``. 

2718 

2719 """ 

2720 

2721 all_states = self.identity_map.all_states() + list(self._new) 

2722 self.identity_map._kill() 

2723 self.identity_map = identity._WeakInstanceDict() 

2724 self._new = {} 

2725 self._deleted = {} 

2726 

2727 statelib.InstanceState._detach_states(all_states, self) 

2728 

2729 def _add_bind(self, key: _SessionBindKey, bind: _SessionBind) -> None: 

2730 new_binds: Dict[_SessionBindKey, _SessionBind] = {} 

2731 

2732 try: 

2733 insp = inspect(key) 

2734 except sa_exc.NoInspectionAvailable as err: 

2735 if not isinstance(key, type): 

2736 raise sa_exc.ArgumentError( 

2737 "Not an acceptable bind target: %s" % key 

2738 ) from err 

2739 else: 

2740 new_binds[key] = bind 

2741 else: 

2742 if TYPE_CHECKING: 

2743 assert isinstance(insp, Inspectable) 

2744 

2745 if isinstance(insp, TableClause): 

2746 new_binds[insp] = bind 

2747 elif insp_is_mapper(insp): 

2748 new_binds[insp.class_] = bind 

2749 for _selectable in insp._all_tables: 

2750 new_binds[_selectable] = bind 

2751 else: 

2752 raise sa_exc.ArgumentError( 

2753 "Not an acceptable bind target: %s" % key 

2754 ) 

2755 

2756 self.binds = self.binds.merge_with( # type: ignore[attr-defined] 

2757 new_binds 

2758 ) 

2759 

2760 def bind_mapper( 

2761 self, mapper: _EntityBindKey[_O], bind: _SessionBind 

2762 ) -> None: 

2763 """Associate a :class:`_orm.Mapper` or arbitrary Python class with a 

2764 "bind", e.g. an :class:`_engine.Engine` or 

2765 :class:`_engine.Connection`. 

2766 

2767 The given entity is added to a lookup used by the 

2768 :meth:`.Session.get_bind` method. 

2769 

2770 :param mapper: a :class:`_orm.Mapper` object, 

2771 or an instance of a mapped 

2772 class, or any Python class that is the base of a set of mapped 

2773 classes. 

2774 

2775 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 

2776 object. 

2777 

2778 .. seealso:: 

2779 

2780 :ref:`session_partitioning` 

2781 

2782 :paramref:`.Session.binds` 

2783 

2784 :meth:`.Session.bind_table` 

2785 

2786 

2787 """ 

2788 self._add_bind(mapper, bind) 

2789 

2790 def bind_table(self, table: TableClause, bind: _SessionBind) -> None: 

2791 """Associate a :class:`_schema.Table` with a "bind", e.g. an 

2792 :class:`_engine.Engine` 

2793 or :class:`_engine.Connection`. 

2794 

2795 The given :class:`_schema.Table` is added to a lookup used by the 

2796 :meth:`.Session.get_bind` method. 

2797 

2798 :param table: a :class:`_schema.Table` object, 

2799 which is typically the target 

2800 of an ORM mapping, or is present within a selectable that is 

2801 mapped. 

2802 

2803 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 

2804 object. 

2805 

2806 .. seealso:: 

2807 

2808 :ref:`session_partitioning` 

2809 

2810 :paramref:`.Session.binds` 

2811 

2812 :meth:`.Session.bind_mapper` 

2813 

2814 

2815 """ 

2816 self._add_bind(table, bind) 

2817 

2818 def get_bind( 

2819 self, 

2820 mapper: Optional[_EntityBindKey[_O]] = None, 

2821 *, 

2822 clause: Optional[ClauseElement] = None, 

2823 bind: Optional[_SessionBind] = None, 

2824 _sa_skip_events: Optional[bool] = None, 

2825 _sa_skip_for_implicit_returning: bool = False, 

2826 **kw: Any, 

2827 ) -> Union[Engine, Connection]: 

2828 """Return a "bind" to which this :class:`.Session` is bound. 

2829 

2830 The "bind" is usually an instance of :class:`_engine.Engine`, 

2831 except in the case where the :class:`.Session` has been 

2832 explicitly bound directly to a :class:`_engine.Connection`. 

2833 

2834 For a multiply-bound or unbound :class:`.Session`, the 

2835 ``mapper`` or ``clause`` arguments are used to determine the 

2836 appropriate bind to return. 

2837 

2838 Note that the "mapper" argument is usually present 

2839 when :meth:`.Session.get_bind` is called via an ORM 

2840 operation such as a :meth:`.Session.query`, each 

2841 individual INSERT/UPDATE/DELETE operation within a 

2842 :meth:`.Session.flush`, call, etc. 

2843 

2844 The order of resolution is: 

2845 

2846 1. if mapper given and :paramref:`.Session.binds` is present, 

2847 locate a bind based first on the mapper in use, then 

2848 on the mapped class in use, then on any base classes that are 

2849 present in the ``__mro__`` of the mapped class, from more specific 

2850 superclasses to more general. 

2851 2. if clause given and ``Session.binds`` is present, 

2852 locate a bind based on :class:`_schema.Table` objects 

2853 found in the given clause present in ``Session.binds``. 

2854 3. if ``Session.binds`` is present, return that. 

2855 4. if clause given, attempt to return a bind 

2856 linked to the :class:`_schema.MetaData` ultimately 

2857 associated with the clause. 

2858 5. if mapper given, attempt to return a bind 

2859 linked to the :class:`_schema.MetaData` ultimately 

2860 associated with the :class:`_schema.Table` or other 

2861 selectable to which the mapper is mapped. 

2862 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError` 

2863 is raised. 

2864 

2865 Note that the :meth:`.Session.get_bind` method can be overridden on 

2866 a user-defined subclass of :class:`.Session` to provide any kind 

2867 of bind resolution scheme. See the example at 

2868 :ref:`session_custom_partitioning`. 

2869 

2870 :param mapper: 

2871 Optional mapped class or corresponding :class:`_orm.Mapper` instance. 

2872 The bind can be derived from a :class:`_orm.Mapper` first by 

2873 consulting the "binds" map associated with this :class:`.Session`, 

2874 and secondly by consulting the :class:`_schema.MetaData` associated 

2875 with the :class:`_schema.Table` to which the :class:`_orm.Mapper` is 

2876 mapped for a bind. 

2877 

2878 :param clause: 

2879 A :class:`_expression.ClauseElement` (i.e. 

2880 :func:`_expression.select`, 

2881 :func:`_expression.text`, 

2882 etc.). If the ``mapper`` argument is not present or could not 

2883 produce a bind, the given expression construct will be searched 

2884 for a bound element, typically a :class:`_schema.Table` 

2885 associated with 

2886 bound :class:`_schema.MetaData`. 

2887 

2888 .. seealso:: 

2889 

2890 :ref:`session_partitioning` 

2891 

2892 :paramref:`.Session.binds` 

2893 

2894 :meth:`.Session.bind_mapper` 

2895 

2896 :meth:`.Session.bind_table` 

2897 

2898 """ 

2899 

2900 # this function is documented as a subclassing hook, so we have 

2901 # to call this method even if the return is simple 

2902 if bind: 

2903 return bind 

2904 elif not self.binds and self.bind: 

2905 # simplest and most common case, we have a bind and no 

2906 # per-mapper/table binds, we're done 

2907 return self.bind 

2908 

2909 # we don't have self.bind and either have self.binds 

2910 # or we don't have self.binds (which is legacy). Look at the 

2911 # mapper and the clause 

2912 if mapper is None and clause is None: 

2913 if self.bind: 

2914 return self.bind 

2915 else: 

2916 raise sa_exc.UnboundExecutionError( 

2917 "This session is not bound to a single Engine or " 

2918 "Connection, and no context was provided to locate " 

2919 "a binding." 

2920 ) 

2921 

2922 # look more closely at the mapper. 

2923 if mapper is not None: 

2924 try: 

2925 inspected_mapper = inspect(mapper) 

2926 except sa_exc.NoInspectionAvailable as err: 

2927 if isinstance(mapper, type): 

2928 raise exc.UnmappedClassError(mapper) from err 

2929 else: 

2930 raise 

2931 else: 

2932 inspected_mapper = None 

2933 

2934 # match up the mapper or clause in the binds 

2935 if self.binds: 

2936 # matching mappers and selectables to entries in the 

2937 # binds dictionary; supported use case. 

2938 if inspected_mapper: 

2939 for cls in inspected_mapper.class_.__mro__: 

2940 if cls in self.binds: 

2941 return self.binds[cls] 

2942 if clause is None: 

2943 clause = inspected_mapper.persist_selectable 

2944 

2945 if clause is not None: 

2946 plugin_subject = clause._propagate_attrs.get( 

2947 "plugin_subject", None 

2948 ) 

2949 

2950 if plugin_subject is not None: 

2951 for cls in plugin_subject.mapper.class_.__mro__: 

2952 if cls in self.binds: 

2953 return self.binds[cls] 

2954 

2955 for obj in visitors.iterate(clause): 

2956 if obj in self.binds: 

2957 if TYPE_CHECKING: 

2958 assert isinstance(obj, Table) 

2959 return self.binds[obj] 

2960 

2961 # none of the binds matched, but we have a fallback bind. 

2962 # return that 

2963 if self.bind: 

2964 return self.bind 

2965 

2966 context = [] 

2967 if inspected_mapper is not None: 

2968 context.append(f"mapper {inspected_mapper}") 

2969 if clause is not None: 

2970 context.append("SQL expression") 

2971 

2972 raise sa_exc.UnboundExecutionError( 

2973 f"Could not locate a bind configured on " 

2974 f'{", ".join(context)} or this Session.' 

2975 ) 

2976 

2977 @overload 

2978 def query(self, _entity: _EntityType[_O]) -> Query[_O]: ... 

2979 

2980 @overload 

2981 def query( 

2982 self, _colexpr: TypedColumnsClauseRole[_T] 

2983 ) -> RowReturningQuery[_T]: ... 

2984 

2985 # START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8 

2986 

2987 # code within this block is **programmatically, 

2988 # statically generated** by tools/generate_tuple_map_overloads.py 

2989 

2990 @overload 

2991 def query( 

2992 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], / 

2993 ) -> RowReturningQuery[_T0, _T1]: ... 

2994 

2995 @overload 

2996 def query( 

2997 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], / 

2998 ) -> RowReturningQuery[_T0, _T1, _T2]: ... 

2999 

3000 @overload 

3001 def query( 

3002 self, 

3003 __ent0: _TCCA[_T0], 

3004 __ent1: _TCCA[_T1], 

3005 __ent2: _TCCA[_T2], 

3006 __ent3: _TCCA[_T3], 

3007 /, 

3008 ) -> RowReturningQuery[_T0, _T1, _T2, _T3]: ... 

3009 

3010 @overload 

3011 def query( 

3012 self, 

3013 __ent0: _TCCA[_T0], 

3014 __ent1: _TCCA[_T1], 

3015 __ent2: _TCCA[_T2], 

3016 __ent3: _TCCA[_T3], 

3017 __ent4: _TCCA[_T4], 

3018 /, 

3019 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4]: ... 

3020 

3021 @overload 

3022 def query( 

3023 self, 

3024 __ent0: _TCCA[_T0], 

3025 __ent1: _TCCA[_T1], 

3026 __ent2: _TCCA[_T2], 

3027 __ent3: _TCCA[_T3], 

3028 __ent4: _TCCA[_T4], 

3029 __ent5: _TCCA[_T5], 

3030 /, 

3031 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5]: ... 

3032 

3033 @overload 

3034 def query( 

3035 self, 

3036 __ent0: _TCCA[_T0], 

3037 __ent1: _TCCA[_T1], 

3038 __ent2: _TCCA[_T2], 

3039 __ent3: _TCCA[_T3], 

3040 __ent4: _TCCA[_T4], 

3041 __ent5: _TCCA[_T5], 

3042 __ent6: _TCCA[_T6], 

3043 /, 

3044 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5, _T6]: ... 

3045 

3046 @overload 

3047 def query( 

3048 self, 

3049 __ent0: _TCCA[_T0], 

3050 __ent1: _TCCA[_T1], 

3051 __ent2: _TCCA[_T2], 

3052 __ent3: _TCCA[_T3], 

3053 __ent4: _TCCA[_T4], 

3054 __ent5: _TCCA[_T5], 

3055 __ent6: _TCCA[_T6], 

3056 __ent7: _TCCA[_T7], 

3057 /, 

3058 *entities: _ColumnsClauseArgument[Any], 

3059 ) -> RowReturningQuery[ 

3060 _T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, Unpack[TupleAny] 

3061 ]: ... 

3062 

3063 # END OVERLOADED FUNCTIONS self.query 

3064 

3065 @overload 

3066 def query( 

3067 self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any 

3068 ) -> Query[Any]: ... 

3069 

3070 def query( 

3071 self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any 

3072 ) -> Query[Any]: 

3073 """Return a new :class:`_query.Query` object corresponding to this 

3074 :class:`_orm.Session`. 

3075 

3076 Note that the :class:`_query.Query` object is legacy as of 

3077 SQLAlchemy 2.0; the :func:`_sql.select` construct is now used 

3078 to construct ORM queries. 

3079 

3080 .. seealso:: 

3081 

3082 :ref:`unified_tutorial` 

3083 

3084 :ref:`queryguide_toplevel` 

3085 

3086 :ref:`query_api_toplevel` - legacy API doc 

3087 

3088 """ 

3089 

3090 return self._query_cls(entities, self, **kwargs) 

3091 

3092 def _identity_lookup( 

3093 self, 

3094 mapper: Mapper[_O], 

3095 primary_key_identity: Union[Any, Tuple[Any, ...]], 

3096 identity_token: Any = None, 

3097 passive: PassiveFlag = PassiveFlag.PASSIVE_OFF, 

3098 lazy_loaded_from: Optional[InstanceState[Any]] = None, 

3099 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

3100 bind_arguments: Optional[_BindArguments] = None, 

3101 ) -> Union[Optional[_O], LoaderCallableStatus]: 

3102 """Locate an object in the identity map. 

3103 

3104 Given a primary key identity, constructs an identity key and then 

3105 looks in the session's identity map. If present, the object may 

3106 be run through unexpiration rules (e.g. load unloaded attributes, 

3107 check if was deleted). 

3108 

3109 e.g.:: 

3110 

3111 obj = session._identity_lookup(inspect(SomeClass), (1,)) 

3112 

3113 :param mapper: mapper in use 

3114 :param primary_key_identity: the primary key we are searching for, as 

3115 a tuple. 

3116 :param identity_token: identity token that should be used to create 

3117 the identity key. Used as is, however overriding subclasses can 

3118 repurpose this in order to interpret the value in a special way, 

3119 such as if None then look among multiple target tokens. 

3120 :param passive: passive load flag passed to 

3121 :func:`.loading.get_from_identity`, which impacts the behavior if 

3122 the object is found; the object may be validated and/or unexpired 

3123 if the flag allows for SQL to be emitted. 

3124 :param lazy_loaded_from: an :class:`.InstanceState` that is 

3125 specifically asking for this identity as a related identity. Used 

3126 for sharding schemes where there is a correspondence between an object 

3127 and a related object being lazy-loaded (or otherwise 

3128 relationship-loaded). 

3129 

3130 :return: None if the object is not found in the identity map, *or* 

3131 if the object was unexpired and found to have been deleted. 

3132 if passive flags disallow SQL and the object is expired, returns 

3133 PASSIVE_NO_RESULT. In all other cases the instance is returned. 

3134 

3135 .. versionchanged:: 1.4.0 - the :meth:`.Session._identity_lookup` 

3136 method was moved from :class:`_query.Query` to 

3137 :class:`.Session`, to avoid having to instantiate the 

3138 :class:`_query.Query` object. 

3139 

3140 

3141 """ 

3142 

3143 key = mapper.identity_key_from_primary_key( 

3144 primary_key_identity, identity_token=identity_token 

3145 ) 

3146 

3147 # work around: https://github.com/python/typing/discussions/1143 

3148 return_value = loading.get_from_identity(self, mapper, key, passive) 

3149 return return_value 

3150 

3151 @util.non_memoized_property 

3152 @contextlib.contextmanager 

3153 def no_autoflush(self) -> Iterator[Session]: 

3154 """Return a context manager that disables autoflush. 

3155 

3156 e.g.:: 

3157 

3158 with session.no_autoflush: 

3159 

3160 some_object = SomeClass() 

3161 session.add(some_object) 

3162 # won't autoflush 

3163 some_object.related_thing = session.query(SomeRelated).first() 

3164 

3165 Operations that proceed within the ``with:`` block 

3166 will not be subject to flushes occurring upon query 

3167 access. This is useful when initializing a series 

3168 of objects which involve existing database queries, 

3169 where the uncompleted object should not yet be flushed. 

3170 

3171 """ 

3172 autoflush = self.autoflush 

3173 self.autoflush = False 

3174 try: 

3175 yield self 

3176 finally: 

3177 self.autoflush = autoflush 

3178 

3179 @util.langhelpers.tag_method_for_warnings( 

3180 "This warning originated from the Session 'autoflush' process, " 

3181 "which was invoked automatically in response to a user-initiated " 

3182 "operation. Consider using ``no_autoflush`` context manager if this " 

3183 "warning happened while initializing objects.", 

3184 sa_exc.SAWarning, 

3185 ) 

3186 def _autoflush(self) -> None: 

3187 if self.autoflush and not self._flushing: 

3188 try: 

3189 self.flush() 

3190 except sa_exc.StatementError as e: 

3191 # note we are reraising StatementError as opposed to 

3192 # raising FlushError with "chaining" to remain compatible 

3193 # with code that catches StatementError, IntegrityError, 

3194 # etc. 

3195 e.add_detail( 

3196 "raised as a result of Query-invoked autoflush; " 

3197 "consider using a session.no_autoflush block if this " 

3198 "flush is occurring prematurely" 

3199 ) 

3200 raise e.with_traceback(sys.exc_info()[2]) 

3201 

3202 def refresh( 

3203 self, 

3204 instance: object, 

3205 attribute_names: Optional[Iterable[str]] = None, 

3206 with_for_update: ForUpdateParameter = None, 

3207 ) -> None: 

3208 """Expire and refresh attributes on the given instance. 

3209 

3210 The selected attributes will first be expired as they would when using 

3211 :meth:`_orm.Session.expire`; then a SELECT statement will be issued to 

3212 the database to refresh column-oriented attributes with the current 

3213 value available in the current transaction. 

3214 

3215 :func:`_orm.relationship` oriented attributes will also be immediately 

3216 loaded if they were already eagerly loaded on the object, using the 

3217 same eager loading strategy that they were loaded with originally. 

3218 

3219 .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method 

3220 can also refresh eagerly loaded attributes. 

3221 

3222 :func:`_orm.relationship` oriented attributes that would normally 

3223 load using the ``select`` (or "lazy") loader strategy will also 

3224 load **if they are named explicitly in the attribute_names 

3225 collection**, emitting a SELECT statement for the attribute using the 

3226 ``immediate`` loader strategy. If lazy-loaded relationships are not 

3227 named in :paramref:`_orm.Session.refresh.attribute_names`, then 

3228 they remain as "lazy loaded" attributes and are not implicitly 

3229 refreshed. 

3230 

3231 .. versionchanged:: 2.0.4 The :meth:`_orm.Session.refresh` method 

3232 will now refresh lazy-loaded :func:`_orm.relationship` oriented 

3233 attributes for those which are named explicitly in the 

3234 :paramref:`_orm.Session.refresh.attribute_names` collection. 

3235 

3236 .. tip:: 

3237 

3238 While the :meth:`_orm.Session.refresh` method is capable of 

3239 refreshing both column and relationship oriented attributes, its 

3240 primary focus is on refreshing of local column-oriented attributes 

3241 on a single instance. For more open ended "refresh" functionality, 

3242 including the ability to refresh the attributes on many objects at 

3243 once while having explicit control over relationship loader 

3244 strategies, use the 

3245 :ref:`populate existing <orm_queryguide_populate_existing>` feature 

3246 instead. 

3247 

3248 Note that a highly isolated transaction will return the same values as 

3249 were previously read in that same transaction, regardless of changes 

3250 in database state outside of that transaction. Refreshing 

3251 attributes usually only makes sense at the start of a transaction 

3252 where database rows have not yet been accessed. 

3253 

3254 :param attribute_names: optional. An iterable collection of 

3255 string attribute names indicating a subset of attributes to 

3256 be refreshed. 

3257 

3258 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 

3259 should be used, or may be a dictionary containing flags to 

3260 indicate a more specific set of FOR UPDATE flags for the SELECT; 

3261 flags should match the parameters of 

3262 :meth:`_query.Query.with_for_update`. 

3263 Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 

3264 

3265 .. seealso:: 

3266 

3267 :ref:`session_expire` - introductory material 

3268 

3269 :meth:`.Session.expire` 

3270 

3271 :meth:`.Session.expire_all` 

3272 

3273 :ref:`orm_queryguide_populate_existing` - allows any ORM query 

3274 to refresh objects as they would be loaded normally. 

3275 

3276 """ 

3277 try: 

3278 state = attributes.instance_state(instance) 

3279 except exc.NO_STATE as err: 

3280 raise exc.UnmappedInstanceError(instance) from err 

3281 

3282 self._expire_state(state, attribute_names) 

3283 

3284 # this autoflush previously used to occur as a secondary effect 

3285 # of the load_on_ident below. Meaning we'd organize the SELECT 

3286 # based on current DB pks, then flush, then if pks changed in that 

3287 # flush, crash. this was unticketed but discovered as part of 

3288 # #8703. So here, autoflush up front, dont autoflush inside 

3289 # load_on_ident. 

3290 self._autoflush() 

3291 

3292 if with_for_update == {}: 

3293 raise sa_exc.ArgumentError( 

3294 "with_for_update should be the boolean value " 

3295 "True, or a dictionary with options. " 

3296 "A blank dictionary is ambiguous." 

3297 ) 

3298 

3299 with_for_update = ForUpdateArg._from_argument(with_for_update) 

3300 

3301 stmt: Select[Unpack[TupleAny]] = sql.select(object_mapper(instance)) 

3302 if ( 

3303 loading._load_on_ident( 

3304 self, 

3305 stmt, 

3306 state.key, 

3307 refresh_state=state, 

3308 with_for_update=with_for_update, 

3309 only_load_props=attribute_names, 

3310 require_pk_cols=True, 

3311 # technically unnecessary as we just did autoflush 

3312 # above, however removes the additional unnecessary 

3313 # call to _autoflush() 

3314 no_autoflush=True, 

3315 is_user_refresh=True, 

3316 ) 

3317 is None 

3318 ): 

3319 raise sa_exc.InvalidRequestError( 

3320 "Could not refresh instance '%s'" % instance_str(instance) 

3321 ) 

3322 

3323 def expire_all(self) -> None: 

3324 """Expires all persistent instances within this Session. 

3325 

3326 When any attributes on a persistent instance is next accessed, 

3327 a query will be issued using the 

3328 :class:`.Session` object's current transactional context in order to 

3329 load all expired attributes for the given instance. Note that 

3330 a highly isolated transaction will return the same values as were 

3331 previously read in that same transaction, regardless of changes 

3332 in database state outside of that transaction. 

3333 

3334 To expire individual objects and individual attributes 

3335 on those objects, use :meth:`Session.expire`. 

3336 

3337 The :class:`.Session` object's default behavior is to 

3338 expire all state whenever the :meth:`Session.rollback` 

3339 or :meth:`Session.commit` methods are called, so that new 

3340 state can be loaded for the new transaction. For this reason, 

3341 calling :meth:`Session.expire_all` is not usually needed, 

3342 assuming the transaction is isolated. 

3343 

3344 .. seealso:: 

3345 

3346 :ref:`session_expire` - introductory material 

3347 

3348 :meth:`.Session.expire` 

3349 

3350 :meth:`.Session.refresh` 

3351 

3352 :meth:`_orm.Query.populate_existing` 

3353 

3354 """ 

3355 for state in self.identity_map.all_states(): 

3356 state._expire(state.dict, self.identity_map._modified) 

3357 

3358 def expire( 

3359 self, instance: object, attribute_names: Optional[Iterable[str]] = None 

3360 ) -> None: 

3361 """Expire the attributes on an instance. 

3362 

3363 Marks the attributes of an instance as out of date. When an expired 

3364 attribute is next accessed, a query will be issued to the 

3365 :class:`.Session` object's current transactional context in order to 

3366 load all expired attributes for the given instance. Note that 

3367 a highly isolated transaction will return the same values as were 

3368 previously read in that same transaction, regardless of changes 

3369 in database state outside of that transaction. 

3370 

3371 To expire all objects in the :class:`.Session` simultaneously, 

3372 use :meth:`Session.expire_all`. 

3373 

3374 The :class:`.Session` object's default behavior is to 

3375 expire all state whenever the :meth:`Session.rollback` 

3376 or :meth:`Session.commit` methods are called, so that new 

3377 state can be loaded for the new transaction. For this reason, 

3378 calling :meth:`Session.expire` only makes sense for the specific 

3379 case that a non-ORM SQL statement was emitted in the current 

3380 transaction. 

3381 

3382 :param instance: The instance to be refreshed. 

3383 :param attribute_names: optional list of string attribute names 

3384 indicating a subset of attributes to be expired. 

3385 

3386 .. seealso:: 

3387 

3388 :ref:`session_expire` - introductory material 

3389 

3390 :meth:`.Session.expire` 

3391 

3392 :meth:`.Session.refresh` 

3393 

3394 :meth:`_orm.Query.populate_existing` 

3395 

3396 """ 

3397 try: 

3398 state = attributes.instance_state(instance) 

3399 except exc.NO_STATE as err: 

3400 raise exc.UnmappedInstanceError(instance) from err 

3401 self._expire_state(state, attribute_names) 

3402 

3403 def _expire_state( 

3404 self, 

3405 state: InstanceState[Any], 

3406 attribute_names: Optional[Iterable[str]], 

3407 ) -> None: 

3408 self._validate_persistent(state) 

3409 if attribute_names: 

3410 state._expire_attributes(state.dict, attribute_names) 

3411 else: 

3412 # pre-fetch the full cascade since the expire is going to 

3413 # remove associations 

3414 cascaded = list( 

3415 state.manager.mapper.cascade_iterator("refresh-expire", state) 

3416 ) 

3417 self._conditional_expire(state) 

3418 for o, m, st_, dct_ in cascaded: 

3419 self._conditional_expire(st_) 

3420 

3421 def _conditional_expire( 

3422 self, state: InstanceState[Any], autoflush: Optional[bool] = None 

3423 ) -> None: 

3424 """Expire a state if persistent, else expunge if pending""" 

3425 

3426 if state.key: 

3427 state._expire(state.dict, self.identity_map._modified) 

3428 elif state in self._new: 

3429 self._new.pop(state) 

3430 state._detach(self) 

3431 

3432 def expunge(self, instance: object) -> None: 

3433 """Remove the `instance` from this ``Session``. 

3434 

3435 This will free all internal references to the instance. Cascading 

3436 will be applied according to the *expunge* cascade rule. 

3437 

3438 """ 

3439 try: 

3440 state = attributes.instance_state(instance) 

3441 except exc.NO_STATE as err: 

3442 raise exc.UnmappedInstanceError(instance) from err 

3443 if state.session_id is not self.hash_key: 

3444 raise sa_exc.InvalidRequestError( 

3445 "Instance %s is not present in this Session" % state_str(state) 

3446 ) 

3447 

3448 cascaded = list( 

3449 state.manager.mapper.cascade_iterator("expunge", state) 

3450 ) 

3451 self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded]) 

3452 

3453 def _expunge_states( 

3454 self, states: Iterable[InstanceState[Any]], to_transient: bool = False 

3455 ) -> None: 

3456 for state in states: 

3457 if state in self._new: 

3458 self._new.pop(state) 

3459 elif self.identity_map.contains_state(state): 

3460 self.identity_map.safe_discard(state) 

3461 self._deleted.pop(state, None) 

3462 elif self._transaction: 

3463 # state is "detached" from being deleted, but still present 

3464 # in the transaction snapshot 

3465 self._transaction._deleted.pop(state, None) 

3466 statelib.InstanceState._detach_states( 

3467 states, self, to_transient=to_transient 

3468 ) 

3469 

3470 def _register_persistent(self, states: Set[InstanceState[Any]]) -> None: 

3471 """Register all persistent objects from a flush. 

3472 

3473 This is used both for pending objects moving to the persistent 

3474 state as well as already persistent objects. 

3475 

3476 """ 

3477 

3478 pending_to_persistent = self.dispatch.pending_to_persistent or None 

3479 for state in states: 

3480 mapper = _state_mapper(state) 

3481 

3482 # prevent against last minute dereferences of the object 

3483 obj = state.obj() 

3484 if obj is not None: 

3485 instance_key = mapper._identity_key_from_state(state) 

3486 

3487 if ( 

3488 _none_set.intersection(instance_key[1]) 

3489 and not mapper.allow_partial_pks 

3490 or _none_set.issuperset(instance_key[1]) 

3491 ): 

3492 raise exc.FlushError( 

3493 "Instance %s has a NULL identity key. If this is an " 

3494 "auto-generated value, check that the database table " 

3495 "allows generation of new primary key values, and " 

3496 "that the mapped Column object is configured to " 

3497 "expect these generated values. Ensure also that " 

3498 "this flush() is not occurring at an inappropriate " 

3499 "time, such as within a load() event." 

3500 % state_str(state) 

3501 ) 

3502 

3503 if state.key is None: 

3504 state.key = instance_key 

3505 elif state.key != instance_key: 

3506 # primary key switch. use safe_discard() in case another 

3507 # state has already replaced this one in the identity 

3508 # map (see test/orm/test_naturalpks.py ReversePKsTest) 

3509 self.identity_map.safe_discard(state) 

3510 trans = self._transaction 

3511 assert trans is not None 

3512 if state in trans._key_switches: 

3513 orig_key = trans._key_switches[state][0] 

3514 else: 

3515 orig_key = state.key 

3516 trans._key_switches[state] = ( 

3517 orig_key, 

3518 instance_key, 

3519 ) 

3520 state.key = instance_key 

3521 

3522 # there can be an existing state in the identity map 

3523 # that is replaced when the primary keys of two instances 

3524 # are swapped; see test/orm/test_naturalpks.py -> test_reverse 

3525 old = self.identity_map.replace(state) 

3526 

3527 # check the replaced state's identity without emitting 

3528 # any SQL; an expired state such as one left over from 

3529 # a rollback will not match and does not warn 

3530 if ( 

3531 old is not None 

3532 and old.obj() is not None 

3533 and mapper._identity_key_from_state( 

3534 old, PassiveFlag.PASSIVE_NO_FETCH 

3535 ) 

3536 == instance_key 

3537 ): 

3538 util.warn( 

3539 "Identity map already had an identity for %s, " 

3540 "replacing it with newly flushed object. Are there " 

3541 "load operations occurring inside of an event handler " 

3542 "within the flush?" % (instance_key,) 

3543 ) 

3544 state._orphaned_outside_of_session = False 

3545 

3546 statelib.InstanceState._commit_all_states( 

3547 ((state, state.dict) for state in states), self.identity_map 

3548 ) 

3549 

3550 self._register_altered(states) 

3551 

3552 if pending_to_persistent is not None: 

3553 for state in states.intersection(self._new): 

3554 pending_to_persistent(self, state) 

3555 

3556 # remove from new last, might be the last strong ref 

3557 for state in set(states).intersection(self._new): 

3558 self._new.pop(state) 

3559 

3560 def _register_altered(self, states: Iterable[InstanceState[Any]]) -> None: 

3561 if self._transaction: 

3562 for state in states: 

3563 if state in self._new: 

3564 self._transaction._new[state] = True 

3565 else: 

3566 self._transaction._dirty[state] = True 

3567 

3568 def _remove_newly_deleted( 

3569 self, states: Iterable[InstanceState[Any]] 

3570 ) -> None: 

3571 persistent_to_deleted = self.dispatch.persistent_to_deleted or None 

3572 for state in states: 

3573 if self._transaction: 

3574 self._transaction._deleted[state] = True 

3575 

3576 if persistent_to_deleted is not None: 

3577 # get a strong reference before we pop out of 

3578 # self._deleted 

3579 obj = state.obj() # noqa 

3580 

3581 self.identity_map.safe_discard(state) 

3582 self._deleted.pop(state, None) 

3583 state._deleted = True 

3584 # can't call state._detach() here, because this state 

3585 # is still in the transaction snapshot and needs to be 

3586 # tracked as part of that 

3587 if persistent_to_deleted is not None: 

3588 persistent_to_deleted(self, state) 

3589 

3590 def add(self, instance: object, *, _warn: bool = True) -> None: 

3591 """Place an object into this :class:`_orm.Session`. 

3592 

3593 Objects that are in the :term:`transient` state when passed to the 

3594 :meth:`_orm.Session.add` method will move to the 

3595 :term:`pending` state, until the next flush, at which point they 

3596 will move to the :term:`persistent` state. 

3597 

3598 Objects that are in the :term:`detached` state when passed to the 

3599 :meth:`_orm.Session.add` method will move to the :term:`persistent` 

3600 state directly. 

3601 

3602 If the transaction used by the :class:`_orm.Session` is rolled back, 

3603 objects which were transient when they were passed to 

3604 :meth:`_orm.Session.add` will be moved back to the 

3605 :term:`transient` state, and will no longer be present within this 

3606 :class:`_orm.Session`. 

3607 

3608 .. seealso:: 

3609 

3610 :meth:`_orm.Session.add_all` 

3611 

3612 :ref:`session_adding` - at :ref:`session_basics` 

3613 

3614 """ 

3615 if _warn and self._warn_on_events: 

3616 self._flush_warning("Session.add()") 

3617 

3618 try: 

3619 state = attributes.instance_state(instance) 

3620 except exc.NO_STATE as err: 

3621 raise exc.UnmappedInstanceError(instance) from err 

3622 

3623 self._save_or_update_state(state) 

3624 

3625 def add_all(self, instances: Iterable[object]) -> None: 

3626 """Add the given collection of instances to this :class:`_orm.Session`. 

3627 

3628 See the documentation for :meth:`_orm.Session.add` for a general 

3629 behavioral description. 

3630 

3631 .. seealso:: 

3632 

3633 :meth:`_orm.Session.add` 

3634 

3635 :ref:`session_adding` - at :ref:`session_basics` 

3636 

3637 """ 

3638 

3639 if self._warn_on_events: 

3640 self._flush_warning("Session.add_all()") 

3641 

3642 for instance in instances: 

3643 self.add(instance, _warn=False) 

3644 

3645 def _save_or_update_state(self, state: InstanceState[Any]) -> None: 

3646 state._orphaned_outside_of_session = False 

3647 self._save_or_update_impl(state) 

3648 

3649 mapper = _state_mapper(state) 

3650 for o, m, st_, dct_ in mapper.cascade_iterator( 

3651 "save-update", state, halt_on=self._contains_state 

3652 ): 

3653 self._save_or_update_impl(st_) 

3654 

3655 def delete(self, instance: object) -> None: 

3656 """Mark an instance as deleted. 

3657 

3658 The object is assumed to be either :term:`persistent` or 

3659 :term:`detached` when passed; after the method is called, the 

3660 object will remain in the :term:`persistent` state until the next 

3661 flush proceeds. During this time, the object will also be a member 

3662 of the :attr:`_orm.Session.deleted` collection. 

3663 

3664 When the next flush proceeds, the object will move to the 

3665 :term:`deleted` state, indicating a ``DELETE`` statement was emitted 

3666 for its row within the current transaction. When the transaction 

3667 is successfully committed, 

3668 the deleted object is moved to the :term:`detached` state and is 

3669 no longer present within this :class:`_orm.Session`. 

3670 

3671 .. seealso:: 

3672 

3673 :ref:`session_deleting` - at :ref:`session_basics` 

3674 

3675 :meth:`.Session.delete_all` - multiple instance version 

3676 

3677 """ 

3678 if self._warn_on_events: 

3679 self._flush_warning("Session.delete()") 

3680 

3681 self._delete_impl(object_state(instance), instance, head=True) 

3682 

3683 def delete_all(self, instances: Iterable[object]) -> None: 

3684 """Calls :meth:`.Session.delete` on multiple instances. 

3685 

3686 .. seealso:: 

3687 

3688 :meth:`.Session.delete` - main documentation on delete 

3689 

3690 .. versionadded:: 2.1 

3691 

3692 """ 

3693 

3694 if self._warn_on_events: 

3695 self._flush_warning("Session.delete_all()") 

3696 

3697 for instance in instances: 

3698 self._delete_impl(object_state(instance), instance, head=True) 

3699 

3700 def _delete_impl( 

3701 self, state: InstanceState[Any], obj: object, head: bool 

3702 ) -> None: 

3703 if state.key is None: 

3704 if head: 

3705 raise sa_exc.InvalidRequestError( 

3706 "Instance '%s' is not persisted" % state_str(state) 

3707 ) 

3708 else: 

3709 return 

3710 

3711 to_attach = self._before_attach(state, obj) 

3712 

3713 if state in self._deleted: 

3714 return 

3715 

3716 self.identity_map.add(state) 

3717 

3718 if to_attach: 

3719 self._after_attach(state, obj) 

3720 

3721 if head: 

3722 # grab the cascades before adding the item to the deleted list 

3723 # so that autoflush does not delete the item 

3724 # the strong reference to the instance itself is significant here 

3725 cascade_states = list( 

3726 state.manager.mapper.cascade_iterator("delete", state) 

3727 ) 

3728 else: 

3729 cascade_states = None 

3730 

3731 self._deleted[state] = obj 

3732 

3733 if head: 

3734 if TYPE_CHECKING: 

3735 assert cascade_states is not None 

3736 for o, m, st_, dct_ in cascade_states: 

3737 self._delete_impl(st_, o, False) 

3738 

3739 def get( 

3740 self, 

3741 entity: _EntityBindKey[_O], 

3742 ident: _PKIdentityArgument, 

3743 *, 

3744 options: Optional[Sequence[ORMOption]] = None, 

3745 populate_existing: bool | None = None, 

3746 with_for_update: ForUpdateParameter = None, 

3747 identity_token: Optional[Any] = None, 

3748 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

3749 bind_arguments: Optional[_BindArguments] = None, 

3750 ) -> Optional[_O]: 

3751 """Return an instance based on the given primary key identifier, 

3752 or ``None`` if not found. 

3753 

3754 E.g.:: 

3755 

3756 my_user = session.get(User, 5) 

3757 

3758 some_object = session.get(VersionedFoo, (5, 10)) 

3759 

3760 some_object = session.get(VersionedFoo, {"id": 5, "version_id": 10}) 

3761 

3762 .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved 

3763 from the now legacy :meth:`_orm.Query.get` method. 

3764 

3765 :meth:`_orm.Session.get` is special in that it provides direct 

3766 access to the identity map of the :class:`.Session`. 

3767 If the given primary key identifier is present 

3768 in the local identity map, the object is returned 

3769 directly from this collection and no SQL is emitted, 

3770 unless the object has been marked fully expired. 

3771 If not present, 

3772 a SELECT is performed in order to locate the object. 

3773 

3774 :meth:`_orm.Session.get` also will perform a check if 

3775 the object is present in the identity map and 

3776 marked as expired - a SELECT 

3777 is emitted to refresh the object as well as to 

3778 ensure that the row is still present. 

3779 If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3780 

3781 :param entity: a mapped class or :class:`.Mapper` indicating the 

3782 type of entity to be loaded. 

3783 

3784 :param ident: A scalar, tuple, or dictionary representing the 

3785 primary key. For a composite (e.g. multiple column) primary key, 

3786 a tuple or dictionary should be passed. 

3787 

3788 For a single-column primary key, the scalar calling form is typically 

3789 the most expedient. If the primary key of a row is the value "5", 

3790 the call looks like:: 

3791 

3792 my_object = session.get(SomeClass, 5) 

3793 

3794 The tuple form contains primary key values typically in 

3795 the order in which they correspond to the mapped 

3796 :class:`_schema.Table` 

3797 object's primary key columns, or if the 

3798 :paramref:`_orm.Mapper.primary_key` configuration parameter were 

3799 used, in 

3800 the order used for that parameter. For example, if the primary key 

3801 of a row is represented by the integer 

3802 digits "5, 10" the call would look like:: 

3803 

3804 my_object = session.get(SomeClass, (5, 10)) 

3805 

3806 The dictionary form should include as keys the mapped attribute names 

3807 corresponding to each element of the primary key. If the mapped class 

3808 has the attributes ``id``, ``version_id`` as the attributes which 

3809 store the object's primary key value, the call would look like:: 

3810 

3811 my_object = session.get(SomeClass, {"id": 5, "version_id": 10}) 

3812 

3813 :param options: optional sequence of loader options which will be 

3814 applied to the query, if one is emitted. 

3815 

3816 :param populate_existing: causes the method to unconditionally emit 

3817 a SQL query and refresh the object with the newly loaded data, 

3818 regardless of whether or not the object is already present. 

3819 Setting this flag takes precedence over passing it as an 

3820 execution option. 

3821 

3822 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 

3823 should be used, or may be a dictionary containing flags to 

3824 indicate a more specific set of FOR UPDATE flags for the SELECT; 

3825 flags should match the parameters of 

3826 :meth:`_query.Query.with_for_update`. 

3827 Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 

3828 

3829 :param execution_options: optional dictionary of execution options, 

3830 which will be associated with the query execution if one is emitted. 

3831 This dictionary can provide a subset of the options that are 

3832 accepted by :meth:`_engine.Connection.execution_options`, and may 

3833 also provide additional options understood only in an ORM context. 

3834 

3835 .. versionadded:: 1.4.29 

3836 

3837 .. seealso:: 

3838 

3839 :ref:`orm_queryguide_execution_options` - ORM-specific execution 

3840 options 

3841 

3842 :param bind_arguments: dictionary of additional arguments to determine 

3843 the bind. May include "mapper", "bind", or other custom arguments. 

3844 Contents of this dictionary are passed to the 

3845 :meth:`.Session.get_bind` method. 

3846 

3847 .. versionadded:: 2.0.0rc1 

3848 

3849 :return: The object instance, or ``None``. 

3850 

3851 """ # noqa: E501 

3852 return self._get_impl( 

3853 entity, 

3854 ident, 

3855 loading._load_on_pk_identity, 

3856 options=options, 

3857 populate_existing=populate_existing, 

3858 with_for_update=with_for_update, 

3859 identity_token=identity_token, 

3860 execution_options=execution_options, 

3861 bind_arguments=bind_arguments, 

3862 ) 

3863 

3864 def get_one( 

3865 self, 

3866 entity: _EntityBindKey[_O], 

3867 ident: _PKIdentityArgument, 

3868 *, 

3869 options: Optional[Sequence[ORMOption]] = None, 

3870 populate_existing: bool | None = None, 

3871 with_for_update: ForUpdateParameter = None, 

3872 identity_token: Optional[Any] = None, 

3873 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

3874 bind_arguments: Optional[_BindArguments] = None, 

3875 ) -> _O: 

3876 """Return exactly one instance based on the given primary key 

3877 identifier, or raise an exception if not found. 

3878 

3879 Raises :class:`_exc.NoResultFound` if the query selects no rows. 

3880 

3881 For a detailed documentation of the arguments see the 

3882 method :meth:`.Session.get`. 

3883 

3884 .. versionadded:: 2.0.22 

3885 

3886 :return: The object instance. 

3887 

3888 .. seealso:: 

3889 

3890 :meth:`.Session.get` - equivalent method that instead 

3891 returns ``None`` if no row was found with the provided primary 

3892 key 

3893 

3894 """ 

3895 

3896 instance = self.get( 

3897 entity, 

3898 ident, 

3899 options=options, 

3900 populate_existing=populate_existing, 

3901 with_for_update=with_for_update, 

3902 identity_token=identity_token, 

3903 execution_options=execution_options, 

3904 bind_arguments=bind_arguments, 

3905 ) 

3906 

3907 if instance is None: 

3908 raise sa_exc.NoResultFound( 

3909 "No row was found when one was required" 

3910 ) 

3911 

3912 return instance 

3913 

3914 def _get_impl( 

3915 self, 

3916 entity: _EntityBindKey[_O], 

3917 primary_key_identity: _PKIdentityArgument, 

3918 db_load_fn: Callable[..., _O], 

3919 *, 

3920 options: Optional[Sequence[ExecutableOption]] = None, 

3921 populate_existing: bool | None = None, 

3922 with_for_update: ForUpdateParameter = None, 

3923 identity_token: Optional[Any] = None, 

3924 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT, 

3925 bind_arguments: Optional[_BindArguments] = None, 

3926 ) -> Optional[_O]: 

3927 # set populate_existing value; direct parameter 

3928 # takes precedence over execution_options 

3929 if populate_existing is not None: 

3930 execution_options = { 

3931 **execution_options, 

3932 "populate_existing": populate_existing, 

3933 } 

3934 else: 

3935 populate_existing = execution_options.get( 

3936 "populate_existing", False 

3937 ) 

3938 

3939 # convert composite types to individual args 

3940 if ( 

3941 is_composite_class(primary_key_identity) 

3942 and type(primary_key_identity) 

3943 in descriptor_props._composite_getters 

3944 ): 

3945 getter = descriptor_props._composite_getters[ 

3946 type(primary_key_identity) 

3947 ] 

3948 primary_key_identity = getter(primary_key_identity) 

3949 

3950 mapper: Optional[Mapper[_O]] = inspect(entity) 

3951 

3952 if mapper is None or not mapper.is_mapper: 

3953 raise sa_exc.ArgumentError( 

3954 "Expected mapped class or mapper, got: %r" % entity 

3955 ) 

3956 

3957 is_dict = isinstance(primary_key_identity, dict) 

3958 if not is_dict: 

3959 primary_key_identity = util.to_list( 

3960 primary_key_identity, default=[None] 

3961 ) 

3962 

3963 if len(primary_key_identity) != len(mapper.primary_key): 

3964 raise sa_exc.InvalidRequestError( 

3965 "Incorrect number of values in identifier to formulate " 

3966 "primary key for session.get(); primary key columns " 

3967 "are %s" % ",".join("'%s'" % c for c in mapper.primary_key) 

3968 ) 

3969 

3970 if is_dict: 

3971 pk_synonyms = mapper._pk_synonyms 

3972 

3973 if pk_synonyms: 

3974 correct_keys = set(pk_synonyms).intersection( 

3975 primary_key_identity 

3976 ) 

3977 

3978 if correct_keys: 

3979 primary_key_identity = dict(primary_key_identity) 

3980 for k in correct_keys: 

3981 primary_key_identity[pk_synonyms[k]] = ( 

3982 primary_key_identity[k] 

3983 ) 

3984 

3985 try: 

3986 primary_key_identity = list( 

3987 primary_key_identity[prop.key] 

3988 for prop in mapper._identity_key_props 

3989 ) 

3990 

3991 except KeyError as err: 

3992 raise sa_exc.InvalidRequestError( 

3993 "Incorrect names of values in identifier to formulate " 

3994 "primary key for session.get(); primary key attribute " 

3995 "names are %s (synonym names are also accepted)" 

3996 % ",".join( 

3997 "'%s'" % prop.key 

3998 for prop in mapper._identity_key_props 

3999 ) 

4000 ) from err 

4001 

4002 for_update_arg = ForUpdateArg._from_argument(with_for_update) 

4003 

4004 if ( 

4005 not populate_existing 

4006 and not mapper.always_refresh 

4007 and for_update_arg is None 

4008 ): 

4009 instance = self._identity_lookup( 

4010 mapper, 

4011 primary_key_identity, 

4012 identity_token=identity_token, 

4013 execution_options=execution_options, 

4014 bind_arguments=bind_arguments, 

4015 ) 

4016 

4017 if instance is not None: 

4018 # reject calls for id in identity map but class 

4019 # mismatch. 

4020 if not isinstance(instance, mapper.class_): 

4021 return None 

4022 return instance 

4023 

4024 # TODO: this was being tested before, but this is not possible 

4025 assert instance is not LoaderCallableStatus.PASSIVE_CLASS_MISMATCH 

4026 

4027 load_options = context.QueryContext.default_load_options 

4028 

4029 if populate_existing: 

4030 load_options += {"_populate_existing": populate_existing} 

4031 statement = sql.select(mapper) 

4032 if for_update_arg is not None: 

4033 statement._for_update_arg = for_update_arg 

4034 

4035 if options: 

4036 statement = statement.options(*options) 

4037 if self.execution_options: 

4038 execution_options = self.execution_options.union(execution_options) 

4039 return db_load_fn( 

4040 self, 

4041 statement, 

4042 primary_key_identity, 

4043 load_options=load_options, 

4044 identity_token=identity_token, 

4045 execution_options=execution_options, 

4046 bind_arguments=bind_arguments, 

4047 ) 

4048 

4049 def merge( 

4050 self, 

4051 instance: _O, 

4052 *, 

4053 load: bool = True, 

4054 options: Optional[Sequence[ORMOption]] = None, 

4055 ) -> _O: 

4056 """Copy the state of a given instance into a corresponding instance 

4057 within this :class:`.Session`. 

4058 

4059 :meth:`.Session.merge` examines the primary key attributes of the 

4060 source instance, and attempts to reconcile it with an instance of the 

4061 same primary key in the session. If not found locally, it attempts 

4062 to load the object from the database based on primary key, and if 

4063 none can be located, creates a new instance. The state of each 

4064 attribute on the source instance is then copied to the target 

4065 instance. The resulting target instance is then returned by the 

4066 method; the original source instance is left unmodified, and 

4067 un-associated with the :class:`.Session` if not already. 

4068 

4069 This operation cascades to associated instances if the association is 

4070 mapped with ``cascade="merge"``. 

4071 

4072 See :ref:`unitofwork_merging` for a detailed discussion of merging. 

4073 

4074 :param instance: Instance to be merged. 

4075 :param load: Boolean, when False, :meth:`.merge` switches into 

4076 a "high performance" mode which causes it to forego emitting history 

4077 events as well as all database access. This flag is used for 

4078 cases such as transferring graphs of objects into a :class:`.Session` 

4079 from a second level cache, or to transfer just-loaded objects 

4080 into the :class:`.Session` owned by a worker thread or process 

4081 without re-querying the database. 

4082 

4083 The ``load=False`` use case adds the caveat that the given 

4084 object has to be in a "clean" state, that is, has no pending changes 

4085 to be flushed - even if the incoming object is detached from any 

4086 :class:`.Session`. This is so that when 

4087 the merge operation populates local attributes and 

4088 cascades to related objects and 

4089 collections, the values can be "stamped" onto the 

4090 target object as is, without generating any history or attribute 

4091 events, and without the need to reconcile the incoming data with 

4092 any existing related objects or collections that might not 

4093 be loaded. The resulting objects from ``load=False`` are always 

4094 produced as "clean", so it is only appropriate that the given objects 

4095 should be "clean" as well, else this suggests a mis-use of the 

4096 method. 

4097 :param options: optional sequence of loader options which will be 

4098 applied to the :meth:`_orm.Session.get` method when the merge 

4099 operation loads the existing version of the object from the database. 

4100 

4101 .. versionadded:: 1.4.24 

4102 

4103 

4104 .. seealso:: 

4105 

4106 :func:`.make_transient_to_detached` - provides for an alternative 

4107 means of "merging" a single object into the :class:`.Session` 

4108 

4109 :meth:`.Session.merge_all` - multiple instance version 

4110 

4111 """ 

4112 

4113 if self._warn_on_events: 

4114 self._flush_warning("Session.merge()") 

4115 

4116 if load: 

4117 # flush current contents if we expect to load data 

4118 self._autoflush() 

4119 

4120 with self.no_autoflush: 

4121 return self._merge( 

4122 object_state(instance), 

4123 attributes.instance_dict(instance), 

4124 load=load, 

4125 options=options, 

4126 _recursive={}, 

4127 _resolve_conflict_map={}, 

4128 ) 

4129 

4130 def merge_all( 

4131 self, 

4132 instances: Iterable[_O], 

4133 *, 

4134 load: bool = True, 

4135 options: Optional[Sequence[ORMOption]] = None, 

4136 ) -> Sequence[_O]: 

4137 """Calls :meth:`.Session.merge` on multiple instances. 

4138 

4139 .. seealso:: 

4140 

4141 :meth:`.Session.merge` - main documentation on merge 

4142 

4143 .. versionadded:: 2.1 

4144 

4145 """ 

4146 

4147 if self._warn_on_events: 

4148 self._flush_warning("Session.merge_all()") 

4149 

4150 if load: 

4151 # flush current contents if we expect to load data 

4152 self._autoflush() 

4153 

4154 with self.no_autoflush: 

4155 return [ 

4156 self._merge( 

4157 object_state(instance), 

4158 attributes.instance_dict(instance), 

4159 load=load, 

4160 options=options, 

4161 _recursive={}, 

4162 _resolve_conflict_map={}, 

4163 ) 

4164 for instance in instances 

4165 ] 

4166 

4167 def _merge( 

4168 self, 

4169 state: InstanceState[_O], 

4170 state_dict: _InstanceDict, 

4171 *, 

4172 options: Optional[Sequence[ORMOption]] = None, 

4173 load: bool, 

4174 _recursive: Dict[Any, object], 

4175 _resolve_conflict_map: Dict[_IdentityKeyType[Any], object], 

4176 ) -> _O: 

4177 mapper: Mapper[_O] = _state_mapper(state) 

4178 if state in _recursive: 

4179 return cast(_O, _recursive[state]) 

4180 

4181 new_instance = False 

4182 key = state.key 

4183 

4184 merged: Optional[_O] 

4185 

4186 if key is None: 

4187 if state in self._new: 

4188 util.warn( 

4189 "Instance %s is already pending in this Session yet is " 

4190 "being merged again; this is probably not what you want " 

4191 "to do" % state_str(state) 

4192 ) 

4193 

4194 if not load: 

4195 raise sa_exc.InvalidRequestError( 

4196 "merge() with load=False option does not support " 

4197 "objects transient (i.e. unpersisted) objects. flush() " 

4198 "all changes on mapped instances before merging with " 

4199 "load=False." 

4200 ) 

4201 key = mapper._identity_key_from_state(state) 

4202 key_is_persistent = LoaderCallableStatus.NEVER_SET not in key[ 

4203 1 

4204 ] and ( 

4205 not _none_set.intersection(key[1]) 

4206 or ( 

4207 mapper.allow_partial_pks 

4208 and not _none_set.issuperset(key[1]) 

4209 ) 

4210 ) 

4211 else: 

4212 key_is_persistent = True 

4213 

4214 merged = self.identity_map.get(key) 

4215 

4216 if merged is None: 

4217 if key_is_persistent and key in _resolve_conflict_map: 

4218 merged = cast(_O, _resolve_conflict_map[key]) 

4219 

4220 elif not load: 

4221 if state.modified: 

4222 raise sa_exc.InvalidRequestError( 

4223 "merge() with load=False option does not support " 

4224 "objects marked as 'dirty'. flush() all changes on " 

4225 "mapped instances before merging with load=False." 

4226 ) 

4227 merged = mapper.class_manager.new_instance() 

4228 merged_state = attributes.instance_state(merged) 

4229 merged_state.key = key 

4230 self._update_impl(merged_state) 

4231 new_instance = True 

4232 

4233 elif key_is_persistent: 

4234 merged = self.get( 

4235 mapper.class_, 

4236 key[1], 

4237 identity_token=key[2], 

4238 options=options, 

4239 ) 

4240 

4241 if merged is None: 

4242 merged = mapper.class_manager.new_instance() 

4243 merged_state = attributes.instance_state(merged) 

4244 merged_dict = attributes.instance_dict(merged) 

4245 new_instance = True 

4246 self._save_or_update_state(merged_state) 

4247 else: 

4248 merged_state = attributes.instance_state(merged) 

4249 merged_dict = attributes.instance_dict(merged) 

4250 

4251 _recursive[state] = merged 

4252 _resolve_conflict_map[key] = merged 

4253 

4254 # check that we didn't just pull the exact same 

4255 # state out. 

4256 if state is not merged_state: 

4257 # version check if applicable 

4258 if mapper.version_id_col is not None: 

4259 existing_version = mapper._get_state_attr_by_column( 

4260 state, 

4261 state_dict, 

4262 mapper.version_id_col, 

4263 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

4264 ) 

4265 

4266 merged_version = mapper._get_state_attr_by_column( 

4267 merged_state, 

4268 merged_dict, 

4269 mapper.version_id_col, 

4270 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

4271 ) 

4272 

4273 if ( 

4274 existing_version 

4275 is not LoaderCallableStatus.PASSIVE_NO_RESULT 

4276 and merged_version 

4277 is not LoaderCallableStatus.PASSIVE_NO_RESULT 

4278 and existing_version != merged_version 

4279 ): 

4280 raise exc.StaleDataError( 

4281 "Version id '%s' on merged state %s " 

4282 "does not match existing version '%s'. " 

4283 "Leave the version attribute unset when " 

4284 "merging to update the most recent version." 

4285 % ( 

4286 existing_version, 

4287 state_str(merged_state), 

4288 merged_version, 

4289 ) 

4290 ) 

4291 

4292 merged_state.load_path = state.load_path 

4293 merged_state.load_options = state.load_options 

4294 

4295 # since we are copying load_options, we need to copy 

4296 # the callables_ that would have been generated by those 

4297 # load_options. 

4298 # assumes that the callables we put in state.callables_ 

4299 # are not instance-specific (which they should not be) 

4300 merged_state._copy_callables(state) 

4301 

4302 for prop in mapper.iterate_properties: 

4303 prop.merge( 

4304 self, 

4305 state, 

4306 state_dict, 

4307 merged_state, 

4308 merged_dict, 

4309 load, 

4310 _recursive, 

4311 _resolve_conflict_map, 

4312 ) 

4313 

4314 if not load: 

4315 # remove any history 

4316 merged_state._commit_all(merged_dict, self.identity_map) 

4317 merged_state.manager.dispatch._sa_event_merge_wo_load( 

4318 merged_state, None 

4319 ) 

4320 

4321 if new_instance: 

4322 merged_state.manager.dispatch.load(merged_state, None) 

4323 

4324 return merged 

4325 

4326 def _validate_persistent(self, state: InstanceState[Any]) -> None: 

4327 if not self.identity_map.contains_state(state): 

4328 raise sa_exc.InvalidRequestError( 

4329 "Instance '%s' is not persistent within this Session" 

4330 % state_str(state) 

4331 ) 

4332 

4333 def _save_impl(self, state: InstanceState[Any]) -> None: 

4334 if state.key is not None: 

4335 raise sa_exc.InvalidRequestError( 

4336 "Object '%s' already has an identity - " 

4337 "it can't be registered as pending" % state_str(state) 

4338 ) 

4339 

4340 obj = state.obj() 

4341 to_attach = self._before_attach(state, obj) 

4342 if state not in self._new: 

4343 self._new[state] = obj 

4344 state.insert_order = len(self._new) 

4345 if to_attach: 

4346 self._after_attach(state, obj) 

4347 

4348 def _update_impl( 

4349 self, state: InstanceState[Any], revert_deletion: bool = False 

4350 ) -> None: 

4351 if state.key is None: 

4352 raise sa_exc.InvalidRequestError( 

4353 "Instance '%s' is not persisted" % state_str(state) 

4354 ) 

4355 

4356 if state._deleted: 

4357 if revert_deletion: 

4358 if not state._attached: 

4359 return 

4360 del state._deleted 

4361 else: 

4362 raise sa_exc.InvalidRequestError( 

4363 "Instance '%s' has been deleted. " 

4364 "Use the make_transient() " 

4365 "function to send this object back " 

4366 "to the transient state." % state_str(state) 

4367 ) 

4368 

4369 obj = state.obj() 

4370 

4371 # check for late gc 

4372 if obj is None: 

4373 return 

4374 

4375 to_attach = self._before_attach(state, obj) 

4376 

4377 self._deleted.pop(state, None) 

4378 if revert_deletion: 

4379 self.identity_map.replace(state) 

4380 else: 

4381 self.identity_map.add(state) 

4382 

4383 if to_attach: 

4384 self._after_attach(state, obj) 

4385 elif revert_deletion: 

4386 self.dispatch.deleted_to_persistent(self, state) 

4387 

4388 def _save_or_update_impl(self, state: InstanceState[Any]) -> None: 

4389 if state.key is None: 

4390 self._save_impl(state) 

4391 else: 

4392 self._update_impl(state) 

4393 

4394 def enable_relationship_loading(self, obj: object) -> None: 

4395 """Associate an object with this :class:`.Session` for related 

4396 object loading. 

4397 

4398 .. warning:: 

4399 

4400 :meth:`.enable_relationship_loading` exists to serve special 

4401 use cases and is not recommended for general use. 

4402 

4403 Accesses of attributes mapped with :func:`_orm.relationship` 

4404 will attempt to load a value from the database using this 

4405 :class:`.Session` as the source of connectivity. The values 

4406 will be loaded based on foreign key and primary key values 

4407 present on this object - if not present, then those relationships 

4408 will be unavailable. 

4409 

4410 The object will be attached to this session, but will 

4411 **not** participate in any persistence operations; its state 

4412 for almost all purposes will remain either "transient" or 

4413 "detached", except for the case of relationship loading. 

4414 

4415 Also note that backrefs will often not work as expected. 

4416 Altering a relationship-bound attribute on the target object 

4417 may not fire off a backref event, if the effective value 

4418 is what was already loaded from a foreign-key-holding value. 

4419 

4420 The :meth:`.Session.enable_relationship_loading` method is 

4421 similar to the ``load_on_pending`` flag on :func:`_orm.relationship`. 

4422 Unlike that flag, :meth:`.Session.enable_relationship_loading` allows 

4423 an object to remain transient while still being able to load 

4424 related items. 

4425 

4426 To make a transient object associated with a :class:`.Session` 

4427 via :meth:`.Session.enable_relationship_loading` pending, add 

4428 it to the :class:`.Session` using :meth:`.Session.add` normally. 

4429 If the object instead represents an existing identity in the database, 

4430 it should be merged using :meth:`.Session.merge`. 

4431 

4432 :meth:`.Session.enable_relationship_loading` does not improve 

4433 behavior when the ORM is used normally - object references should be 

4434 constructed at the object level, not at the foreign key level, so 

4435 that they are present in an ordinary way before flush() 

4436 proceeds. This method is not intended for general use. 

4437 

4438 .. seealso:: 

4439 

4440 :paramref:`_orm.relationship.load_on_pending` - this flag 

4441 allows per-relationship loading of many-to-ones on items that 

4442 are pending. 

4443 

4444 :func:`.make_transient_to_detached` - allows for an object to 

4445 be added to a :class:`.Session` without SQL emitted, which then 

4446 will unexpire attributes on access. 

4447 

4448 """ 

4449 try: 

4450 state = attributes.instance_state(obj) 

4451 except exc.NO_STATE as err: 

4452 raise exc.UnmappedInstanceError(obj) from err 

4453 

4454 to_attach = self._before_attach(state, obj) 

4455 state._load_pending = True 

4456 if to_attach: 

4457 self._after_attach(state, obj) 

4458 

4459 def _before_attach(self, state: InstanceState[Any], obj: object) -> bool: 

4460 self._autobegin_t() 

4461 

4462 if state.session_id == self.hash_key: 

4463 return False 

4464 

4465 if state.session_id and state.session_id in _sessions: 

4466 raise sa_exc.InvalidRequestError( 

4467 "Object '%s' is already attached to session '%s' " 

4468 "(this is '%s')" 

4469 % (state_str(state), state.session_id, self.hash_key) 

4470 ) 

4471 

4472 self.dispatch.before_attach(self, state) 

4473 

4474 return True 

4475 

4476 def _after_attach(self, state: InstanceState[Any], obj: object) -> None: 

4477 state.session_id = self.hash_key 

4478 if state.modified and state._strong_obj is None: 

4479 state._strong_obj = obj 

4480 self.dispatch.after_attach(self, state) 

4481 

4482 if state.key: 

4483 self.dispatch.detached_to_persistent(self, state) 

4484 else: 

4485 self.dispatch.transient_to_pending(self, state) 

4486 

4487 def __contains__(self, instance: object) -> bool: 

4488 """Return True if the instance is associated with this session. 

4489 

4490 The instance may be pending or persistent within the Session for a 

4491 result of True. 

4492 

4493 """ 

4494 try: 

4495 state = attributes.instance_state(instance) 

4496 except exc.NO_STATE as err: 

4497 raise exc.UnmappedInstanceError(instance) from err 

4498 return self._contains_state(state) 

4499 

4500 def __iter__(self) -> Iterator[object]: 

4501 """Iterate over all pending or persistent instances within this 

4502 Session. 

4503 

4504 """ 

4505 return iter( 

4506 list(self._new.values()) + list(self.identity_map.values()) 

4507 ) 

4508 

4509 def _contains_state(self, state: InstanceState[Any]) -> bool: 

4510 return state in self._new or self.identity_map.contains_state(state) 

4511 

4512 def flush(self, objects: Optional[Sequence[Any]] = None) -> None: 

4513 """Flush all the object changes to the database. 

4514 

4515 Writes out all pending object creations, deletions and modifications 

4516 to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are 

4517 automatically ordered by the Session's unit of work dependency 

4518 solver. 

4519 

4520 Database operations will be issued in the current transactional 

4521 context and do not affect the state of the transaction, unless an 

4522 error occurs, in which case the entire transaction is rolled back. 

4523 You may flush() as often as you like within a transaction to move 

4524 changes from Python to the database's transaction buffer. 

4525 

4526 :param objects: Optional; restricts the flush operation to operate 

4527 only on elements that are in the given collection. 

4528 

4529 This feature is for an extremely narrow set of use cases where 

4530 particular objects may need to be operated upon before the 

4531 full flush() occurs. It is not intended for general use. 

4532 

4533 .. deprecated:: 2.1 

4534 

4535 """ 

4536 

4537 if self._flushing: 

4538 raise sa_exc.InvalidRequestError("Session is already flushing") 

4539 

4540 if self._is_clean(): 

4541 return 

4542 try: 

4543 self._flushing = True 

4544 self._flush(objects) 

4545 finally: 

4546 self._flushing = False 

4547 

4548 def _flush_warning(self, method: Any) -> None: 

4549 util.warn( 

4550 "Usage of the '%s' operation is not currently supported " 

4551 "within the execution stage of the flush process. " 

4552 "Results may not be consistent. Consider using alternative " 

4553 "event listeners or connection-level operations instead." % method 

4554 ) 

4555 

4556 def _is_clean(self) -> bool: 

4557 return ( 

4558 not self.identity_map.check_modified() 

4559 and not self._deleted 

4560 and not self._new 

4561 ) 

4562 

4563 # have this here since it otherwise causes issues with the proxy 

4564 # method generation 

4565 @deprecated_params( 

4566 objects=( 

4567 "2.1", 

4568 "The `objects` parameter of `Session.flush` is deprecated", 

4569 ) 

4570 ) 

4571 def _flush(self, objects: Optional[Sequence[object]] = None) -> None: 

4572 dirty = self._dirty_states 

4573 if not dirty and not self._deleted and not self._new: 

4574 self.identity_map._modified.clear() 

4575 return 

4576 

4577 flush_context = UOWTransaction(self) 

4578 

4579 if self.dispatch.before_flush: 

4580 self.dispatch.before_flush(self, flush_context, objects) 

4581 # re-establish "dirty states" in case the listeners 

4582 # added 

4583 dirty = self._dirty_states 

4584 

4585 deleted = set(self._deleted) 

4586 new = set(self._new) 

4587 

4588 dirty = set(dirty).difference(deleted) 

4589 

4590 # create the set of all objects we want to operate upon 

4591 if objects: 

4592 # specific list passed in 

4593 objset = set() 

4594 for o in objects: 

4595 try: 

4596 state = attributes.instance_state(o) 

4597 

4598 except exc.NO_STATE as err: 

4599 raise exc.UnmappedInstanceError(o) from err 

4600 objset.add(state) 

4601 else: 

4602 objset = None 

4603 

4604 # store objects whose fate has been decided 

4605 processed = set() 

4606 

4607 # put all saves/updates into the flush context. detect top-level 

4608 # orphans and throw them into deleted. 

4609 if objset: 

4610 proc = new.union(dirty).intersection(objset).difference(deleted) 

4611 else: 

4612 proc = new.union(dirty).difference(deleted) 

4613 

4614 for state in proc: 

4615 is_orphan = _state_mapper(state)._is_orphan(state) 

4616 

4617 is_persistent_orphan = is_orphan and state.has_identity 

4618 

4619 if ( 

4620 is_orphan 

4621 and not is_persistent_orphan 

4622 and state._orphaned_outside_of_session 

4623 ): 

4624 self._expunge_states([state]) 

4625 else: 

4626 _reg = flush_context.register_object( 

4627 state, isdelete=is_persistent_orphan 

4628 ) 

4629 assert _reg, "Failed to add object to the flush context!" 

4630 processed.add(state) 

4631 

4632 # put all remaining deletes into the flush context. 

4633 if objset: 

4634 proc = deleted.intersection(objset).difference(processed) 

4635 else: 

4636 proc = deleted.difference(processed) 

4637 for state in proc: 

4638 _reg = flush_context.register_object(state, isdelete=True) 

4639 assert _reg, "Failed to add object to the flush context!" 

4640 

4641 if not flush_context.has_work: 

4642 return 

4643 

4644 flush_context.transaction = transaction = self._autobegin_t()._begin() 

4645 try: 

4646 self._warn_on_events = True 

4647 try: 

4648 flush_context.execute() 

4649 finally: 

4650 self._warn_on_events = False 

4651 

4652 self.dispatch.after_flush(self, flush_context) 

4653 

4654 flush_context.finalize_flush_changes() 

4655 

4656 if not objects and self.identity_map._modified: 

4657 len_ = len(self.identity_map._modified) 

4658 

4659 statelib.InstanceState._commit_all_states( 

4660 [ 

4661 (state, state.dict) 

4662 for state in self.identity_map._modified 

4663 ], 

4664 instance_dict=self.identity_map, 

4665 ) 

4666 util.warn( 

4667 "Attribute history events accumulated on %d " 

4668 "previously clean instances " 

4669 "within inner-flush event handlers have been " 

4670 "reset, and will not result in database updates. " 

4671 "Consider using set_committed_value() within " 

4672 "inner-flush event handlers to avoid this warning." % len_ 

4673 ) 

4674 

4675 # useful assertions: 

4676 # if not objects: 

4677 # assert not self.identity_map._modified 

4678 # else: 

4679 # assert self.identity_map._modified == \ 

4680 # self.identity_map._modified.difference(objects) 

4681 

4682 self.dispatch.after_flush_postexec(self, flush_context) 

4683 

4684 transaction.commit() 

4685 

4686 except: 

4687 with util.safe_reraise(): 

4688 transaction.rollback(_capture_exception=True) 

4689 

4690 def bulk_save_objects( 

4691 self, 

4692 objects: Iterable[object], 

4693 return_defaults: bool = False, 

4694 update_changed_only: bool = True, 

4695 preserve_order: bool = True, 

4696 ) -> None: 

4697 """Perform a bulk save of the given list of objects. 

4698 

4699 .. legacy:: 

4700 

4701 This method is a legacy feature as of the 2.0 series of 

4702 SQLAlchemy. For modern bulk INSERT and UPDATE, see 

4703 the sections :ref:`orm_queryguide_bulk_insert` and 

4704 :ref:`orm_queryguide_bulk_update`. 

4705 

4706 For general INSERT and UPDATE of existing ORM mapped objects, 

4707 prefer standard :term:`unit of work` data management patterns, 

4708 introduced in the :ref:`unified_tutorial` at 

4709 :ref:`tutorial_orm_data_manipulation`. SQLAlchemy 2.0 

4710 now uses :ref:`engine_insertmanyvalues` with modern dialects 

4711 which solves previous issues of bulk INSERT slowness. 

4712 

4713 :param objects: a sequence of mapped object instances. The mapped 

4714 objects are persisted as is, and are **not** associated with the 

4715 :class:`.Session` afterwards. 

4716 

4717 For each object, whether the object is sent as an INSERT or an 

4718 UPDATE is dependent on the same rules used by the :class:`.Session` 

4719 in traditional operation; if the object has the 

4720 :attr:`.InstanceState.key` 

4721 attribute set, then the object is assumed to be "detached" and 

4722 will result in an UPDATE. Otherwise, an INSERT is used. 

4723 

4724 In the case of an UPDATE, statements are grouped based on which 

4725 attributes have changed, and are thus to be the subject of each 

4726 SET clause. If ``update_changed_only`` is False, then all 

4727 attributes present within each object are applied to the UPDATE 

4728 statement, which may help in allowing the statements to be grouped 

4729 together into a larger executemany(), and will also reduce the 

4730 overhead of checking history on attributes. 

4731 

4732 :param return_defaults: when True, rows that are missing values which 

4733 generate defaults, namely integer primary key defaults and sequences, 

4734 will be inserted **one at a time**, so that the primary key value 

4735 is available. In particular this will allow joined-inheritance 

4736 and other multi-table mappings to insert correctly without the need 

4737 to provide primary key values ahead of time; however, 

4738 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly 

4739 reduces the performance gains** of the method overall. It is strongly 

4740 advised to please use the standard :meth:`_orm.Session.add_all` 

4741 approach. 

4742 

4743 :param update_changed_only: when True, UPDATE statements are rendered 

4744 based on those attributes in each state that have logged changes. 

4745 When False, all attributes present are rendered into the SET clause 

4746 with the exception of primary key attributes. 

4747 

4748 :param preserve_order: when True, the order of inserts and updates 

4749 matches exactly the order in which the objects are given. When 

4750 False, common types of objects are grouped into inserts 

4751 and updates, to allow for more batching opportunities. 

4752 

4753 .. seealso:: 

4754 

4755 :doc:`queryguide/dml` 

4756 

4757 :meth:`.Session.bulk_insert_mappings` 

4758 

4759 :meth:`.Session.bulk_update_mappings` 

4760 

4761 """ 

4762 

4763 obj_states: Iterable[InstanceState[Any]] 

4764 

4765 obj_states = (attributes.instance_state(obj) for obj in objects) 

4766 

4767 if not preserve_order: 

4768 # the purpose of this sort is just so that common mappers 

4769 # and persistence states are grouped together, so that groupby 

4770 # will return a single group for a particular type of mapper. 

4771 # it's not trying to be deterministic beyond that. 

4772 obj_states = sorted( 

4773 obj_states, 

4774 key=lambda state: (id(state.mapper), state.key is not None), 

4775 ) 

4776 

4777 def grouping_key( 

4778 state: InstanceState[_O], 

4779 ) -> Tuple[Mapper[_O], bool]: 

4780 return (state.mapper, state.key is not None) 

4781 

4782 for (mapper, isupdate), states in itertools.groupby( 

4783 obj_states, grouping_key 

4784 ): 

4785 self._bulk_save_mappings( 

4786 mapper, 

4787 states, 

4788 isupdate=isupdate, 

4789 isstates=True, 

4790 return_defaults=return_defaults, 

4791 update_changed_only=update_changed_only, 

4792 render_nulls=False, 

4793 ) 

4794 

4795 def bulk_insert_mappings( 

4796 self, 

4797 mapper: _EntityBindKey[Any], 

4798 mappings: Iterable[Dict[str, Any]], 

4799 return_defaults: bool = False, 

4800 render_nulls: bool = False, 

4801 ) -> None: 

4802 """Perform a bulk insert of the given list of mapping dictionaries. 

4803 

4804 .. legacy:: 

4805 

4806 This method is a legacy feature as of the 2.0 series of 

4807 SQLAlchemy. For modern bulk INSERT and UPDATE, see 

4808 the sections :ref:`orm_queryguide_bulk_insert` and 

4809 :ref:`orm_queryguide_bulk_update`. The 2.0 API shares 

4810 implementation details with this method and adds new features 

4811 as well. 

4812 

4813 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

4814 object, 

4815 representing the single kind of object represented within the mapping 

4816 list. 

4817 

4818 :param mappings: a sequence of dictionaries, each one containing the 

4819 state of the mapped row to be inserted, in terms of the attribute 

4820 names on the mapped class. If the mapping refers to multiple tables, 

4821 such as a joined-inheritance mapping, each dictionary must contain all 

4822 keys to be populated into all tables. 

4823 

4824 :param return_defaults: when True, the INSERT process will be altered 

4825 to ensure that newly generated primary key values will be fetched. 

4826 The rationale for this parameter is typically to enable 

4827 :ref:`Joined Table Inheritance <joined_inheritance>` mappings to 

4828 be bulk inserted. 

4829 

4830 .. note:: for backends that don't support RETURNING, the 

4831 :paramref:`_orm.Session.bulk_insert_mappings.return_defaults` 

4832 parameter can significantly decrease performance as INSERT 

4833 statements can no longer be batched. See 

4834 :ref:`engine_insertmanyvalues` 

4835 for background on which backends are affected. 

4836 

4837 :param render_nulls: When True, a value of ``None`` will result 

4838 in a NULL value being included in the INSERT statement, rather 

4839 than the column being omitted from the INSERT. This allows all 

4840 the rows being INSERTed to have the identical set of columns which 

4841 allows the full set of rows to be batched to the DBAPI. Normally, 

4842 each column-set that contains a different combination of NULL values 

4843 than the previous row must omit a different series of columns from 

4844 the rendered INSERT statement, which means it must be emitted as a 

4845 separate statement. By passing this flag, the full set of rows 

4846 are guaranteed to be batchable into one batch; the cost however is 

4847 that server-side defaults which are invoked by an omitted column will 

4848 be skipped, so care must be taken to ensure that these are not 

4849 necessary. 

4850 

4851 .. warning:: 

4852 

4853 When this flag is set, **server side default SQL values will 

4854 not be invoked** for those columns that are inserted as NULL; 

4855 the NULL value will be sent explicitly. Care must be taken 

4856 to ensure that no server-side default functions need to be 

4857 invoked for the operation as a whole. 

4858 

4859 .. seealso:: 

4860 

4861 :doc:`queryguide/dml` 

4862 

4863 :meth:`.Session.bulk_save_objects` 

4864 

4865 :meth:`.Session.bulk_update_mappings` 

4866 

4867 """ 

4868 self._bulk_save_mappings( 

4869 mapper, 

4870 mappings, 

4871 isupdate=False, 

4872 isstates=False, 

4873 return_defaults=return_defaults, 

4874 update_changed_only=False, 

4875 render_nulls=render_nulls, 

4876 ) 

4877 

4878 def bulk_update_mappings( 

4879 self, mapper: _EntityBindKey[Any], mappings: Iterable[Dict[str, Any]] 

4880 ) -> None: 

4881 """Perform a bulk update of the given list of mapping dictionaries. 

4882 

4883 .. legacy:: 

4884 

4885 This method is a legacy feature as of the 2.0 series of 

4886 SQLAlchemy. For modern bulk INSERT and UPDATE, see 

4887 the sections :ref:`orm_queryguide_bulk_insert` and 

4888 :ref:`orm_queryguide_bulk_update`. The 2.0 API shares 

4889 implementation details with this method and adds new features 

4890 as well. 

4891 

4892 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

4893 object, 

4894 representing the single kind of object represented within the mapping 

4895 list. 

4896 

4897 :param mappings: a sequence of dictionaries, each one containing the 

4898 state of the mapped row to be updated, in terms of the attribute names 

4899 on the mapped class. If the mapping refers to multiple tables, such 

4900 as a joined-inheritance mapping, each dictionary may contain keys 

4901 corresponding to all tables. All those keys which are present and 

4902 are not part of the primary key are applied to the SET clause of the 

4903 UPDATE statement; the primary key values, which are required, are 

4904 applied to the WHERE clause. 

4905 

4906 

4907 .. seealso:: 

4908 

4909 :doc:`queryguide/dml` 

4910 

4911 :meth:`.Session.bulk_insert_mappings` 

4912 

4913 :meth:`.Session.bulk_save_objects` 

4914 

4915 """ 

4916 self._bulk_save_mappings( 

4917 mapper, 

4918 mappings, 

4919 isupdate=True, 

4920 isstates=False, 

4921 return_defaults=False, 

4922 update_changed_only=False, 

4923 render_nulls=False, 

4924 ) 

4925 

4926 def _bulk_save_mappings( 

4927 self, 

4928 mapper: _EntityBindKey[_O], 

4929 mappings: Union[Iterable[InstanceState[_O]], Iterable[Dict[str, Any]]], 

4930 *, 

4931 isupdate: bool, 

4932 isstates: bool, 

4933 return_defaults: bool, 

4934 update_changed_only: bool, 

4935 render_nulls: bool, 

4936 ) -> None: 

4937 mapper = _class_to_mapper(mapper) 

4938 

4939 try: 

4940 self._flushing = True 

4941 

4942 transaction = self._autobegin_t()._begin() 

4943 try: 

4944 if isupdate: 

4945 bulk_persistence._bulk_update( 

4946 mapper, 

4947 mappings, 

4948 transaction, 

4949 isstates=isstates, 

4950 update_changed_only=update_changed_only, 

4951 ) 

4952 else: 

4953 bulk_persistence._bulk_insert( 

4954 mapper, 

4955 mappings, 

4956 transaction, 

4957 isstates=isstates, 

4958 return_defaults=return_defaults, 

4959 render_nulls=render_nulls, 

4960 ) 

4961 transaction.commit() 

4962 

4963 except: 

4964 with util.safe_reraise(): 

4965 transaction.rollback(_capture_exception=True) 

4966 finally: 

4967 self._flushing = False 

4968 

4969 def is_modified( 

4970 self, instance: object, include_collections: bool = True 

4971 ) -> bool: 

4972 r"""Return ``True`` if the given instance has locally 

4973 modified attributes. 

4974 

4975 This method retrieves the history for each instrumented 

4976 attribute on the instance and performs a comparison of the current 

4977 value to its previously flushed or committed value, if any. 

4978 

4979 It is in effect a more expensive and accurate 

4980 version of checking for the given instance in the 

4981 :attr:`.Session.dirty` collection; a full test for 

4982 each attribute's net "dirty" status is performed. 

4983 

4984 E.g.:: 

4985 

4986 return session.is_modified(someobject) 

4987 

4988 A few caveats to this method apply: 

4989 

4990 * Instances present in the :attr:`.Session.dirty` collection may 

4991 report ``False`` when tested with this method. This is because 

4992 the object may have received change events via attribute mutation, 

4993 thus placing it in :attr:`.Session.dirty`, but ultimately the state 

4994 is the same as that loaded from the database, resulting in no net 

4995 change here. 

4996 * Scalar attributes may not have recorded the previously set 

4997 value when a new value was applied, if the attribute was not loaded, 

4998 or was expired, at the time the new value was received - in these 

4999 cases, the attribute is assumed to have a change, even if there is 

5000 ultimately no net change against its database value. SQLAlchemy in 

5001 most cases does not need the "old" value when a set event occurs, so 

5002 it skips the expense of a SQL call if the old value isn't present, 

5003 based on the assumption that an UPDATE of the scalar value is 

5004 usually needed, and in those few cases where it isn't, is less 

5005 expensive on average than issuing a defensive SELECT. 

5006 

5007 The "old" value is fetched unconditionally upon set only if the 

5008 attribute container has the ``active_history`` flag set to ``True``. 

5009 This flag is set typically for primary key attributes and scalar 

5010 object references that are not a simple many-to-one. To set this 

5011 flag for any arbitrary mapped column, use the ``active_history`` 

5012 argument with :func:`.column_property`. 

5013 

5014 :param instance: mapped instance to be tested for pending changes. 

5015 :param include_collections: Indicates if multivalued collections 

5016 should be included in the operation. Setting this to ``False`` is a 

5017 way to detect only local-column based properties (i.e. scalar columns 

5018 or many-to-one foreign keys) that would result in an UPDATE for this 

5019 instance upon flush. 

5020 

5021 """ 

5022 state = object_state(instance) 

5023 

5024 if not state.modified: 

5025 return False 

5026 

5027 dict_ = state.dict 

5028 

5029 for attr in state.manager.attributes: 

5030 if ( 

5031 not include_collections 

5032 and hasattr(attr.impl, "get_collection") 

5033 ) or not hasattr(attr.impl, "get_history"): 

5034 continue 

5035 

5036 added, unchanged, deleted = attr.impl.get_history( 

5037 state, dict_, passive=PassiveFlag.NO_CHANGE 

5038 ) 

5039 

5040 if added or deleted: 

5041 return True 

5042 else: 

5043 return False 

5044 

5045 @property 

5046 def is_active(self) -> bool: 

5047 """True if this :class:`.Session` not in "partial rollback" state. 

5048 

5049 .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins 

5050 a new transaction immediately, so this attribute will be False 

5051 when the :class:`_orm.Session` is first instantiated. 

5052 

5053 "partial rollback" state typically indicates that the flush process 

5054 of the :class:`_orm.Session` has failed, and that the 

5055 :meth:`_orm.Session.rollback` method must be emitted in order to 

5056 fully roll back the transaction. 

5057 

5058 If this :class:`_orm.Session` is not in a transaction at all, the 

5059 :class:`_orm.Session` will autobegin when it is first used, so in this 

5060 case :attr:`_orm.Session.is_active` will return True. 

5061 

5062 Otherwise, if this :class:`_orm.Session` is within a transaction, 

5063 and that transaction has not been rolled back internally, the 

5064 :attr:`_orm.Session.is_active` will also return True. 

5065 

5066 .. seealso:: 

5067 

5068 :ref:`faq_session_rollback` 

5069 

5070 :meth:`_orm.Session.in_transaction` 

5071 

5072 """ 

5073 return self._transaction is None or self._transaction.is_active 

5074 

5075 @property 

5076 def _dirty_states(self) -> Iterable[InstanceState[Any]]: 

5077 """The set of all persistent states considered dirty. 

5078 

5079 This method returns all states that were modified including 

5080 those that were possibly deleted. 

5081 

5082 """ 

5083 return self.identity_map._dirty_states() 

5084 

5085 @property 

5086 def dirty(self) -> IdentitySet: 

5087 """The set of all persistent instances considered dirty. 

5088 

5089 E.g.:: 

5090 

5091 some_mapped_object in session.dirty 

5092 

5093 Instances are considered dirty when they were modified but not 

5094 deleted. 

5095 

5096 Note that this 'dirty' calculation is 'optimistic'; most 

5097 attribute-setting or collection modification operations will 

5098 mark an instance as 'dirty' and place it in this set, even if 

5099 there is no net change to the attribute's value. At flush 

5100 time, the value of each attribute is compared to its 

5101 previously saved value, and if there's no net change, no SQL 

5102 operation will occur (this is a more expensive operation so 

5103 it's only done at flush time). 

5104 

5105 To check if an instance has actionable net changes to its 

5106 attributes, use the :meth:`.Session.is_modified` method. 

5107 

5108 """ 

5109 return IdentitySet( 

5110 [ 

5111 state.obj() 

5112 for state in self._dirty_states 

5113 if state not in self._deleted 

5114 ] 

5115 ) 

5116 

5117 @property 

5118 def deleted(self) -> IdentitySet: 

5119 "The set of all instances marked as 'deleted' within this ``Session``" 

5120 

5121 return util.IdentitySet(list(self._deleted.values())) 

5122 

5123 @property 

5124 def new(self) -> IdentitySet: 

5125 "The set of all instances marked as 'new' within this ``Session``." 

5126 

5127 return util.IdentitySet(list(self._new.values())) 

5128 

5129 

5130_S = TypeVar("_S", bound="Session") 

5131 

5132 

5133class sessionmaker(_SessionClassMethods, Generic[_S]): 

5134 """A configurable :class:`.Session` factory. 

5135 

5136 The :class:`.sessionmaker` factory generates new 

5137 :class:`.Session` objects when called, creating them given 

5138 the configurational arguments established here. 

5139 

5140 e.g.:: 

5141 

5142 from sqlalchemy import create_engine 

5143 from sqlalchemy.orm import sessionmaker 

5144 

5145 # an Engine, which the Session will use for connection 

5146 # resources 

5147 engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/") 

5148 

5149 Session = sessionmaker(engine) 

5150 

5151 with Session() as session: 

5152 session.add(some_object) 

5153 session.add(some_other_object) 

5154 session.commit() 

5155 

5156 Context manager use is optional; otherwise, the returned 

5157 :class:`_orm.Session` object may be closed explicitly via the 

5158 :meth:`_orm.Session.close` method. Using a 

5159 ``try:/finally:`` block is optional, however will ensure that the close 

5160 takes place even if there are database errors:: 

5161 

5162 session = Session() 

5163 try: 

5164 session.add(some_object) 

5165 session.add(some_other_object) 

5166 session.commit() 

5167 finally: 

5168 session.close() 

5169 

5170 :class:`.sessionmaker` acts as a factory for :class:`_orm.Session` 

5171 objects in the same way as an :class:`_engine.Engine` acts as a factory 

5172 for :class:`_engine.Connection` objects. In this way it also includes 

5173 a :meth:`_orm.sessionmaker.begin` method, that provides a context 

5174 manager which both begins and commits a transaction, as well as closes 

5175 out the :class:`_orm.Session` when complete, rolling back the transaction 

5176 if any errors occur:: 

5177 

5178 Session = sessionmaker(engine) 

5179 

5180 with Session.begin() as session: 

5181 session.add(some_object) 

5182 session.add(some_other_object) 

5183 # commits transaction, closes session 

5184 

5185 .. versionadded:: 1.4 

5186 

5187 When calling upon :class:`_orm.sessionmaker` to construct a 

5188 :class:`_orm.Session`, keyword arguments may also be passed to the 

5189 method; these arguments will override that of the globally configured 

5190 parameters. Below we use a :class:`_orm.sessionmaker` bound to a certain 

5191 :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead 

5192 bound to a specific :class:`_engine.Connection` procured from that engine:: 

5193 

5194 Session = sessionmaker(engine) 

5195 

5196 # bind an individual session to a connection 

5197 

5198 with engine.connect() as connection: 

5199 with Session(bind=connection) as session: 

5200 ... # work with session 

5201 

5202 The class also includes a method :meth:`_orm.sessionmaker.configure`, which 

5203 can be used to specify additional keyword arguments to the factory, which 

5204 will take effect for subsequent :class:`.Session` objects generated. This 

5205 is usually used to associate one or more :class:`_engine.Engine` objects 

5206 with an existing 

5207 :class:`.sessionmaker` factory before it is first used:: 

5208 

5209 # application starts, sessionmaker does not have 

5210 # an engine bound yet 

5211 Session = sessionmaker() 

5212 

5213 # ... later, when an engine URL is read from a configuration 

5214 # file or other events allow the engine to be created 

5215 engine = create_engine("sqlite:///foo.db") 

5216 Session.configure(bind=engine) 

5217 

5218 sess = Session() 

5219 # work with session 

5220 

5221 .. seealso:: 

5222 

5223 :ref:`session_getting` - introductory text on creating 

5224 sessions using :class:`.sessionmaker`. 

5225 

5226 """ 

5227 

5228 class_: Type[_S] 

5229 

5230 @overload 

5231 def __init__( 

5232 self, 

5233 bind: Optional[_SessionBind] = ..., 

5234 *, 

5235 class_: Type[_S], 

5236 autoflush: bool = ..., 

5237 expire_on_commit: bool = ..., 

5238 info: Optional[_InfoType] = ..., 

5239 **kw: Any, 

5240 ): ... 

5241 

5242 @overload 

5243 def __init__( 

5244 self: "sessionmaker[Session]", 

5245 bind: Optional[_SessionBind] = ..., 

5246 *, 

5247 autoflush: bool = ..., 

5248 expire_on_commit: bool = ..., 

5249 info: Optional[_InfoType] = ..., 

5250 **kw: Any, 

5251 ): ... 

5252 

5253 def __init__( 

5254 self, 

5255 bind: Optional[_SessionBind] = None, 

5256 *, 

5257 class_: Type[_S] = Session, # type: ignore[assignment] 

5258 autoflush: bool = True, 

5259 expire_on_commit: bool = True, 

5260 info: Optional[_InfoType] = None, 

5261 **kw: Any, 

5262 ): 

5263 r"""Construct a new :class:`.sessionmaker`. 

5264 

5265 All arguments here except for ``class_`` correspond to arguments 

5266 accepted by :class:`.Session` directly. See the 

5267 :meth:`.Session.__init__` docstring for more details on parameters. 

5268 

5269 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 

5270 with 

5271 which newly created :class:`.Session` objects will be associated. 

5272 :param class\_: class to use in order to create new :class:`.Session` 

5273 objects. Defaults to :class:`.Session`. 

5274 :param autoflush: The autoflush setting to use with newly created 

5275 :class:`.Session` objects. 

5276 

5277 .. seealso:: 

5278 

5279 :ref:`session_flushing` - additional background on autoflush 

5280 

5281 :param expire_on_commit=True: the 

5282 :paramref:`_orm.Session.expire_on_commit` setting to use 

5283 with newly created :class:`.Session` objects. 

5284 

5285 :param info: optional dictionary of information that will be available 

5286 via :attr:`.Session.info`. Note this dictionary is *updated*, not 

5287 replaced, when the ``info`` parameter is specified to the specific 

5288 :class:`.Session` construction operation. 

5289 

5290 :param \**kw: all other keyword arguments are passed to the 

5291 constructor of newly created :class:`.Session` objects. 

5292 

5293 """ 

5294 kw["bind"] = bind 

5295 kw["autoflush"] = autoflush 

5296 kw["expire_on_commit"] = expire_on_commit 

5297 if info is not None: 

5298 kw["info"] = info 

5299 self.kw = kw 

5300 # make our own subclass of the given class, so that 

5301 # events can be associated with it specifically. 

5302 self.class_ = type(class_.__name__, (class_,), {}) 

5303 

5304 def begin(self) -> contextlib.AbstractContextManager[_S]: 

5305 """Produce a context manager that both provides a new 

5306 :class:`_orm.Session` as well as a transaction that commits. 

5307 

5308 

5309 e.g.:: 

5310 

5311 Session = sessionmaker(some_engine) 

5312 

5313 with Session.begin() as session: 

5314 session.add(some_object) 

5315 

5316 # commits transaction, closes session 

5317 

5318 .. versionadded:: 1.4 

5319 

5320 

5321 """ 

5322 

5323 session = self() 

5324 return session._maker_context_manager() 

5325 

5326 def __call__(self, **local_kw: Any) -> _S: 

5327 """Produce a new :class:`.Session` object using the configuration 

5328 established in this :class:`.sessionmaker`. 

5329 

5330 In Python, the ``__call__`` method is invoked on an object when 

5331 it is "called" in the same way as a function:: 

5332 

5333 Session = sessionmaker(some_engine) 

5334 session = Session() # invokes sessionmaker.__call__() 

5335 

5336 """ 

5337 for k, v in self.kw.items(): 

5338 if k == "info" and "info" in local_kw: 

5339 d = v.copy() 

5340 d.update(local_kw["info"]) 

5341 local_kw["info"] = d 

5342 else: 

5343 local_kw.setdefault(k, v) 

5344 return self.class_(**local_kw) 

5345 

5346 def configure(self, **new_kw: Any) -> None: 

5347 """(Re)configure the arguments for this sessionmaker. 

5348 

5349 e.g.:: 

5350 

5351 Session = sessionmaker() 

5352 

5353 Session.configure(bind=create_engine("sqlite://")) 

5354 """ 

5355 self.kw.update(new_kw) 

5356 

5357 def __repr__(self) -> str: 

5358 return "%s(class_=%r, %s)" % ( 

5359 self.__class__.__name__, 

5360 self.class_.__name__, 

5361 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 

5362 ) 

5363 

5364 

5365def close_all_sessions() -> None: 

5366 """Close all sessions in memory. 

5367 

5368 This function consults a global registry of all :class:`.Session` objects 

5369 and calls :meth:`.Session.close` on them, which resets them to a clean 

5370 state. 

5371 

5372 This function is not for general use but may be useful for test suites 

5373 within the teardown scheme. 

5374 

5375 """ 

5376 

5377 for sess in _sessions.values(): 

5378 sess.close() 

5379 

5380 

5381def make_transient(instance: object) -> None: 

5382 """Alter the state of the given instance so that it is :term:`transient`. 

5383 

5384 .. note:: 

5385 

5386 :func:`.make_transient` is a special-case function for 

5387 advanced use cases only. 

5388 

5389 The given mapped instance is assumed to be in the :term:`persistent` or 

5390 :term:`detached` state. The function will remove its association with any 

5391 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 

5392 effect is that the object will behave as though it were newly constructed, 

5393 except retaining any attribute / collection values that were loaded at the 

5394 time of the call. The :attr:`.InstanceState.deleted` flag is also reset 

5395 if this object had been deleted as a result of using 

5396 :meth:`.Session.delete`. 

5397 

5398 .. warning:: 

5399 

5400 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 

5401 load ORM-mapped attributes that are not currently loaded at the time 

5402 the function is called. This includes attributes which: 

5403 

5404 * were expired via :meth:`.Session.expire` 

5405 

5406 * were expired as the natural effect of committing a session 

5407 transaction, e.g. :meth:`.Session.commit` 

5408 

5409 * are normally :term:`lazy loaded` but are not currently loaded 

5410 

5411 * are "deferred" (see :ref:`orm_queryguide_column_deferral`) and are 

5412 not yet loaded 

5413 

5414 * were not present in the query which loaded this object, such as that 

5415 which is common in joined table inheritance and other scenarios. 

5416 

5417 After :func:`.make_transient` is called, unloaded attributes such 

5418 as those above will normally resolve to the value ``None`` when 

5419 accessed, or an empty collection for a collection-oriented attribute. 

5420 As the object is transient and un-associated with any database 

5421 identity, it will no longer retrieve these values. 

5422 

5423 .. seealso:: 

5424 

5425 :func:`.make_transient_to_detached` 

5426 

5427 """ 

5428 state = attributes.instance_state(instance) 

5429 s = _state_session(state) 

5430 if s: 

5431 s._expunge_states([state]) 

5432 

5433 # remove expired state 

5434 state.expired_attributes.clear() 

5435 

5436 # remove deferred callables 

5437 if state.callables: 

5438 del state.callables 

5439 

5440 if state.key: 

5441 del state.key 

5442 if state._deleted: 

5443 del state._deleted 

5444 

5445 

5446def make_transient_to_detached(instance: object) -> None: 

5447 """Make the given transient instance :term:`detached`. 

5448 

5449 .. note:: 

5450 

5451 :func:`.make_transient_to_detached` is a special-case function for 

5452 advanced use cases only. 

5453 

5454 All attribute history on the given instance 

5455 will be reset as though the instance were freshly loaded 

5456 from a query. Missing attributes will be marked as expired. 

5457 The primary key attributes of the object, which are required, will be made 

5458 into the "key" of the instance. 

5459 

5460 The object can then be added to a session, or merged 

5461 possibly with the load=False flag, at which point it will look 

5462 as if it were loaded that way, without emitting SQL. 

5463 

5464 This is a special use case function that differs from a normal 

5465 call to :meth:`.Session.merge` in that a given persistent state 

5466 can be manufactured without any SQL calls. 

5467 

5468 .. seealso:: 

5469 

5470 :func:`.make_transient` 

5471 

5472 :meth:`.Session.enable_relationship_loading` 

5473 

5474 """ 

5475 state = attributes.instance_state(instance) 

5476 if state.session_id or state.key: 

5477 raise sa_exc.InvalidRequestError("Given object must be transient") 

5478 state.key = state.mapper._identity_key_from_state(state) 

5479 if state._deleted: 

5480 del state._deleted 

5481 state._commit_all(state.dict) 

5482 state._expire_attributes(state.dict, state.unloaded) 

5483 

5484 

5485def object_session(instance: object) -> Optional[Session]: 

5486 """Return the :class:`.Session` to which the given instance belongs. 

5487 

5488 This is essentially the same as the :attr:`.InstanceState.session` 

5489 accessor. See that attribute for details. 

5490 

5491 """ 

5492 

5493 try: 

5494 state = attributes.instance_state(instance) 

5495 except exc.NO_STATE as err: 

5496 raise exc.UnmappedInstanceError(instance) from err 

5497 else: 

5498 return _state_session(state) 

5499 

5500 

5501_new_sessionid = util.counter()