API refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-07 16:25:52 +09:00
parent 76d0d86211
commit 91c7e04474
1171 changed files with 81940 additions and 44117 deletions

View File

@@ -1,5 +1,5 @@
# orm/session.py
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -146,9 +146,9 @@ __all__ = [
"object_session",
]
_sessions: weakref.WeakValueDictionary[
int, Session
] = weakref.WeakValueDictionary()
_sessions: weakref.WeakValueDictionary[int, Session] = (
weakref.WeakValueDictionary()
)
"""Weak-referencing dictionary of :class:`.Session` objects.
"""
@@ -188,8 +188,7 @@ class _ConnectionCallableProto(Protocol):
mapper: Optional[Mapper[Any]] = None,
instance: Optional[object] = None,
**kw: Any,
) -> Connection:
...
) -> Connection: ...
def _state_session(state: InstanceState[Any]) -> Optional[Session]:
@@ -576,22 +575,67 @@ class ORMExecuteState(util.MemoizedSlots):
@property
def is_select(self) -> bool:
"""return True if this is a SELECT operation."""
"""return True if this is a SELECT operation.
.. versionchanged:: 2.0.30 - the attribute is also True for a
:meth:`_sql.Select.from_statement` construct that is itself against
a :class:`_sql.Select` construct, such as
``select(Entity).from_statement(select(..))``
"""
return self.statement.is_select
@property
def is_from_statement(self) -> bool:
"""return True if this operation is a
:meth:`_sql.Select.from_statement` operation.
This is independent from :attr:`_orm.ORMExecuteState.is_select`, as a
``select().from_statement()`` construct can be used with
INSERT/UPDATE/DELETE RETURNING types of statements as well.
:attr:`_orm.ORMExecuteState.is_select` will only be set if the
:meth:`_sql.Select.from_statement` is itself against a
:class:`_sql.Select` construct.
.. versionadded:: 2.0.30
"""
return self.statement.is_from_statement
@property
def is_insert(self) -> bool:
"""return True if this is an INSERT operation."""
"""return True if this is an INSERT operation.
.. versionchanged:: 2.0.30 - the attribute is also True for a
:meth:`_sql.Select.from_statement` construct that is itself against
a :class:`_sql.Insert` construct, such as
``select(Entity).from_statement(insert(..))``
"""
return self.statement.is_dml and self.statement.is_insert
@property
def is_update(self) -> bool:
"""return True if this is an UPDATE operation."""
"""return True if this is an UPDATE operation.
.. versionchanged:: 2.0.30 - the attribute is also True for a
:meth:`_sql.Select.from_statement` construct that is itself against
a :class:`_sql.Update` construct, such as
``select(Entity).from_statement(update(..))``
"""
return self.statement.is_dml and self.statement.is_update
@property
def is_delete(self) -> bool:
"""return True if this is a DELETE operation."""
"""return True if this is a DELETE operation.
.. versionchanged:: 2.0.30 - the attribute is also True for a
:meth:`_sql.Select.from_statement` construct that is itself against
a :class:`_sql.Delete` construct, such as
``select(Entity).from_statement(delete(..))``
"""
return self.statement.is_dml and self.statement.is_delete
@property
@@ -1000,9 +1044,11 @@ class SessionTransaction(_StateChange, TransactionalContext):
def _begin(self, nested: bool = False) -> SessionTransaction:
return SessionTransaction(
self.session,
SessionTransactionOrigin.BEGIN_NESTED
if nested
else SessionTransactionOrigin.SUBTRANSACTION,
(
SessionTransactionOrigin.BEGIN_NESTED
if nested
else SessionTransactionOrigin.SUBTRANSACTION
),
self,
)
@@ -1165,6 +1211,17 @@ class SessionTransaction(_StateChange, TransactionalContext):
else:
join_transaction_mode = "rollback_only"
if local_connect:
util.warn(
"The engine provided as bind produced a "
"connection that is already in a transaction. "
"This is usually caused by a core event, "
"such as 'engine_connect', that has left a "
"transaction open. The effective join "
"transaction mode used by this session is "
f"{join_transaction_mode!r}. To silence this "
"warning, do not leave transactions open"
)
if join_transaction_mode in (
"control_fully",
"rollback_only",
@@ -1512,12 +1569,16 @@ class Session(_SessionClassMethods, EventTarget):
operation. The complete heuristics for resolution are
described at :meth:`.Session.get_bind`. Usage looks like::
Session = sessionmaker(binds={
SomeMappedClass: create_engine('postgresql+psycopg2://engine1'),
SomeDeclarativeBase: create_engine('postgresql+psycopg2://engine2'),
some_mapper: create_engine('postgresql+psycopg2://engine3'),
some_table: create_engine('postgresql+psycopg2://engine4'),
})
Session = sessionmaker(
binds={
SomeMappedClass: create_engine("postgresql+psycopg2://engine1"),
SomeDeclarativeBase: create_engine(
"postgresql+psycopg2://engine2"
),
some_mapper: create_engine("postgresql+psycopg2://engine3"),
some_table: create_engine("postgresql+psycopg2://engine4"),
}
)
.. seealso::
@@ -1712,7 +1773,7 @@ class Session(_SessionClassMethods, EventTarget):
# the idea is that at some point NO_ARG will warn that in the future
# the default will switch to close_resets_only=False.
if close_resets_only or close_resets_only is _NoArg.NO_ARG:
if close_resets_only in (True, _NoArg.NO_ARG):
self._close_state = _SessionCloseState.CLOSE_IS_RESET
else:
self._close_state = _SessionCloseState.ACTIVE
@@ -1819,9 +1880,11 @@ class Session(_SessionClassMethods, EventTarget):
)
trans = SessionTransaction(
self,
SessionTransactionOrigin.BEGIN
if begin
else SessionTransactionOrigin.AUTOBEGIN,
(
SessionTransactionOrigin.BEGIN
if begin
else SessionTransactionOrigin.AUTOBEGIN
),
)
assert self._transaction is trans
return trans
@@ -2057,8 +2120,7 @@ class Session(_SessionClassMethods, EventTarget):
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
_scalar_result: Literal[True] = ...,
) -> Any:
...
) -> Any: ...
@overload
def _execute_internal(
@@ -2071,8 +2133,7 @@ class Session(_SessionClassMethods, EventTarget):
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
_scalar_result: bool = ...,
) -> Result[Any]:
...
) -> Result[Any]: ...
def _execute_internal(
self,
@@ -2215,8 +2276,7 @@ class Session(_SessionClassMethods, EventTarget):
bind_arguments: Optional[_BindArguments] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
) -> Result[_T]:
...
) -> Result[_T]: ...
@overload
def execute(
@@ -2228,8 +2288,7 @@ class Session(_SessionClassMethods, EventTarget):
bind_arguments: Optional[_BindArguments] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
) -> CursorResult[Any]:
...
) -> CursorResult[Any]: ...
@overload
def execute(
@@ -2241,8 +2300,7 @@ class Session(_SessionClassMethods, EventTarget):
bind_arguments: Optional[_BindArguments] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
) -> Result[Any]:
...
) -> Result[Any]: ...
def execute(
self,
@@ -2262,9 +2320,8 @@ class Session(_SessionClassMethods, EventTarget):
E.g.::
from sqlalchemy import select
result = session.execute(
select(User).where(User.id == 5)
)
result = session.execute(select(User).where(User.id == 5))
The API contract of :meth:`_orm.Session.execute` is similar to that
of :meth:`_engine.Connection.execute`, the :term:`2.0 style` version
@@ -2323,8 +2380,7 @@ class Session(_SessionClassMethods, EventTarget):
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
) -> Optional[_T]:
...
) -> Optional[_T]: ...
@overload
def scalar(
@@ -2335,8 +2391,7 @@ class Session(_SessionClassMethods, EventTarget):
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
) -> Any:
...
) -> Any: ...
def scalar(
self,
@@ -2373,8 +2428,7 @@ class Session(_SessionClassMethods, EventTarget):
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
) -> ScalarResult[_T]:
...
) -> ScalarResult[_T]: ...
@overload
def scalars(
@@ -2385,8 +2439,7 @@ class Session(_SessionClassMethods, EventTarget):
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
) -> ScalarResult[Any]:
...
) -> ScalarResult[Any]: ...
def scalars(
self,
@@ -2472,12 +2525,12 @@ class Session(_SessionClassMethods, EventTarget):
:class:`_orm.Session`, resetting the session to its initial state.
This method provides for same "reset-only" behavior that the
:meth:_orm.Session.close method has provided historically, where the
:meth:`_orm.Session.close` method has provided historically, where the
state of the :class:`_orm.Session` is reset as though the object were
brand new, and ready to be used again.
The method may then be useful for :class:`_orm.Session` objects
This method may then be useful for :class:`_orm.Session` objects
which set :paramref:`_orm.Session.close_resets_only` to ``False``,
so that "reset only" behavior is still available from this method.
so that "reset only" behavior is still available.
.. versionadded:: 2.0.22
@@ -2795,14 +2848,12 @@ class Session(_SessionClassMethods, EventTarget):
)
@overload
def query(self, _entity: _EntityType[_O]) -> Query[_O]:
...
def query(self, _entity: _EntityType[_O]) -> Query[_O]: ...
@overload
def query(
self, _colexpr: TypedColumnsClauseRole[_T]
) -> RowReturningQuery[Tuple[_T]]:
...
) -> RowReturningQuery[Tuple[_T]]: ...
# START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8
@@ -2812,14 +2863,12 @@ class Session(_SessionClassMethods, EventTarget):
@overload
def query(
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
) -> RowReturningQuery[Tuple[_T0, _T1]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1]]: ...
@overload
def query(
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]: ...
@overload
def query(
@@ -2828,8 +2877,7 @@ class Session(_SessionClassMethods, EventTarget):
__ent1: _TCCA[_T1],
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]: ...
@overload
def query(
@@ -2839,8 +2887,7 @@ class Session(_SessionClassMethods, EventTarget):
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
__ent4: _TCCA[_T4],
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
@overload
def query(
@@ -2851,8 +2898,7 @@ class Session(_SessionClassMethods, EventTarget):
__ent3: _TCCA[_T3],
__ent4: _TCCA[_T4],
__ent5: _TCCA[_T5],
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
@overload
def query(
@@ -2864,8 +2910,7 @@ class Session(_SessionClassMethods, EventTarget):
__ent4: _TCCA[_T4],
__ent5: _TCCA[_T5],
__ent6: _TCCA[_T6],
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
@overload
def query(
@@ -2878,16 +2923,14 @@ class Session(_SessionClassMethods, EventTarget):
__ent5: _TCCA[_T5],
__ent6: _TCCA[_T6],
__ent7: _TCCA[_T7],
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
...
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]: ...
# END OVERLOADED FUNCTIONS self.query
@overload
def query(
self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
) -> Query[Any]:
...
) -> Query[Any]: ...
def query(
self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
@@ -2930,7 +2973,7 @@ class Session(_SessionClassMethods, EventTarget):
e.g.::
obj = session._identity_lookup(inspect(SomeClass), (1, ))
obj = session._identity_lookup(inspect(SomeClass), (1,))
:param mapper: mapper in use
:param primary_key_identity: the primary key we are searching for, as
@@ -3001,7 +3044,8 @@ class Session(_SessionClassMethods, EventTarget):
@util.langhelpers.tag_method_for_warnings(
"This warning originated from the Session 'autoflush' process, "
"which was invoked automatically in response to a user-initiated "
"operation.",
"operation. Consider using ``no_autoflush`` context manager if this "
"warning happened while initializing objects.",
sa_exc.SAWarning,
)
def _autoflush(self) -> None:
@@ -3557,10 +3601,7 @@ class Session(_SessionClassMethods, EventTarget):
some_object = session.get(VersionedFoo, (5, 10))
some_object = session.get(
VersionedFoo,
{"id": 5, "version_id": 10}
)
some_object = session.get(VersionedFoo, {"id": 5, "version_id": 10})
.. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved
from the now legacy :meth:`_orm.Query.get` method.
@@ -3649,7 +3690,7 @@ class Session(_SessionClassMethods, EventTarget):
:return: The object instance, or ``None``.
"""
""" # noqa: E501
return self._get_impl(
entity,
ident,
@@ -3677,8 +3718,7 @@ class Session(_SessionClassMethods, EventTarget):
"""Return exactly one instance based on the given primary key
identifier, or raise an exception if not found.
Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query
selects no rows.
Raises :class:`_exc.NoResultFound` if the query selects no rows.
For a detailed documentation of the arguments see the
method :meth:`.Session.get`.
@@ -3768,9 +3808,9 @@ class Session(_SessionClassMethods, EventTarget):
if correct_keys:
primary_key_identity = dict(primary_key_identity)
for k in correct_keys:
primary_key_identity[
pk_synonyms[k]
] = primary_key_identity[k]
primary_key_identity[pk_synonyms[k]] = (
primary_key_identity[k]
)
try:
primary_key_identity = list(
@@ -3974,14 +4014,7 @@ class Session(_SessionClassMethods, EventTarget):
else:
key_is_persistent = True
if key in self.identity_map:
try:
merged = self.identity_map[key]
except KeyError:
# object was GC'ed right as we checked for it
merged = None
else:
merged = None
merged = self.identity_map.get(key)
if merged is None:
if key_is_persistent and key in _resolve_conflict_map:
@@ -4545,11 +4578,11 @@ class Session(_SessionClassMethods, EventTarget):
self._bulk_save_mappings(
mapper,
states,
isupdate,
True,
return_defaults,
update_changed_only,
False,
isupdate=isupdate,
isstates=True,
return_defaults=return_defaults,
update_changed_only=update_changed_only,
render_nulls=False,
)
def bulk_insert_mappings(
@@ -4628,11 +4661,11 @@ class Session(_SessionClassMethods, EventTarget):
self._bulk_save_mappings(
mapper,
mappings,
False,
False,
return_defaults,
False,
render_nulls,
isupdate=False,
isstates=False,
return_defaults=return_defaults,
update_changed_only=False,
render_nulls=render_nulls,
)
def bulk_update_mappings(
@@ -4674,13 +4707,20 @@ class Session(_SessionClassMethods, EventTarget):
"""
self._bulk_save_mappings(
mapper, mappings, True, False, False, False, False
mapper,
mappings,
isupdate=True,
isstates=False,
return_defaults=False,
update_changed_only=False,
render_nulls=False,
)
def _bulk_save_mappings(
self,
mapper: Mapper[_O],
mappings: Union[Iterable[InstanceState[_O]], Iterable[Dict[str, Any]]],
*,
isupdate: bool,
isstates: bool,
return_defaults: bool,
@@ -4697,17 +4737,17 @@ class Session(_SessionClassMethods, EventTarget):
mapper,
mappings,
transaction,
isstates,
update_changed_only,
isstates=isstates,
update_changed_only=update_changed_only,
)
else:
bulk_persistence._bulk_insert(
mapper,
mappings,
transaction,
isstates,
return_defaults,
render_nulls,
isstates=isstates,
return_defaults=return_defaults,
render_nulls=render_nulls,
)
transaction.commit()
@@ -4725,7 +4765,7 @@ class Session(_SessionClassMethods, EventTarget):
This method retrieves the history for each instrumented
attribute on the instance and performs a comparison of the current
value to its previously committed value, if any.
value to its previously flushed or committed value, if any.
It is in effect a more expensive and accurate
version of checking for the given instance in the
@@ -4895,7 +4935,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
# an Engine, which the Session will use for connection
# resources
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/')
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
Session = sessionmaker(engine)
@@ -4948,7 +4988,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
with engine.connect() as connection:
with Session(bind=connection) as session:
# work with session
... # work with session
The class also includes a method :meth:`_orm.sessionmaker.configure`, which
can be used to specify additional keyword arguments to the factory, which
@@ -4963,7 +5003,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
# ... later, when an engine URL is read from a configuration
# file or other events allow the engine to be created
engine = create_engine('sqlite:///foo.db')
engine = create_engine("sqlite:///foo.db")
Session.configure(bind=engine)
sess = Session()
@@ -4988,8 +5028,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
expire_on_commit: bool = ...,
info: Optional[_InfoType] = ...,
**kw: Any,
):
...
): ...
@overload
def __init__(
@@ -5000,8 +5039,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
expire_on_commit: bool = ...,
info: Optional[_InfoType] = ...,
**kw: Any,
):
...
): ...
def __init__(
self,
@@ -5103,7 +5141,7 @@ class sessionmaker(_SessionClassMethods, Generic[_S]):
Session = sessionmaker()
Session.configure(bind=create_engine('sqlite://'))
Session.configure(bind=create_engine("sqlite://"))
"""
self.kw.update(new_kw)