This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/__init__.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/base.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -44,10 +44,12 @@ class ReversibleProxy(Generic[_PT]):
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
@overload
|
||||
def _assign_proxied(self, target: _PT) -> _PT: ...
|
||||
def _assign_proxied(self, target: _PT) -> _PT:
|
||||
...
|
||||
|
||||
@overload
|
||||
def _assign_proxied(self, target: None) -> None: ...
|
||||
def _assign_proxied(self, target: None) -> None:
|
||||
...
|
||||
|
||||
def _assign_proxied(self, target: Optional[_PT]) -> Optional[_PT]:
|
||||
if target is not None:
|
||||
@@ -71,26 +73,28 @@ class ReversibleProxy(Generic[_PT]):
|
||||
cls._proxy_objects.pop(ref, None)
|
||||
|
||||
@classmethod
|
||||
def _regenerate_proxy_for_target(
|
||||
cls, target: _PT, **additional_kw: Any
|
||||
) -> Self:
|
||||
def _regenerate_proxy_for_target(cls, target: _PT) -> Self:
|
||||
raise NotImplementedError()
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def _retrieve_proxy_for_target(
|
||||
cls, target: _PT, regenerate: Literal[True] = ..., **additional_kw: Any
|
||||
) -> Self: ...
|
||||
cls,
|
||||
target: _PT,
|
||||
regenerate: Literal[True] = ...,
|
||||
) -> Self:
|
||||
...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def _retrieve_proxy_for_target(
|
||||
cls, target: _PT, regenerate: bool = True, **additional_kw: Any
|
||||
) -> Optional[Self]: ...
|
||||
cls, target: _PT, regenerate: bool = True
|
||||
) -> Optional[Self]:
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def _retrieve_proxy_for_target(
|
||||
cls, target: _PT, regenerate: bool = True, **additional_kw: Any
|
||||
cls, target: _PT, regenerate: bool = True
|
||||
) -> Optional[Self]:
|
||||
try:
|
||||
proxy_ref = cls._proxy_objects[weakref.ref(target)]
|
||||
@@ -102,7 +106,7 @@ class ReversibleProxy(Generic[_PT]):
|
||||
return proxy # type: ignore
|
||||
|
||||
if regenerate:
|
||||
return cls._regenerate_proxy_for_target(target, **additional_kw)
|
||||
return cls._regenerate_proxy_for_target(target)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -178,7 +182,7 @@ class GeneratorStartableContext(StartableContext[_T_co]):
|
||||
# tell if we get the same exception back
|
||||
value = typ()
|
||||
try:
|
||||
await self.gen.athrow(value)
|
||||
await util.athrow(self.gen, typ, value, traceback)
|
||||
except StopAsyncIteration as exc:
|
||||
# Suppress StopIteration *unless* it's the same exception that
|
||||
# was passed to throw(). This prevents a StopIteration
|
||||
@@ -215,7 +219,7 @@ class GeneratorStartableContext(StartableContext[_T_co]):
|
||||
|
||||
|
||||
def asyncstartablecontext(
|
||||
func: Callable[..., AsyncIterator[_T_co]],
|
||||
func: Callable[..., AsyncIterator[_T_co]]
|
||||
) -> Callable[..., GeneratorStartableContext[_T_co]]:
|
||||
"""@asyncstartablecontext decorator.
|
||||
|
||||
@@ -224,9 +228,7 @@ def asyncstartablecontext(
|
||||
``@contextlib.asynccontextmanager`` supports, and the usage pattern
|
||||
is different as well.
|
||||
|
||||
Typical usage:
|
||||
|
||||
.. sourcecode:: text
|
||||
Typical usage::
|
||||
|
||||
@asyncstartablecontext
|
||||
async def some_async_generator(<arguments>):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/engine.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -41,8 +41,6 @@ from ...engine.base import NestedTransaction
|
||||
from ...engine.base import Transaction
|
||||
from ...exc import ArgumentError
|
||||
from ...util.concurrency import greenlet_spawn
|
||||
from ...util.typing import Concatenate
|
||||
from ...util.typing import ParamSpec
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...engine.cursor import CursorResult
|
||||
@@ -63,7 +61,6 @@ if TYPE_CHECKING:
|
||||
from ...sql.base import Executable
|
||||
from ...sql.selectable import TypedReturnsRows
|
||||
|
||||
_P = ParamSpec("_P")
|
||||
_T = TypeVar("_T", bound=Any)
|
||||
|
||||
|
||||
@@ -198,7 +195,6 @@ class AsyncConnection(
|
||||
method of :class:`_asyncio.AsyncEngine`::
|
||||
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
engine = create_async_engine("postgresql+asyncpg://user:pass@host/dbname")
|
||||
|
||||
async with engine.connect() as conn:
|
||||
@@ -255,7 +251,7 @@ class AsyncConnection(
|
||||
|
||||
@classmethod
|
||||
def _regenerate_proxy_for_target(
|
||||
cls, target: Connection, **additional_kw: Any # noqa: U100
|
||||
cls, target: Connection
|
||||
) -> AsyncConnection:
|
||||
return AsyncConnection(
|
||||
AsyncEngine._retrieve_proxy_for_target(target.engine), target
|
||||
@@ -418,12 +414,13 @@ class AsyncConnection(
|
||||
yield_per: int = ...,
|
||||
insertmanyvalues_page_size: int = ...,
|
||||
schema_translate_map: Optional[SchemaTranslateMapType] = ...,
|
||||
preserve_rowcount: bool = False,
|
||||
**opt: Any,
|
||||
) -> AsyncConnection: ...
|
||||
) -> AsyncConnection:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execution_options(self, **opt: Any) -> AsyncConnection: ...
|
||||
async def execution_options(self, **opt: Any) -> AsyncConnection:
|
||||
...
|
||||
|
||||
async def execution_options(self, **opt: Any) -> AsyncConnection:
|
||||
r"""Set non-SQL options for the connection which take effect
|
||||
@@ -521,7 +518,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> GeneratorStartableContext[AsyncResult[_T]]: ...
|
||||
) -> GeneratorStartableContext[AsyncResult[_T]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def stream(
|
||||
@@ -530,7 +528,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> GeneratorStartableContext[AsyncResult[Any]]: ...
|
||||
) -> GeneratorStartableContext[AsyncResult[Any]]:
|
||||
...
|
||||
|
||||
@asyncstartablecontext
|
||||
async def stream(
|
||||
@@ -545,7 +544,7 @@ class AsyncConnection(
|
||||
|
||||
E.g.::
|
||||
|
||||
result = await conn.stream(stmt)
|
||||
result = await conn.stream(stmt):
|
||||
async for row in result:
|
||||
print(f"{row}")
|
||||
|
||||
@@ -574,11 +573,6 @@ class AsyncConnection(
|
||||
:meth:`.AsyncConnection.stream_scalars`
|
||||
|
||||
"""
|
||||
if not self.dialect.supports_server_side_cursors:
|
||||
raise exc.InvalidRequestError(
|
||||
"Cant use `stream` or `stream_scalars` with the current "
|
||||
"dialect since it does not support server side cursors."
|
||||
)
|
||||
|
||||
result = await greenlet_spawn(
|
||||
self._proxied.execute,
|
||||
@@ -606,7 +600,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> CursorResult[_T]: ...
|
||||
) -> CursorResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -615,7 +610,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> CursorResult[Any]: ...
|
||||
) -> CursorResult[Any]:
|
||||
...
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
@@ -671,7 +667,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreSingleExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> Optional[_T]: ...
|
||||
) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(
|
||||
@@ -680,7 +677,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreSingleExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> Any: ...
|
||||
) -> Any:
|
||||
...
|
||||
|
||||
async def scalar(
|
||||
self,
|
||||
@@ -711,7 +709,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> ScalarResult[_T]: ...
|
||||
) -> ScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalars(
|
||||
@@ -720,7 +719,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreAnyExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> ScalarResult[Any]: ...
|
||||
) -> ScalarResult[Any]:
|
||||
...
|
||||
|
||||
async def scalars(
|
||||
self,
|
||||
@@ -752,7 +752,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreSingleExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> GeneratorStartableContext[AsyncScalarResult[_T]]: ...
|
||||
) -> GeneratorStartableContext[AsyncScalarResult[_T]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def stream_scalars(
|
||||
@@ -761,7 +762,8 @@ class AsyncConnection(
|
||||
parameters: Optional[_CoreSingleExecuteParams] = None,
|
||||
*,
|
||||
execution_options: Optional[CoreExecuteOptionsParameter] = None,
|
||||
) -> GeneratorStartableContext[AsyncScalarResult[Any]]: ...
|
||||
) -> GeneratorStartableContext[AsyncScalarResult[Any]]:
|
||||
...
|
||||
|
||||
@asyncstartablecontext
|
||||
async def stream_scalars(
|
||||
@@ -817,12 +819,9 @@ class AsyncConnection(
|
||||
yield result.scalars()
|
||||
|
||||
async def run_sync(
|
||||
self,
|
||||
fn: Callable[Concatenate[Connection, _P], _T],
|
||||
*arg: _P.args,
|
||||
**kw: _P.kwargs,
|
||||
self, fn: Callable[..., _T], *arg: Any, **kw: Any
|
||||
) -> _T:
|
||||
'''Invoke the given synchronous (i.e. not async) callable,
|
||||
"""Invoke the given synchronous (i.e. not async) callable,
|
||||
passing a synchronous-style :class:`_engine.Connection` as the first
|
||||
argument.
|
||||
|
||||
@@ -832,26 +831,26 @@ class AsyncConnection(
|
||||
E.g.::
|
||||
|
||||
def do_something_with_core(conn: Connection, arg1: int, arg2: str) -> str:
|
||||
"""A synchronous function that does not require awaiting
|
||||
'''A synchronous function that does not require awaiting
|
||||
|
||||
:param conn: a Core SQLAlchemy Connection, used synchronously
|
||||
|
||||
:return: an optional return value is supported
|
||||
|
||||
"""
|
||||
conn.execute(some_table.insert().values(int_col=arg1, str_col=arg2))
|
||||
'''
|
||||
conn.execute(
|
||||
some_table.insert().values(int_col=arg1, str_col=arg2)
|
||||
)
|
||||
return "success"
|
||||
|
||||
|
||||
async def do_something_async(async_engine: AsyncEngine) -> None:
|
||||
"""an async function that uses awaiting"""
|
||||
'''an async function that uses awaiting'''
|
||||
|
||||
async with async_engine.begin() as async_conn:
|
||||
# run do_something_with_core() with a sync-style
|
||||
# Connection, proxied into an awaitable
|
||||
return_code = await async_conn.run_sync(
|
||||
do_something_with_core, 5, "strval"
|
||||
)
|
||||
return_code = await async_conn.run_sync(do_something_with_core, 5, "strval")
|
||||
print(return_code)
|
||||
|
||||
This method maintains the asyncio event loop all the way through
|
||||
@@ -882,11 +881,9 @@ class AsyncConnection(
|
||||
|
||||
:ref:`session_run_sync`
|
||||
|
||||
''' # noqa: E501
|
||||
""" # noqa: E501
|
||||
|
||||
return await greenlet_spawn(
|
||||
fn, self._proxied, *arg, _require_await=False, **kw
|
||||
)
|
||||
return await greenlet_spawn(fn, self._proxied, *arg, **kw)
|
||||
|
||||
def __await__(self) -> Generator[Any, None, AsyncConnection]:
|
||||
return self.start().__await__()
|
||||
@@ -931,7 +928,7 @@ class AsyncConnection(
|
||||
return self._proxied.invalidated
|
||||
|
||||
@property
|
||||
def dialect(self) -> Dialect:
|
||||
def dialect(self) -> Any:
|
||||
r"""Proxy for the :attr:`_engine.Connection.dialect` attribute
|
||||
on behalf of the :class:`_asyncio.AsyncConnection` class.
|
||||
|
||||
@@ -940,7 +937,7 @@ class AsyncConnection(
|
||||
return self._proxied.dialect
|
||||
|
||||
@dialect.setter
|
||||
def dialect(self, attr: Dialect) -> None:
|
||||
def dialect(self, attr: Any) -> None:
|
||||
self._proxied.dialect = attr
|
||||
|
||||
@property
|
||||
@@ -1001,7 +998,6 @@ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable):
|
||||
:func:`_asyncio.create_async_engine` function::
|
||||
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
engine = create_async_engine("postgresql+asyncpg://user:pass@host/dbname")
|
||||
|
||||
.. versionadded:: 1.4
|
||||
@@ -1041,9 +1037,7 @@ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable):
|
||||
return self.sync_engine
|
||||
|
||||
@classmethod
|
||||
def _regenerate_proxy_for_target(
|
||||
cls, target: Engine, **additional_kw: Any # noqa: U100
|
||||
) -> AsyncEngine:
|
||||
def _regenerate_proxy_for_target(cls, target: Engine) -> AsyncEngine:
|
||||
return AsyncEngine(target)
|
||||
|
||||
@contextlib.asynccontextmanager
|
||||
@@ -1060,6 +1054,7 @@ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable):
|
||||
)
|
||||
await conn.execute(text("my_special_procedure(5)"))
|
||||
|
||||
|
||||
"""
|
||||
conn = self.connect()
|
||||
|
||||
@@ -1105,10 +1100,12 @@ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable):
|
||||
insertmanyvalues_page_size: int = ...,
|
||||
schema_translate_map: Optional[SchemaTranslateMapType] = ...,
|
||||
**opt: Any,
|
||||
) -> AsyncEngine: ...
|
||||
) -> AsyncEngine:
|
||||
...
|
||||
|
||||
@overload
|
||||
def execution_options(self, **opt: Any) -> AsyncEngine: ...
|
||||
def execution_options(self, **opt: Any) -> AsyncEngine:
|
||||
...
|
||||
|
||||
def execution_options(self, **opt: Any) -> AsyncEngine:
|
||||
"""Return a new :class:`_asyncio.AsyncEngine` that will provide
|
||||
@@ -1163,7 +1160,7 @@ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable):
|
||||
This applies **only** to the built-in cache that is established
|
||||
via the :paramref:`_engine.create_engine.query_cache_size` parameter.
|
||||
It will not impact any dictionary caches that were passed via the
|
||||
:paramref:`.Connection.execution_options.compiled_cache` parameter.
|
||||
:paramref:`.Connection.execution_options.query_cache` parameter.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
@@ -1346,7 +1343,7 @@ class AsyncTransaction(
|
||||
|
||||
@classmethod
|
||||
def _regenerate_proxy_for_target(
|
||||
cls, target: Transaction, **additional_kw: Any # noqa: U100
|
||||
cls, target: Transaction
|
||||
) -> AsyncTransaction:
|
||||
sync_connection = target.connection
|
||||
sync_transaction = target
|
||||
@@ -1421,17 +1418,19 @@ class AsyncTransaction(
|
||||
|
||||
|
||||
@overload
|
||||
def _get_sync_engine_or_connection(async_engine: AsyncEngine) -> Engine: ...
|
||||
def _get_sync_engine_or_connection(async_engine: AsyncEngine) -> Engine:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def _get_sync_engine_or_connection(
|
||||
async_engine: AsyncConnection,
|
||||
) -> Connection: ...
|
||||
) -> Connection:
|
||||
...
|
||||
|
||||
|
||||
def _get_sync_engine_or_connection(
|
||||
async_engine: Union[AsyncEngine, AsyncConnection],
|
||||
async_engine: Union[AsyncEngine, AsyncConnection]
|
||||
) -> Union[Engine, Connection]:
|
||||
if isinstance(async_engine, AsyncConnection):
|
||||
return async_engine._proxied
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/exc.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/result.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -93,7 +93,6 @@ class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
|
||||
|
||||
self._metadata = real_result._metadata
|
||||
self._unique_filter_state = real_result._unique_filter_state
|
||||
self._source_supports_scalars = real_result._source_supports_scalars
|
||||
self._post_creational_filter = None
|
||||
|
||||
# BaseCursorResult pre-generates the "_row_getter". Use that
|
||||
@@ -325,20 +324,22 @@ class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
|
||||
return await greenlet_spawn(self._only_one_row, True, False, False)
|
||||
|
||||
@overload
|
||||
async def scalar_one(self: AsyncResult[Tuple[_T]]) -> _T: ...
|
||||
async def scalar_one(self: AsyncResult[Tuple[_T]]) -> _T:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar_one(self) -> Any: ...
|
||||
async def scalar_one(self) -> Any:
|
||||
...
|
||||
|
||||
async def scalar_one(self) -> Any:
|
||||
"""Return exactly one scalar result or raise an exception.
|
||||
|
||||
This is equivalent to calling :meth:`_asyncio.AsyncResult.scalars` and
|
||||
then :meth:`_asyncio.AsyncScalarResult.one`.
|
||||
then :meth:`_asyncio.AsyncResult.one`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`_asyncio.AsyncScalarResult.one`
|
||||
:meth:`_asyncio.AsyncResult.one`
|
||||
|
||||
:meth:`_asyncio.AsyncResult.scalars`
|
||||
|
||||
@@ -348,20 +349,22 @@ class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
|
||||
@overload
|
||||
async def scalar_one_or_none(
|
||||
self: AsyncResult[Tuple[_T]],
|
||||
) -> Optional[_T]: ...
|
||||
) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar_one_or_none(self) -> Optional[Any]: ...
|
||||
async def scalar_one_or_none(self) -> Optional[Any]:
|
||||
...
|
||||
|
||||
async def scalar_one_or_none(self) -> Optional[Any]:
|
||||
"""Return exactly one scalar result or ``None``.
|
||||
|
||||
This is equivalent to calling :meth:`_asyncio.AsyncResult.scalars` and
|
||||
then :meth:`_asyncio.AsyncScalarResult.one_or_none`.
|
||||
then :meth:`_asyncio.AsyncResult.one_or_none`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`_asyncio.AsyncScalarResult.one_or_none`
|
||||
:meth:`_asyncio.AsyncResult.one_or_none`
|
||||
|
||||
:meth:`_asyncio.AsyncResult.scalars`
|
||||
|
||||
@@ -400,10 +403,12 @@ class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
|
||||
return await greenlet_spawn(self._only_one_row, True, True, False)
|
||||
|
||||
@overload
|
||||
async def scalar(self: AsyncResult[Tuple[_T]]) -> Optional[_T]: ...
|
||||
async def scalar(self: AsyncResult[Tuple[_T]]) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(self) -> Any: ...
|
||||
async def scalar(self) -> Any:
|
||||
...
|
||||
|
||||
async def scalar(self) -> Any:
|
||||
"""Fetch the first column of the first row, and close the result set.
|
||||
@@ -447,13 +452,16 @@ class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
|
||||
@overload
|
||||
def scalars(
|
||||
self: AsyncResult[Tuple[_T]], index: Literal[0]
|
||||
) -> AsyncScalarResult[_T]: ...
|
||||
) -> AsyncScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def scalars(self: AsyncResult[Tuple[_T]]) -> AsyncScalarResult[_T]: ...
|
||||
def scalars(self: AsyncResult[Tuple[_T]]) -> AsyncScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]: ...
|
||||
def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]:
|
||||
...
|
||||
|
||||
def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]:
|
||||
"""Return an :class:`_asyncio.AsyncScalarResult` filtering object which
|
||||
@@ -825,9 +833,11 @@ class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
|
||||
"""
|
||||
...
|
||||
|
||||
def __aiter__(self) -> AsyncIterator[_R]: ...
|
||||
async def __aiter__(self) -> AsyncIterator[_R]:
|
||||
...
|
||||
|
||||
async def __anext__(self) -> _R: ...
|
||||
async def __anext__(self) -> _R:
|
||||
...
|
||||
|
||||
async def first(self) -> Optional[_R]:
|
||||
"""Fetch the first object or ``None`` if no object is present.
|
||||
@@ -861,20 +871,22 @@ class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar_one(self: AsyncTupleResult[Tuple[_T]]) -> _T: ...
|
||||
async def scalar_one(self: AsyncTupleResult[Tuple[_T]]) -> _T:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar_one(self) -> Any: ...
|
||||
async def scalar_one(self) -> Any:
|
||||
...
|
||||
|
||||
async def scalar_one(self) -> Any:
|
||||
"""Return exactly one scalar result or raise an exception.
|
||||
|
||||
This is equivalent to calling :meth:`_engine.Result.scalars`
|
||||
and then :meth:`_engine.AsyncScalarResult.one`.
|
||||
and then :meth:`_engine.Result.one`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`_engine.AsyncScalarResult.one`
|
||||
:meth:`_engine.Result.one`
|
||||
|
||||
:meth:`_engine.Result.scalars`
|
||||
|
||||
@@ -884,20 +896,22 @@ class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
|
||||
@overload
|
||||
async def scalar_one_or_none(
|
||||
self: AsyncTupleResult[Tuple[_T]],
|
||||
) -> Optional[_T]: ...
|
||||
) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar_one_or_none(self) -> Optional[Any]: ...
|
||||
async def scalar_one_or_none(self) -> Optional[Any]:
|
||||
...
|
||||
|
||||
async def scalar_one_or_none(self) -> Optional[Any]:
|
||||
"""Return exactly one or no scalar result.
|
||||
|
||||
This is equivalent to calling :meth:`_engine.Result.scalars`
|
||||
and then :meth:`_engine.AsyncScalarResult.one_or_none`.
|
||||
and then :meth:`_engine.Result.one_or_none`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`_engine.AsyncScalarResult.one_or_none`
|
||||
:meth:`_engine.Result.one_or_none`
|
||||
|
||||
:meth:`_engine.Result.scalars`
|
||||
|
||||
@@ -905,12 +919,12 @@ class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(
|
||||
self: AsyncTupleResult[Tuple[_T]],
|
||||
) -> Optional[_T]: ...
|
||||
async def scalar(self: AsyncTupleResult[Tuple[_T]]) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(self) -> Any: ...
|
||||
async def scalar(self) -> Any:
|
||||
...
|
||||
|
||||
async def scalar(self) -> Any:
|
||||
"""Fetch the first column of the first row, and close the result
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/scoping.py
|
||||
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -364,7 +364,7 @@ class async_scoped_session(Generic[_AS]):
|
||||
object is entered::
|
||||
|
||||
async with async_session.begin():
|
||||
... # ORM transaction is begun
|
||||
# .. ORM transaction is begun
|
||||
|
||||
Note that database IO will not normally occur when the session-level
|
||||
transaction is begun, as database transactions begin on an
|
||||
@@ -536,7 +536,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> Result[_T]: ...
|
||||
) -> Result[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -548,7 +549,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> CursorResult[Any]: ...
|
||||
) -> CursorResult[Any]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -560,7 +562,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> Result[Any]: ...
|
||||
) -> Result[Any]:
|
||||
...
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
@@ -808,28 +811,28 @@ class async_scoped_session(Generic[_AS]):
|
||||
|
||||
# construct async engines w/ async drivers
|
||||
engines = {
|
||||
"leader": create_async_engine("sqlite+aiosqlite:///leader.db"),
|
||||
"other": create_async_engine("sqlite+aiosqlite:///other.db"),
|
||||
"follower1": create_async_engine("sqlite+aiosqlite:///follower1.db"),
|
||||
"follower2": create_async_engine("sqlite+aiosqlite:///follower2.db"),
|
||||
'leader':create_async_engine("sqlite+aiosqlite:///leader.db"),
|
||||
'other':create_async_engine("sqlite+aiosqlite:///other.db"),
|
||||
'follower1':create_async_engine("sqlite+aiosqlite:///follower1.db"),
|
||||
'follower2':create_async_engine("sqlite+aiosqlite:///follower2.db"),
|
||||
}
|
||||
|
||||
|
||||
class RoutingSession(Session):
|
||||
def get_bind(self, mapper=None, clause=None, **kw):
|
||||
# within get_bind(), return sync engines
|
||||
if mapper and issubclass(mapper.class_, MyOtherClass):
|
||||
return engines["other"].sync_engine
|
||||
return engines['other'].sync_engine
|
||||
elif self._flushing or isinstance(clause, (Update, Delete)):
|
||||
return engines["leader"].sync_engine
|
||||
return engines['leader'].sync_engine
|
||||
else:
|
||||
return engines[
|
||||
random.choice(["follower1", "follower2"])
|
||||
random.choice(['follower1','follower2'])
|
||||
].sync_engine
|
||||
|
||||
|
||||
# apply to AsyncSession using sync_session_class
|
||||
AsyncSessionMaker = async_sessionmaker(sync_session_class=RoutingSession)
|
||||
AsyncSessionMaker = async_sessionmaker(
|
||||
sync_session_class=RoutingSession
|
||||
)
|
||||
|
||||
The :meth:`_orm.Session.get_bind` method is called in a non-asyncio,
|
||||
implicitly non-blocking context in the same manner as ORM event hooks
|
||||
@@ -864,7 +867,7 @@ class async_scoped_session(Generic[_AS]):
|
||||
|
||||
This method retrieves the history for each instrumented
|
||||
attribute on the instance and performs a comparison of the current
|
||||
value to its previously flushed or committed value, if any.
|
||||
value to its previously committed value, if any.
|
||||
|
||||
It is in effect a more expensive and accurate
|
||||
version of checking for the given instance in the
|
||||
@@ -1012,7 +1015,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> Optional[_T]: ...
|
||||
) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(
|
||||
@@ -1023,7 +1027,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> Any: ...
|
||||
) -> Any:
|
||||
...
|
||||
|
||||
async def scalar(
|
||||
self,
|
||||
@@ -1065,7 +1070,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> ScalarResult[_T]: ...
|
||||
) -> ScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalars(
|
||||
@@ -1076,7 +1082,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> ScalarResult[Any]: ...
|
||||
) -> ScalarResult[Any]:
|
||||
...
|
||||
|
||||
async def scalars(
|
||||
self,
|
||||
@@ -1175,7 +1182,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
Proxied for the :class:`_asyncio.AsyncSession` class on
|
||||
behalf of the :class:`_asyncio.scoping.async_scoped_session` class.
|
||||
|
||||
Raises :class:`_exc.NoResultFound` if the query selects no rows.
|
||||
Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query selects
|
||||
no rows.
|
||||
|
||||
..versionadded: 2.0.22
|
||||
|
||||
@@ -1205,7 +1213,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncResult[_T]: ...
|
||||
) -> AsyncResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def stream(
|
||||
@@ -1216,7 +1225,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncResult[Any]: ...
|
||||
) -> AsyncResult[Any]:
|
||||
...
|
||||
|
||||
async def stream(
|
||||
self,
|
||||
@@ -1255,7 +1265,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncScalarResult[_T]: ...
|
||||
) -> AsyncScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def stream_scalars(
|
||||
@@ -1266,7 +1277,8 @@ class async_scoped_session(Generic[_AS]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncScalarResult[Any]: ...
|
||||
) -> AsyncScalarResult[Any]:
|
||||
...
|
||||
|
||||
async def stream_scalars(
|
||||
self,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ext/asyncio/session.py
|
||||
# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2020-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -38,9 +38,6 @@ from ...orm import Session
|
||||
from ...orm import SessionTransaction
|
||||
from ...orm import state as _instance_state
|
||||
from ...util.concurrency import greenlet_spawn
|
||||
from ...util.typing import Concatenate
|
||||
from ...util.typing import ParamSpec
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .engine import AsyncConnection
|
||||
@@ -74,7 +71,6 @@ if TYPE_CHECKING:
|
||||
|
||||
_AsyncSessionBind = Union["AsyncEngine", "AsyncConnection"]
|
||||
|
||||
_P = ParamSpec("_P")
|
||||
_T = TypeVar("_T", bound=Any)
|
||||
|
||||
|
||||
@@ -336,12 +332,9 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
)
|
||||
|
||||
async def run_sync(
|
||||
self,
|
||||
fn: Callable[Concatenate[Session, _P], _T],
|
||||
*arg: _P.args,
|
||||
**kw: _P.kwargs,
|
||||
self, fn: Callable[..., _T], *arg: Any, **kw: Any
|
||||
) -> _T:
|
||||
'''Invoke the given synchronous (i.e. not async) callable,
|
||||
"""Invoke the given synchronous (i.e. not async) callable,
|
||||
passing a synchronous-style :class:`_orm.Session` as the first
|
||||
argument.
|
||||
|
||||
@@ -351,27 +344,25 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
E.g.::
|
||||
|
||||
def some_business_method(session: Session, param: str) -> str:
|
||||
"""A synchronous function that does not require awaiting
|
||||
'''A synchronous function that does not require awaiting
|
||||
|
||||
:param session: a SQLAlchemy Session, used synchronously
|
||||
|
||||
:return: an optional return value is supported
|
||||
|
||||
"""
|
||||
'''
|
||||
session.add(MyObject(param=param))
|
||||
session.flush()
|
||||
return "success"
|
||||
|
||||
|
||||
async def do_something_async(async_engine: AsyncEngine) -> None:
|
||||
"""an async function that uses awaiting"""
|
||||
'''an async function that uses awaiting'''
|
||||
|
||||
with AsyncSession(async_engine) as async_session:
|
||||
# run some_business_method() with a sync-style
|
||||
# Session, proxied into an awaitable
|
||||
return_code = await async_session.run_sync(
|
||||
some_business_method, param="param1"
|
||||
)
|
||||
return_code = await async_session.run_sync(some_business_method, param="param1")
|
||||
print(return_code)
|
||||
|
||||
This method maintains the asyncio event loop all the way through
|
||||
@@ -393,11 +384,9 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
:meth:`.AsyncConnection.run_sync`
|
||||
|
||||
:ref:`session_run_sync`
|
||||
''' # noqa: E501
|
||||
""" # noqa: E501
|
||||
|
||||
return await greenlet_spawn(
|
||||
fn, self.sync_session, *arg, _require_await=False, **kw
|
||||
)
|
||||
return await greenlet_spawn(fn, self.sync_session, *arg, **kw)
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -409,7 +398,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> Result[_T]: ...
|
||||
) -> Result[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -421,7 +411,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> CursorResult[Any]: ...
|
||||
) -> CursorResult[Any]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def execute(
|
||||
@@ -433,7 +424,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
_parent_execute_state: Optional[Any] = None,
|
||||
_add_event: Optional[Any] = None,
|
||||
) -> Result[Any]: ...
|
||||
) -> Result[Any]:
|
||||
...
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
@@ -479,7 +471,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> Optional[_T]: ...
|
||||
) -> Optional[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalar(
|
||||
@@ -490,7 +483,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> Any: ...
|
||||
) -> Any:
|
||||
...
|
||||
|
||||
async def scalar(
|
||||
self,
|
||||
@@ -534,7 +528,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> ScalarResult[_T]: ...
|
||||
) -> ScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def scalars(
|
||||
@@ -545,7 +540,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> ScalarResult[Any]: ...
|
||||
) -> ScalarResult[Any]:
|
||||
...
|
||||
|
||||
async def scalars(
|
||||
self,
|
||||
@@ -628,7 +624,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
"""Return an instance based on the given primary key identifier,
|
||||
or raise an exception if not found.
|
||||
|
||||
Raises :class:`_exc.NoResultFound` if the query selects no rows.
|
||||
Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query selects
|
||||
no rows.
|
||||
|
||||
..versionadded: 2.0.22
|
||||
|
||||
@@ -658,7 +655,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncResult[_T]: ...
|
||||
) -> AsyncResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def stream(
|
||||
@@ -669,7 +667,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncResult[Any]: ...
|
||||
) -> AsyncResult[Any]:
|
||||
...
|
||||
|
||||
async def stream(
|
||||
self,
|
||||
@@ -711,7 +710,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncScalarResult[_T]: ...
|
||||
) -> AsyncScalarResult[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def stream_scalars(
|
||||
@@ -722,7 +722,8 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
|
||||
bind_arguments: Optional[_BindArguments] = None,
|
||||
**kw: Any,
|
||||
) -> AsyncScalarResult[Any]: ...
|
||||
) -> AsyncScalarResult[Any]:
|
||||
...
|
||||
|
||||
async def stream_scalars(
|
||||
self,
|
||||
@@ -811,9 +812,7 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
"""
|
||||
trans = self.sync_session.get_transaction()
|
||||
if trans is not None:
|
||||
return AsyncSessionTransaction._retrieve_proxy_for_target(
|
||||
trans, async_session=self
|
||||
)
|
||||
return AsyncSessionTransaction._retrieve_proxy_for_target(trans)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -829,9 +828,7 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
|
||||
trans = self.sync_session.get_nested_transaction()
|
||||
if trans is not None:
|
||||
return AsyncSessionTransaction._retrieve_proxy_for_target(
|
||||
trans, async_session=self
|
||||
)
|
||||
return AsyncSessionTransaction._retrieve_proxy_for_target(trans)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -882,28 +879,28 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
|
||||
# construct async engines w/ async drivers
|
||||
engines = {
|
||||
"leader": create_async_engine("sqlite+aiosqlite:///leader.db"),
|
||||
"other": create_async_engine("sqlite+aiosqlite:///other.db"),
|
||||
"follower1": create_async_engine("sqlite+aiosqlite:///follower1.db"),
|
||||
"follower2": create_async_engine("sqlite+aiosqlite:///follower2.db"),
|
||||
'leader':create_async_engine("sqlite+aiosqlite:///leader.db"),
|
||||
'other':create_async_engine("sqlite+aiosqlite:///other.db"),
|
||||
'follower1':create_async_engine("sqlite+aiosqlite:///follower1.db"),
|
||||
'follower2':create_async_engine("sqlite+aiosqlite:///follower2.db"),
|
||||
}
|
||||
|
||||
|
||||
class RoutingSession(Session):
|
||||
def get_bind(self, mapper=None, clause=None, **kw):
|
||||
# within get_bind(), return sync engines
|
||||
if mapper and issubclass(mapper.class_, MyOtherClass):
|
||||
return engines["other"].sync_engine
|
||||
return engines['other'].sync_engine
|
||||
elif self._flushing or isinstance(clause, (Update, Delete)):
|
||||
return engines["leader"].sync_engine
|
||||
return engines['leader'].sync_engine
|
||||
else:
|
||||
return engines[
|
||||
random.choice(["follower1", "follower2"])
|
||||
random.choice(['follower1','follower2'])
|
||||
].sync_engine
|
||||
|
||||
|
||||
# apply to AsyncSession using sync_session_class
|
||||
AsyncSessionMaker = async_sessionmaker(sync_session_class=RoutingSession)
|
||||
AsyncSessionMaker = async_sessionmaker(
|
||||
sync_session_class=RoutingSession
|
||||
)
|
||||
|
||||
The :meth:`_orm.Session.get_bind` method is called in a non-asyncio,
|
||||
implicitly non-blocking context in the same manner as ORM event hooks
|
||||
@@ -959,7 +956,7 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
object is entered::
|
||||
|
||||
async with async_session.begin():
|
||||
... # ORM transaction is begun
|
||||
# .. ORM transaction is begun
|
||||
|
||||
Note that database IO will not normally occur when the session-level
|
||||
transaction is begun, as database transactions begin on an
|
||||
@@ -1312,7 +1309,7 @@ class AsyncSession(ReversibleProxy[Session]):
|
||||
|
||||
This method retrieves the history for each instrumented
|
||||
attribute on the instance and performs a comparison of the current
|
||||
value to its previously flushed or committed value, if any.
|
||||
value to its previously committed value, if any.
|
||||
|
||||
It is in effect a more expensive and accurate
|
||||
version of checking for the given instance in the
|
||||
@@ -1636,22 +1633,16 @@ class async_sessionmaker(Generic[_AS]):
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
|
||||
|
||||
async def run_some_sql(
|
||||
async_session: async_sessionmaker[AsyncSession],
|
||||
) -> None:
|
||||
async def run_some_sql(async_session: async_sessionmaker[AsyncSession]) -> None:
|
||||
async with async_session() as session:
|
||||
session.add(SomeObject(data="object"))
|
||||
session.add(SomeOtherObject(name="other object"))
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# an AsyncEngine, which the AsyncSession will use for connection
|
||||
# resources
|
||||
engine = create_async_engine(
|
||||
"postgresql+asyncpg://scott:tiger@localhost/"
|
||||
)
|
||||
engine = create_async_engine('postgresql+asyncpg://scott:tiger@localhost/')
|
||||
|
||||
# create a reusable factory for new AsyncSession instances
|
||||
async_session = async_sessionmaker(engine)
|
||||
@@ -1695,7 +1686,8 @@ class async_sessionmaker(Generic[_AS]):
|
||||
expire_on_commit: bool = ...,
|
||||
info: Optional[_InfoType] = ...,
|
||||
**kw: Any,
|
||||
): ...
|
||||
):
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
@@ -1706,7 +1698,8 @@ class async_sessionmaker(Generic[_AS]):
|
||||
expire_on_commit: bool = ...,
|
||||
info: Optional[_InfoType] = ...,
|
||||
**kw: Any,
|
||||
): ...
|
||||
):
|
||||
...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1750,6 +1743,7 @@ class async_sessionmaker(Generic[_AS]):
|
||||
|
||||
# commits transaction, closes session
|
||||
|
||||
|
||||
"""
|
||||
|
||||
session = self()
|
||||
@@ -1782,7 +1776,7 @@ class async_sessionmaker(Generic[_AS]):
|
||||
|
||||
AsyncSession = async_sessionmaker(some_engine)
|
||||
|
||||
AsyncSession.configure(bind=create_async_engine("sqlite+aiosqlite://"))
|
||||
AsyncSession.configure(bind=create_async_engine('sqlite+aiosqlite://'))
|
||||
""" # noqa E501
|
||||
|
||||
self.kw.update(new_kw)
|
||||
@@ -1868,27 +1862,12 @@ class AsyncSessionTransaction(
|
||||
|
||||
await greenlet_spawn(self._sync_transaction().commit)
|
||||
|
||||
@classmethod
|
||||
def _regenerate_proxy_for_target( # type: ignore[override]
|
||||
cls,
|
||||
target: SessionTransaction,
|
||||
async_session: AsyncSession,
|
||||
**additional_kw: Any, # noqa: U100
|
||||
) -> AsyncSessionTransaction:
|
||||
sync_transaction = target
|
||||
nested = target.nested
|
||||
obj = cls.__new__(cls)
|
||||
obj.session = async_session
|
||||
obj.sync_transaction = obj._assign_proxied(sync_transaction)
|
||||
obj.nested = nested
|
||||
return obj
|
||||
|
||||
async def start(
|
||||
self, is_ctxmanager: bool = False
|
||||
) -> AsyncSessionTransaction:
|
||||
self.sync_transaction = self._assign_proxied(
|
||||
await greenlet_spawn(
|
||||
self.session.sync_session.begin_nested
|
||||
self.session.sync_session.begin_nested # type: ignore
|
||||
if self.nested
|
||||
else self.session.sync_session.begin
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user