This commit is contained in:
2025-05-22 20:25:38 +02:00
parent 09f6750c2b
commit ce03fbf12f
529 changed files with 3353 additions and 3312 deletions

View File

@@ -83,6 +83,7 @@ from ..sql import roles
from ..sql import Select
from ..sql import TableClause
from ..sql import visitors
from ..sql.base import _NoArg
from ..sql.base import CompileState
from ..sql.schema import Table
from ..sql.selectable import ForUpdateArg
@@ -510,7 +511,8 @@ class ORMExecuteState(util.MemoizedSlots):
"""
return self.bind_arguments.get("mapper", None)
mp: Optional[Mapper[Any]] = self.bind_arguments.get("mapper", None)
return mp
@property
def all_mappers(self) -> Sequence[Mapper[Any]]:
@@ -718,9 +720,14 @@ class ORMExecuteState(util.MemoizedSlots):
"This ORM execution is not against a SELECT statement "
"so there are no load options."
)
return self.execution_options.get(
lo: Union[
context.QueryContext.default_load_options,
Type[context.QueryContext.default_load_options],
] = self.execution_options.get(
"_sa_orm_load_options", context.QueryContext.default_load_options
)
return lo
@property
def update_delete_options(
@@ -737,10 +744,14 @@ class ORMExecuteState(util.MemoizedSlots):
"This ORM execution is not against an UPDATE or DELETE "
"statement so there are no update options."
)
return self.execution_options.get(
uo: Union[
bulk_persistence.BulkUDCompileState.default_update_options,
Type[bulk_persistence.BulkUDCompileState.default_update_options],
] = self.execution_options.get(
"_sa_orm_update_options",
bulk_persistence.BulkUDCompileState.default_update_options,
)
return uo
@property
def _non_compile_orm_options(self) -> Sequence[ORMOption]:
@@ -881,6 +892,12 @@ class SessionTransaction(_StateChange, TransactionalContext):
self.nested = nested = origin is SessionTransactionOrigin.BEGIN_NESTED
self.origin = origin
if session._close_state is _SessionCloseState.CLOSED:
raise sa_exc.InvalidRequestError(
"This Session has been permanently closed and is unable "
"to handle any more transaction requests."
)
if nested:
if not parent:
raise sa_exc.InvalidRequestError(
@@ -1372,6 +1389,12 @@ class SessionTransaction(_StateChange, TransactionalContext):
return self._state not in (COMMITTED, CLOSED)
class _SessionCloseState(Enum):
ACTIVE = 1
CLOSED = 2
CLOSE_IS_RESET = 3
class Session(_SessionClassMethods, EventTarget):
"""Manages persistence operations for ORM-mapped objects.
@@ -1416,6 +1439,7 @@ class Session(_SessionClassMethods, EventTarget):
twophase: bool
join_transaction_mode: JoinTransactionMode
_query_cls: Type[Query[Any]]
_close_state: _SessionCloseState
def __init__(
self,
@@ -1432,6 +1456,7 @@ class Session(_SessionClassMethods, EventTarget):
query_cls: Optional[Type[Query[Any]]] = None,
autocommit: Literal[False] = False,
join_transaction_mode: JoinTransactionMode = "conditional_savepoint",
close_resets_only: Union[bool, _NoArg] = _NoArg.NO_ARG,
):
r"""Construct a new :class:`_orm.Session`.
@@ -1607,8 +1632,9 @@ class Session(_SessionClassMethods, EventTarget):
.. tip:: When using SQLite, the SQLite driver included through
Python 3.11 does not handle SAVEPOINTs correctly in all cases
without workarounds. See the section
:ref:`pysqlite_serializable` for details on current workarounds.
without workarounds. See the sections
:ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable`
for details on current workarounds.
* ``"control_fully"`` - the :class:`_orm.Session` will take
control of the given transaction as its own;
@@ -1639,6 +1665,18 @@ class Session(_SessionClassMethods, EventTarget):
.. versionadded:: 2.0.0rc1
:param close_resets_only: Defaults to ``True``. Determines if
the session should reset itself after calling ``.close()``
or should pass in a no longer usable state, disabling re-use.
.. versionadded:: 2.0.22 added flag ``close_resets_only``.
A future SQLAlchemy version may change the default value of
this flag to ``False``.
.. seealso::
:ref:`session_closing` - Detail on the semantics of
:meth:`_orm.Session.close` and :meth:`_orm.Session.reset`.
""" # noqa
@@ -1671,6 +1709,13 @@ class Session(_SessionClassMethods, EventTarget):
self.autoflush = autoflush
self.expire_on_commit = expire_on_commit
self.enable_baked_queries = enable_baked_queries
# 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:
self._close_state = _SessionCloseState.CLOSE_IS_RESET
else:
self._close_state = _SessionCloseState.ACTIVE
if (
join_transaction_mode
and join_transaction_mode
@@ -1858,7 +1903,8 @@ class Session(_SessionClassMethods, EventTarget):
:ref:`pysqlite_serializable` - special workarounds required
with the SQLite driver in order for SAVEPOINT to work
correctly.
correctly. For asyncio use cases, see the section
:ref:`aiosqlite_serializable`.
"""
return self.begin(nested=True)
@@ -2393,12 +2439,17 @@ class Session(_SessionClassMethods, EventTarget):
.. tip::
The :meth:`_orm.Session.close` method **does not prevent the
Session from being used again**. The :class:`_orm.Session` itself
does not actually have a distinct "closed" state; it merely means
In the default running mode the :meth:`_orm.Session.close`
method **does not prevent the Session from being used again**.
The :class:`_orm.Session` itself does not actually have a
distinct "closed" state; it merely means
the :class:`_orm.Session` will release all database connections
and ORM objects.
Setting the parameter :paramref:`_orm.Session.close_resets_only`
to ``False`` will instead make the ``close`` final, meaning that
any further action on the session will be forbidden.
.. versionchanged:: 1.4 The :meth:`.Session.close` method does not
immediately create a new :class:`.SessionTransaction` object;
instead, the new :class:`.SessionTransaction` is created only if
@@ -2407,11 +2458,40 @@ class Session(_SessionClassMethods, EventTarget):
.. seealso::
:ref:`session_closing` - detail on the semantics of
:meth:`_orm.Session.close`
:meth:`_orm.Session.close` and :meth:`_orm.Session.reset`.
:meth:`_orm.Session.reset` - a similar method that behaves like
``close()`` with the parameter
:paramref:`_orm.Session.close_resets_only` set to ``True``.
"""
self._close_impl(invalidate=False)
def reset(self) -> None:
"""Close out the transactional resources and ORM objects used by this
: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
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
which set :paramref:`_orm.Session.close_resets_only` to ``False``,
so that "reset only" behavior is still available from this method.
.. versionadded:: 2.0.22
.. seealso::
:ref:`session_closing` - detail on the semantics of
:meth:`_orm.Session.close` and :meth:`_orm.Session.reset`.
:meth:`_orm.Session.close` - a similar method will additionally
prevent re-use of the Session when the parameter
:paramref:`_orm.Session.close_resets_only` is set to ``False``.
"""
self._close_impl(invalidate=False, is_reset=True)
def invalidate(self) -> None:
"""Close this Session, using connection invalidation.
@@ -2448,7 +2528,9 @@ class Session(_SessionClassMethods, EventTarget):
"""
self._close_impl(invalidate=True)
def _close_impl(self, invalidate: bool) -> None:
def _close_impl(self, invalidate: bool, is_reset: bool = False) -> None:
if not is_reset and self._close_state is _SessionCloseState.ACTIVE:
self._close_state = _SessionCloseState.CLOSED
self.expunge_all()
if self._transaction is not None:
for transaction in self._transaction._iterate_self_and_parents():
@@ -3580,6 +3662,57 @@ class Session(_SessionClassMethods, EventTarget):
bind_arguments=bind_arguments,
)
def get_one(
self,
entity: _EntityBindKey[_O],
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
populate_existing: bool = False,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
) -> _O:
"""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.
For a detailed documentation of the arguments see the
method :meth:`.Session.get`.
.. versionadded:: 2.0.22
:return: The object instance.
.. seealso::
:meth:`.Session.get` - equivalent method that instead
returns ``None`` if no row was found with the provided primary
key
"""
instance = self.get(
entity,
ident,
options=options,
populate_existing=populate_existing,
with_for_update=with_for_update,
identity_token=identity_token,
execution_options=execution_options,
bind_arguments=bind_arguments,
)
if instance is None:
raise sa_exc.NoResultFound(
"No row was found when one was required"
)
return instance
def _get_impl(
self,
entity: _EntityBindKey[_O],