SQLAlchemy 2.1.0__cp315-cp315t-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +251 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +921 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5977 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3156 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315t-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315t-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315t-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315t-win_arm64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3647 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1489 -0
  112. sqlalchemy/ext/asyncio/exc.py +30 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1787 -0
  115. sqlalchemy/ext/asyncio/session.py +2268 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2711 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2335 -0
  142. sqlalchemy/orm/decl_base.py +2300 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1205 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3615 -0
  160. sqlalchemy/orm/scoping.py +2251 -0
  161. sqlalchemy/orm/session.py +5493 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3559 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2550 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1544 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315t-win_arm64.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315t-win_arm64.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2852 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8469 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6062 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7080 -0
  206. sqlalchemy/sql/selectable.py +7644 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +496 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1990 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +777 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +176 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315t-win_arm64.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315t-win_arm64.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2803 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2268 @@
1
+ # ext/asyncio/session.py
2
+ # Copyright (C) 2020-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
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from typing import Any
11
+ from typing import Awaitable
12
+ from typing import Callable
13
+ from typing import cast
14
+ from typing import Concatenate
15
+ from typing import Dict
16
+ from typing import Generic
17
+ from typing import Iterable
18
+ from typing import Iterator
19
+ from typing import Mapping
20
+ from typing import NoReturn
21
+ from typing import Optional
22
+ from typing import overload
23
+ from typing import ParamSpec
24
+ from typing import Sequence
25
+ from typing import Tuple
26
+ from typing import Type
27
+ from typing import TYPE_CHECKING
28
+ from typing import TypeVar
29
+ from typing import Union
30
+
31
+ from . import engine
32
+ from . import exc as async_exc
33
+ from .base import ReversibleProxy
34
+ from .base import StartableContext
35
+ from .result import _ensure_sync_result
36
+ from .result import AsyncResult
37
+ from .result import AsyncScalarResult
38
+ from ... import util
39
+ from ...orm import close_all_sessions as _sync_close_all_sessions
40
+ from ...orm import object_session
41
+ from ...orm import Session
42
+ from ...orm import SessionTransaction
43
+ from ...orm import state as _instance_state
44
+ from ...util.concurrency import greenlet_spawn
45
+ from ...util.typing import Never
46
+ from ...util.typing import TupleAny
47
+ from ...util.typing import TypeVarTuple
48
+ from ...util.typing import Unpack
49
+
50
+ if TYPE_CHECKING:
51
+ from .engine import AsyncConnection
52
+ from .engine import AsyncEngine
53
+ from ...engine import Connection
54
+ from ...engine import Engine
55
+ from ...engine import Result
56
+ from ...engine import Row
57
+ from ...engine import RowMapping
58
+ from ...engine import ScalarResult
59
+ from ...engine.interfaces import _CoreAnyExecuteParams
60
+ from ...engine.interfaces import _ExecuteOptions
61
+ from ...engine.interfaces import CoreExecuteOptionsParameter
62
+ from ...event import dispatcher
63
+ from ...orm._typing import _IdentityKeyType
64
+ from ...orm._typing import _O
65
+ from ...orm._typing import OrmExecuteOptionsParameter
66
+ from ...orm.identity import IdentityMap
67
+ from ...orm.interfaces import ORMOption
68
+ from ...orm.session import _BindArguments
69
+ from ...orm.session import _EntityBindKey
70
+ from ...orm.session import _PKIdentityArgument
71
+ from ...orm.session import _SessionBind
72
+ from ...orm.session import _SessionBindKey
73
+ from ...sql._typing import _InfoType
74
+ from ...sql.base import Executable
75
+ from ...sql.elements import ClauseElement
76
+ from ...sql.selectable import ForUpdateParameter
77
+ from ...sql.selectable import TableClause
78
+ from ...sql.selectable import TypedReturnsRows
79
+ from ...util import IdentitySet
80
+
81
+ _AsyncSessionBind = Union["AsyncEngine", "AsyncConnection"]
82
+
83
+ _P = ParamSpec("_P")
84
+ _T = TypeVar("_T", bound=Any)
85
+ _Ts = TypeVarTuple("_Ts")
86
+
87
+ _EXECUTE_OPTIONS = util.immutabledict({"prebuffer_rows": True})
88
+ _STREAM_OPTIONS = util.immutabledict({"stream_results": True})
89
+
90
+
91
+ class AsyncAttrs:
92
+ """Mixin class which provides an awaitable accessor for all attributes.
93
+
94
+ E.g.::
95
+
96
+ from __future__ import annotations
97
+
98
+ from typing import List
99
+
100
+ from sqlalchemy import ForeignKey
101
+ from sqlalchemy import func
102
+ from sqlalchemy.ext.asyncio import AsyncAttrs
103
+ from sqlalchemy.orm import DeclarativeBase
104
+ from sqlalchemy.orm import Mapped
105
+ from sqlalchemy.orm import mapped_column
106
+ from sqlalchemy.orm import relationship
107
+
108
+
109
+ class Base(AsyncAttrs, DeclarativeBase):
110
+ pass
111
+
112
+
113
+ class A(Base):
114
+ __tablename__ = "a"
115
+
116
+ id: Mapped[int] = mapped_column(primary_key=True)
117
+ data: Mapped[str]
118
+ bs: Mapped[List[B]] = relationship()
119
+
120
+
121
+ class B(Base):
122
+ __tablename__ = "b"
123
+ id: Mapped[int] = mapped_column(primary_key=True)
124
+ a_id: Mapped[int] = mapped_column(ForeignKey("a.id"))
125
+ data: Mapped[str]
126
+
127
+ In the above example, the :class:`_asyncio.AsyncAttrs` mixin is applied to
128
+ the declarative ``Base`` class where it takes effect for all subclasses.
129
+ This mixin adds a single new attribute
130
+ :attr:`_asyncio.AsyncAttrs.awaitable_attrs` to all classes, which will
131
+ yield the value of any attribute as an awaitable. This allows attributes
132
+ which may be subject to lazy loading or deferred / unexpiry loading to be
133
+ accessed such that IO can still be emitted::
134
+
135
+ a1 = (await async_session.scalars(select(A).where(A.id == 5))).one()
136
+
137
+ # use the lazy loader on ``a1.bs`` via the ``.awaitable_attrs``
138
+ # interface, so that it may be awaited
139
+ for b1 in await a1.awaitable_attrs.bs:
140
+ print(b1)
141
+
142
+ The :attr:`_asyncio.AsyncAttrs.awaitable_attrs` performs a call against the
143
+ attribute that is approximately equivalent to using the
144
+ :meth:`_asyncio.AsyncSession.run_sync` method, e.g.::
145
+
146
+ for b1 in await async_session.run_sync(lambda sess: a1.bs):
147
+ print(b1)
148
+
149
+ .. versionadded:: 2.0.13
150
+
151
+ .. seealso::
152
+
153
+ :ref:`asyncio_orm_avoid_lazyloads`
154
+
155
+ """
156
+
157
+ class _AsyncAttrGetitem:
158
+ __slots__ = "_instance"
159
+
160
+ def __init__(self, _instance: Any):
161
+ self._instance = _instance
162
+
163
+ def __getattr__(self, name: str) -> Awaitable[Any]:
164
+ return greenlet_spawn(getattr, self._instance, name)
165
+
166
+ @property
167
+ def awaitable_attrs(self) -> AsyncAttrs._AsyncAttrGetitem:
168
+ """provide a namespace of all attributes on this object wrapped
169
+ as awaitables.
170
+
171
+ e.g.::
172
+
173
+
174
+ a1 = (await async_session.scalars(select(A).where(A.id == 5))).one()
175
+
176
+ some_attribute = await a1.awaitable_attrs.some_deferred_attribute
177
+ some_collection = await a1.awaitable_attrs.some_collection
178
+
179
+ """ # noqa: E501
180
+
181
+ return AsyncAttrs._AsyncAttrGetitem(self)
182
+
183
+
184
+ @util.create_proxy_methods(
185
+ Session,
186
+ ":class:`_orm.Session`",
187
+ ":class:`_asyncio.AsyncSession`",
188
+ classmethods=["object_session", "identity_key"],
189
+ methods=[
190
+ "__contains__",
191
+ "__iter__",
192
+ "add",
193
+ "add_all",
194
+ "expire",
195
+ "expire_all",
196
+ "expunge",
197
+ "expunge_all",
198
+ "is_modified",
199
+ "in_transaction",
200
+ "in_nested_transaction",
201
+ ],
202
+ attributes=[
203
+ "dirty",
204
+ "deleted",
205
+ "new",
206
+ "identity_map",
207
+ "is_active",
208
+ "autoflush",
209
+ "no_autoflush",
210
+ "info",
211
+ "execution_options",
212
+ ],
213
+ )
214
+ class AsyncSession(ReversibleProxy[Session]):
215
+ """Asyncio version of :class:`_orm.Session`.
216
+
217
+ The :class:`_asyncio.AsyncSession` is a proxy for a traditional
218
+ :class:`_orm.Session` instance.
219
+
220
+ The :class:`_asyncio.AsyncSession` is **not safe for use in concurrent
221
+ tasks.**. See :ref:`session_faq_threadsafe` for background.
222
+
223
+ .. versionadded:: 1.4
224
+
225
+ To use an :class:`_asyncio.AsyncSession` with custom :class:`_orm.Session`
226
+ implementations, see the
227
+ :paramref:`_asyncio.AsyncSession.sync_session_class` parameter.
228
+
229
+
230
+ """
231
+
232
+ _is_asyncio = True
233
+
234
+ dispatch: dispatcher[Session]
235
+
236
+ def __init__(
237
+ self,
238
+ bind: Optional[_AsyncSessionBind] = None,
239
+ *,
240
+ binds: Optional[Dict[_SessionBindKey, _AsyncSessionBind]] = None,
241
+ sync_session_class: Optional[Type[Session]] = None,
242
+ **kw: Any,
243
+ ):
244
+ r"""Construct a new :class:`_asyncio.AsyncSession`.
245
+
246
+ All parameters other than ``sync_session_class`` are passed to the
247
+ ``sync_session_class`` callable directly to instantiate a new
248
+ :class:`_orm.Session`. Refer to :meth:`_orm.Session.__init__` for
249
+ parameter documentation.
250
+
251
+ :param sync_session_class:
252
+ A :class:`_orm.Session` subclass or other callable which will be used
253
+ to construct the :class:`_orm.Session` which will be proxied. This
254
+ parameter may be used to provide custom :class:`_orm.Session`
255
+ subclasses. Defaults to the
256
+ :attr:`_asyncio.AsyncSession.sync_session_class` class-level
257
+ attribute.
258
+
259
+ .. versionadded:: 1.4.24
260
+
261
+ """
262
+ self._async_bind = bind
263
+ self._async_binds = dict(binds) if binds else {}
264
+
265
+ sync_bind = sync_binds = None
266
+
267
+ if bind:
268
+ sync_bind = engine._get_sync_engine_or_connection(bind)
269
+
270
+ if binds:
271
+ sync_binds = {
272
+ key: engine._get_sync_engine_or_connection(b)
273
+ for key, b in binds.items()
274
+ }
275
+
276
+ if sync_session_class:
277
+ self.sync_session_class = sync_session_class
278
+
279
+ self.sync_session = self._proxied = self._assign_proxied(
280
+ self.sync_session_class(bind=sync_bind, binds=sync_binds, **kw)
281
+ )
282
+
283
+ @util.memoized_property
284
+ def _translate_binds(
285
+ self,
286
+ ) -> Dict[
287
+ Union[_SessionBind, _AsyncSessionBind],
288
+ Union[_SessionBind, _AsyncSessionBind],
289
+ ]:
290
+ """The sync <-> async bind translation map, in both directions.
291
+
292
+ Establishing a bind on the :attr:`.AsyncSession.sync_session` runs the
293
+ asyncio bind through
294
+ :func:`_asyncio.engine._get_sync_engine_or_connection`, so the map is
295
+ built by running that same translation over the asyncio binds this
296
+ :class:`.AsyncSession` holds. A bind is therefore translatable only
297
+ while it remains one of this session's own, which is what allows a
298
+ lookup miss to be reported as an error rather than silently
299
+ translating an unrelated object.
300
+
301
+ The map is memoized and discarded by
302
+ :meth:`.AsyncSession._invalidate_translate_binds` whenever the binds
303
+ change; it is only ever built from binds this
304
+ :class:`.AsyncSession` was itself given, so nothing here keeps an
305
+ engine or connection alive that isn't already held by
306
+ ``_async_bind`` / ``_async_binds``.
307
+
308
+ """
309
+
310
+ translate: Dict[
311
+ Union[_SessionBind, _AsyncSessionBind],
312
+ Union[_SessionBind, _AsyncSessionBind],
313
+ ] = {}
314
+
315
+ for async_bind in (self._async_bind, *self._async_binds.values()):
316
+ if async_bind is None:
317
+ continue
318
+ sync_bind = engine._get_sync_engine_or_connection(async_bind)
319
+ translate[async_bind] = sync_bind
320
+ translate[sync_bind] = async_bind
321
+
322
+ return translate
323
+
324
+ def _invalidate_translate_binds(self) -> None:
325
+ """Discard the memoized bind translation map.
326
+
327
+ Called whenever the binds this :class:`.AsyncSession` holds change,
328
+ so that the map is rebuilt on next use.
329
+
330
+ """
331
+ util.memoized_property.reset(self, "_translate_binds")
332
+
333
+ @overload
334
+ def _translate_for_bind(self, bind: Engine) -> AsyncEngine: ...
335
+
336
+ @overload
337
+ def _translate_for_bind(self, bind: Connection) -> AsyncConnection: ...
338
+
339
+ @overload
340
+ def _translate_for_bind(self, bind: AsyncEngine) -> Engine: ...
341
+
342
+ @overload
343
+ def _translate_for_bind(self, bind: AsyncConnection) -> Connection: ...
344
+
345
+ @overload
346
+ def _translate_for_bind(self, bind: None) -> None: ...
347
+
348
+ @overload
349
+ def _translate_for_bind(self, bind: Any) -> Any: ...
350
+
351
+ def _translate_for_bind(self, bind: Any) -> Any:
352
+ if bind is None:
353
+ return None
354
+
355
+ try:
356
+ return self._translate_binds[bind]
357
+ except KeyError as ke:
358
+ raise async_exc.AsyncBindNotFound(
359
+ f"Can't translate bind {bind!r}; it is not among the binds "
360
+ f"established for this {type(self).__name__}. Binds should "
361
+ f"be established on the {type(self).__name__} rather than "
362
+ f"directly on its sync_session, so that the asyncio object "
363
+ f"each one was derived from is known."
364
+ ) from ke
365
+
366
+ @property
367
+ def bind(self) -> Optional[_AsyncSessionBind]:
368
+ """The :class:`_asyncio.AsyncEngine` or
369
+ :class:`_asyncio.AsyncConnection` this :class:`_asyncio.AsyncSession`
370
+ is bound to, if any.
371
+
372
+ The value is derived from :attr:`_orm.Session.bind` on the underlying
373
+ :attr:`_asyncio.AsyncSession.sync_session`, translated back into the
374
+ asyncio object it was established from, so that a bind assigned after
375
+ construction is reflected here as well. The attribute may be assigned
376
+ to, which establishes the bind on the
377
+ :attr:`_asyncio.AsyncSession.sync_session`.
378
+
379
+ .. versionadded:: 2.1
380
+
381
+ """
382
+
383
+ return self._translate_for_bind(self.sync_session.bind)
384
+
385
+ @bind.setter
386
+ def bind(self, bind: Optional[_AsyncSessionBind]) -> None:
387
+ if bind is not None:
388
+ sync_bind = engine._get_sync_engine_or_connection(bind)
389
+ else:
390
+ sync_bind = None
391
+ self.sync_session.bind = sync_bind
392
+
393
+ self._async_bind = bind
394
+ self._invalidate_translate_binds()
395
+
396
+ def bind_mapper(
397
+ self, mapper: _EntityBindKey[_O], bind: _AsyncSessionBind
398
+ ) -> None:
399
+ """Associate a :class:`_orm.Mapper` or arbitrary Python class with an
400
+ asyncio "bind", e.g. an :class:`_asyncio.AsyncEngine` or
401
+ :class:`_asyncio.AsyncConnection`.
402
+
403
+ This is the asyncio-facing counterpart to
404
+ :meth:`_orm.Session.bind_mapper`, which is what the given bind is
405
+ established with on the underlying
406
+ :attr:`.AsyncSession.sync_session`.
407
+
408
+ .. versionadded:: 2.1
409
+
410
+ .. seealso::
411
+
412
+ :meth:`_orm.Session.bind_mapper`
413
+
414
+ """
415
+ self.sync_session.bind_mapper(
416
+ mapper, engine._get_sync_engine_or_connection(bind)
417
+ )
418
+ self._register_async_bind(mapper, bind)
419
+
420
+ def bind_table(self, table: TableClause, bind: _AsyncSessionBind) -> None:
421
+ """Associate a :class:`_schema.Table` with an asyncio "bind", e.g. an
422
+ :class:`_asyncio.AsyncEngine` or :class:`_asyncio.AsyncConnection`.
423
+
424
+ This is the asyncio-facing counterpart to
425
+ :meth:`_orm.Session.bind_table`, which is what the given bind is
426
+ established with on the underlying
427
+ :attr:`.AsyncSession.sync_session`.
428
+
429
+ .. versionadded:: 2.1
430
+
431
+ .. seealso::
432
+
433
+ :meth:`_orm.Session.bind_table`
434
+
435
+ """
436
+ self.sync_session.bind_table(
437
+ table, engine._get_sync_engine_or_connection(bind)
438
+ )
439
+ self._register_async_bind(table, bind)
440
+
441
+ def _register_async_bind(
442
+ self, key: _SessionBindKey, bind: _AsyncSessionBind
443
+ ) -> None:
444
+ """Record the asyncio object a newly established bind came from."""
445
+
446
+ self._async_binds[key] = bind
447
+ self._invalidate_translate_binds()
448
+
449
+ @property
450
+ def binds(self) -> Mapping[_SessionBindKey, _AsyncSessionBind]:
451
+ """An immutable mapping of the per-mapper / per-table binds established
452
+ for this :class:`_asyncio.AsyncSession`.
453
+
454
+ Like :attr:`_asyncio.AsyncSession.bind`, the collection is derived from
455
+ :attr:`_orm.Session.binds` on the underlying
456
+ :attr:`_asyncio.AsyncSession.sync_session` with each bind translated
457
+ back into the asyncio object it was established from; the keys are
458
+ therefore normalized in the same way as they are for
459
+ :attr:`_orm.Session.binds`.
460
+
461
+ As the collection is derived it is read-only; binds are established by
462
+ passing the :paramref:`_asyncio.AsyncSession.binds` parameter.
463
+
464
+ .. versionadded:: 2.1
465
+
466
+ """
467
+
468
+ return util.immutabledict( # type: ignore[type-var]
469
+ {
470
+ key: self._translate_for_bind(bind)
471
+ for key, bind in self.sync_session.binds.items()
472
+ }
473
+ )
474
+
475
+ sync_session_class: Type[Session] = Session
476
+ """The class or callable that provides the
477
+ underlying :class:`_orm.Session` instance for a particular
478
+ :class:`_asyncio.AsyncSession`.
479
+
480
+ At the class level, this attribute is the default value for the
481
+ :paramref:`_asyncio.AsyncSession.sync_session_class` parameter. Custom
482
+ subclasses of :class:`_asyncio.AsyncSession` can override this.
483
+
484
+ At the instance level, this attribute indicates the current class or
485
+ callable that was used to provide the :class:`_orm.Session` instance for
486
+ this :class:`_asyncio.AsyncSession` instance.
487
+
488
+ .. versionadded:: 1.4.24
489
+
490
+ """
491
+
492
+ sync_session: Session
493
+ """Reference to the underlying :class:`_orm.Session` this
494
+ :class:`_asyncio.AsyncSession` proxies requests towards.
495
+
496
+ This instance can be used as an event target.
497
+
498
+ .. seealso::
499
+
500
+ :ref:`asyncio_events`
501
+
502
+ """
503
+
504
+ @classmethod
505
+ def _no_async_engine_events(cls) -> NoReturn:
506
+ raise NotImplementedError(
507
+ "asynchronous events are not implemented at this time. Apply "
508
+ "synchronous listeners to the AsyncSession.sync_session."
509
+ )
510
+
511
+ async def refresh(
512
+ self,
513
+ instance: object,
514
+ attribute_names: Optional[Iterable[str]] = None,
515
+ with_for_update: ForUpdateParameter = None,
516
+ ) -> None:
517
+ """Expire and refresh the attributes on the given instance.
518
+
519
+ A query will be issued to the database and all attributes will be
520
+ refreshed with their current database value.
521
+
522
+ This is the async version of the :meth:`_orm.Session.refresh` method.
523
+ See that method for a complete description of all options.
524
+
525
+ .. seealso::
526
+
527
+ :meth:`_orm.Session.refresh` - main documentation for refresh
528
+
529
+ """
530
+
531
+ await greenlet_spawn(
532
+ self.sync_session.refresh,
533
+ instance,
534
+ attribute_names=attribute_names,
535
+ with_for_update=with_for_update,
536
+ )
537
+
538
+ async def run_sync(
539
+ self,
540
+ fn: Callable[Concatenate[Session, _P], _T],
541
+ *arg: _P.args,
542
+ **kw: _P.kwargs,
543
+ ) -> _T:
544
+ '''Invoke the given synchronous (i.e. not async) callable,
545
+ passing a synchronous-style :class:`_orm.Session` as the first
546
+ argument.
547
+
548
+ This method allows traditional synchronous SQLAlchemy functions to
549
+ run within the context of an asyncio application.
550
+
551
+ E.g.::
552
+
553
+ def some_business_method(session: Session, param: str) -> str:
554
+ """A synchronous function that does not require awaiting
555
+
556
+ :param session: a SQLAlchemy Session, used synchronously
557
+
558
+ :return: an optional return value is supported
559
+
560
+ """
561
+ session.add(MyObject(param=param))
562
+ session.flush()
563
+ return "success"
564
+
565
+
566
+ async def do_something_async(async_engine: AsyncEngine) -> None:
567
+ """an async function that uses awaiting"""
568
+
569
+ with AsyncSession(async_engine) as async_session:
570
+ # run some_business_method() with a sync-style
571
+ # Session, proxied into an awaitable
572
+ return_code = await async_session.run_sync(
573
+ some_business_method, param="param1"
574
+ )
575
+ print(return_code)
576
+
577
+ This method maintains the asyncio event loop all the way through
578
+ to the database connection by running the given callable in a
579
+ specially instrumented greenlet.
580
+
581
+ .. tip::
582
+
583
+ The provided callable is invoked inline within the asyncio event
584
+ loop, and will block on traditional IO calls. IO within this
585
+ callable should only call into SQLAlchemy's asyncio database
586
+ APIs which will be properly adapted to the greenlet context.
587
+
588
+ .. seealso::
589
+
590
+ :class:`.AsyncAttrs` - a mixin for ORM mapped classes that provides
591
+ a similar feature more succinctly on a per-attribute basis
592
+
593
+ :meth:`.AsyncConnection.run_sync`
594
+
595
+ :ref:`session_run_sync`
596
+ ''' # noqa: E501
597
+
598
+ return await greenlet_spawn(
599
+ fn, self.sync_session, *arg, _require_await=False, **kw
600
+ )
601
+
602
+ @overload
603
+ async def execute(
604
+ self,
605
+ statement: TypedReturnsRows[Unpack[_Ts]],
606
+ params: Optional[_CoreAnyExecuteParams] = None,
607
+ *,
608
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
609
+ bind_arguments: Optional[_BindArguments] = None,
610
+ _parent_execute_state: Optional[Any] = None,
611
+ _add_event: Optional[Any] = None,
612
+ ) -> Result[Unpack[_Ts]]: ...
613
+
614
+ @overload
615
+ async def execute(
616
+ self,
617
+ statement: Executable,
618
+ params: Optional[_CoreAnyExecuteParams] = None,
619
+ *,
620
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
621
+ bind_arguments: Optional[_BindArguments] = None,
622
+ _parent_execute_state: Optional[Any] = None,
623
+ _add_event: Optional[Any] = None,
624
+ ) -> Result[Unpack[TupleAny]]: ...
625
+
626
+ async def execute(
627
+ self,
628
+ statement: Executable,
629
+ params: Optional[_CoreAnyExecuteParams] = None,
630
+ *,
631
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
632
+ bind_arguments: Optional[_BindArguments] = None,
633
+ **kw: Any,
634
+ ) -> Result[Unpack[TupleAny]]:
635
+ """Execute a statement and return a buffered
636
+ :class:`_engine.Result` object.
637
+
638
+ .. seealso::
639
+
640
+ :meth:`_orm.Session.execute` - main documentation for execute
641
+
642
+ """
643
+
644
+ if execution_options:
645
+ execution_options = util.immutabledict(execution_options).union(
646
+ _EXECUTE_OPTIONS
647
+ )
648
+ else:
649
+ execution_options = _EXECUTE_OPTIONS
650
+
651
+ result = await greenlet_spawn(
652
+ self.sync_session.execute,
653
+ statement,
654
+ params=params,
655
+ execution_options=execution_options,
656
+ bind_arguments=bind_arguments,
657
+ **kw,
658
+ )
659
+ return await _ensure_sync_result(result, self.execute)
660
+
661
+ # special case to handle mypy issue:
662
+ # https://github.com/python/mypy/issues/20651
663
+ @overload
664
+ async def scalar(
665
+ self,
666
+ statement: TypedReturnsRows[Never],
667
+ params: Optional[_CoreAnyExecuteParams] = None,
668
+ *,
669
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
670
+ bind_arguments: Optional[_BindArguments] = None,
671
+ **kw: Any,
672
+ ) -> Optional[Any]: ...
673
+
674
+ @overload
675
+ async def scalar(
676
+ self,
677
+ statement: TypedReturnsRows[_T],
678
+ params: Optional[_CoreAnyExecuteParams] = None,
679
+ *,
680
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
681
+ bind_arguments: Optional[_BindArguments] = None,
682
+ **kw: Any,
683
+ ) -> Optional[_T]: ...
684
+
685
+ @overload
686
+ async def scalar(
687
+ self,
688
+ statement: Executable,
689
+ params: Optional[_CoreAnyExecuteParams] = None,
690
+ *,
691
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
692
+ bind_arguments: Optional[_BindArguments] = None,
693
+ **kw: Any,
694
+ ) -> Any: ...
695
+
696
+ async def scalar(
697
+ self,
698
+ statement: Executable,
699
+ params: Optional[_CoreAnyExecuteParams] = None,
700
+ *,
701
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
702
+ bind_arguments: Optional[_BindArguments] = None,
703
+ **kw: Any,
704
+ ) -> Any:
705
+ """Execute a statement and return a scalar result.
706
+
707
+ .. seealso::
708
+
709
+ :meth:`_orm.Session.scalar` - main documentation for scalar
710
+
711
+ """
712
+
713
+ if execution_options:
714
+ execution_options = util.immutabledict(execution_options).union(
715
+ _EXECUTE_OPTIONS
716
+ )
717
+ else:
718
+ execution_options = _EXECUTE_OPTIONS
719
+
720
+ return await greenlet_spawn(
721
+ self.sync_session.scalar,
722
+ statement,
723
+ params=params,
724
+ execution_options=execution_options,
725
+ bind_arguments=bind_arguments,
726
+ **kw,
727
+ )
728
+
729
+ @overload
730
+ async def scalars(
731
+ self,
732
+ statement: TypedReturnsRows[_T],
733
+ params: Optional[_CoreAnyExecuteParams] = None,
734
+ *,
735
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
736
+ bind_arguments: Optional[_BindArguments] = None,
737
+ **kw: Any,
738
+ ) -> ScalarResult[_T]: ...
739
+
740
+ @overload
741
+ async def scalars(
742
+ self,
743
+ statement: Executable,
744
+ params: Optional[_CoreAnyExecuteParams] = None,
745
+ *,
746
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
747
+ bind_arguments: Optional[_BindArguments] = None,
748
+ **kw: Any,
749
+ ) -> ScalarResult[Any]: ...
750
+
751
+ async def scalars(
752
+ self,
753
+ statement: Executable,
754
+ params: Optional[_CoreAnyExecuteParams] = None,
755
+ *,
756
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
757
+ bind_arguments: Optional[_BindArguments] = None,
758
+ **kw: Any,
759
+ ) -> ScalarResult[Any]:
760
+ """Execute a statement and return scalar results.
761
+
762
+ :return: a :class:`_result.ScalarResult` object
763
+
764
+ .. versionadded:: 1.4.24 Added :meth:`_asyncio.AsyncSession.scalars`
765
+
766
+ .. versionadded:: 1.4.26 Added
767
+ :meth:`_asyncio.async_scoped_session.scalars`
768
+
769
+ .. seealso::
770
+
771
+ :meth:`_orm.Session.scalars` - main documentation for scalars
772
+
773
+ :meth:`_asyncio.AsyncSession.stream_scalars` - streaming version
774
+
775
+ """
776
+
777
+ result = await self.execute(
778
+ statement,
779
+ params=params,
780
+ execution_options=execution_options,
781
+ bind_arguments=bind_arguments,
782
+ **kw,
783
+ )
784
+ return result.scalars()
785
+
786
+ async def get(
787
+ self,
788
+ entity: _EntityBindKey[_O],
789
+ ident: _PKIdentityArgument,
790
+ *,
791
+ options: Optional[Sequence[ORMOption]] = None,
792
+ populate_existing: bool | None = None,
793
+ with_for_update: ForUpdateParameter = None,
794
+ identity_token: Optional[Any] = None,
795
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
796
+ ) -> Union[_O, None]:
797
+ """Return an instance based on the given primary key identifier,
798
+ or ``None`` if not found.
799
+
800
+ .. seealso::
801
+
802
+ :meth:`_orm.Session.get` - main documentation for get
803
+
804
+
805
+ """
806
+
807
+ return await greenlet_spawn(
808
+ cast("Callable[..., _O]", self.sync_session.get),
809
+ entity,
810
+ ident,
811
+ options=options,
812
+ populate_existing=populate_existing,
813
+ with_for_update=with_for_update,
814
+ identity_token=identity_token,
815
+ execution_options=execution_options,
816
+ )
817
+
818
+ async def get_one(
819
+ self,
820
+ entity: _EntityBindKey[_O],
821
+ ident: _PKIdentityArgument,
822
+ *,
823
+ options: Optional[Sequence[ORMOption]] = None,
824
+ populate_existing: bool | None = None,
825
+ with_for_update: ForUpdateParameter = None,
826
+ identity_token: Optional[Any] = None,
827
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
828
+ ) -> _O:
829
+ """Return an instance based on the given primary key identifier,
830
+ or raise an exception if not found.
831
+
832
+ Raises :class:`_exc.NoResultFound` if the query selects no rows.
833
+
834
+ ..versionadded: 2.0.22
835
+
836
+ .. seealso::
837
+
838
+ :meth:`_orm.Session.get_one` - main documentation for get_one
839
+
840
+ """
841
+
842
+ return await greenlet_spawn(
843
+ cast("Callable[..., _O]", self.sync_session.get_one),
844
+ entity,
845
+ ident,
846
+ options=options,
847
+ populate_existing=populate_existing,
848
+ with_for_update=with_for_update,
849
+ identity_token=identity_token,
850
+ execution_options=execution_options,
851
+ )
852
+
853
+ @overload
854
+ async def stream(
855
+ self,
856
+ statement: TypedReturnsRows[Unpack[_Ts]],
857
+ params: Optional[_CoreAnyExecuteParams] = None,
858
+ *,
859
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
860
+ bind_arguments: Optional[_BindArguments] = None,
861
+ **kw: Any,
862
+ ) -> AsyncResult[Unpack[_Ts]]: ...
863
+
864
+ @overload
865
+ async def stream(
866
+ self,
867
+ statement: Executable,
868
+ params: Optional[_CoreAnyExecuteParams] = None,
869
+ *,
870
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
871
+ bind_arguments: Optional[_BindArguments] = None,
872
+ **kw: Any,
873
+ ) -> AsyncResult[Unpack[TupleAny]]: ...
874
+
875
+ async def stream(
876
+ self,
877
+ statement: Executable,
878
+ params: Optional[_CoreAnyExecuteParams] = None,
879
+ *,
880
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
881
+ bind_arguments: Optional[_BindArguments] = None,
882
+ **kw: Any,
883
+ ) -> AsyncResult[Unpack[TupleAny]]:
884
+ """Execute a statement and return a streaming
885
+ :class:`_asyncio.AsyncResult` object.
886
+
887
+ """
888
+
889
+ if execution_options:
890
+ execution_options = util.immutabledict(execution_options).union(
891
+ _STREAM_OPTIONS
892
+ )
893
+ else:
894
+ execution_options = _STREAM_OPTIONS
895
+
896
+ result = await greenlet_spawn(
897
+ self.sync_session.execute,
898
+ statement,
899
+ params=params,
900
+ execution_options=execution_options,
901
+ bind_arguments=bind_arguments,
902
+ **kw,
903
+ )
904
+ return AsyncResult(result)
905
+
906
+ @overload
907
+ async def stream_scalars(
908
+ self,
909
+ statement: TypedReturnsRows[_T],
910
+ params: Optional[_CoreAnyExecuteParams] = None,
911
+ *,
912
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
913
+ bind_arguments: Optional[_BindArguments] = None,
914
+ **kw: Any,
915
+ ) -> AsyncScalarResult[_T]: ...
916
+
917
+ @overload
918
+ async def stream_scalars(
919
+ self,
920
+ statement: Executable,
921
+ params: Optional[_CoreAnyExecuteParams] = None,
922
+ *,
923
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
924
+ bind_arguments: Optional[_BindArguments] = None,
925
+ **kw: Any,
926
+ ) -> AsyncScalarResult[Any]: ...
927
+
928
+ async def stream_scalars(
929
+ self,
930
+ statement: Executable,
931
+ params: Optional[_CoreAnyExecuteParams] = None,
932
+ *,
933
+ execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
934
+ bind_arguments: Optional[_BindArguments] = None,
935
+ **kw: Any,
936
+ ) -> AsyncScalarResult[Any]:
937
+ """Execute a statement and return a stream of scalar results.
938
+
939
+ :return: an :class:`_asyncio.AsyncScalarResult` object
940
+
941
+ .. versionadded:: 1.4.24
942
+
943
+ .. seealso::
944
+
945
+ :meth:`_orm.Session.scalars` - main documentation for scalars
946
+
947
+ :meth:`_asyncio.AsyncSession.scalars` - non streaming version
948
+
949
+ """
950
+
951
+ result = await self.stream(
952
+ statement,
953
+ params=params,
954
+ execution_options=execution_options,
955
+ bind_arguments=bind_arguments,
956
+ **kw,
957
+ )
958
+ return result.scalars()
959
+
960
+ async def delete(self, instance: object) -> None:
961
+ """Mark an instance as deleted.
962
+
963
+ The database delete operation occurs upon ``flush()``.
964
+
965
+ As this operation may need to cascade along unloaded relationships,
966
+ it is awaitable to allow for those queries to take place.
967
+
968
+ .. seealso::
969
+
970
+ :meth:`_orm.Session.delete` - main documentation for delete
971
+
972
+ """
973
+ await greenlet_spawn(self.sync_session.delete, instance)
974
+
975
+ async def delete_all(self, instances: Iterable[object]) -> None:
976
+ """Calls :meth:`.AsyncSession.delete` on multiple instances.
977
+
978
+ .. seealso::
979
+
980
+ :meth:`_orm.Session.delete_all` - main documentation for delete_all
981
+
982
+ """
983
+ await greenlet_spawn(self.sync_session.delete_all, instances)
984
+
985
+ async def merge(
986
+ self,
987
+ instance: _O,
988
+ *,
989
+ load: bool = True,
990
+ options: Optional[Sequence[ORMOption]] = None,
991
+ ) -> _O:
992
+ """Copy the state of a given instance into a corresponding instance
993
+ within this :class:`_asyncio.AsyncSession`.
994
+
995
+ .. seealso::
996
+
997
+ :meth:`_orm.Session.merge` - main documentation for merge
998
+
999
+ """
1000
+ return await greenlet_spawn(
1001
+ self.sync_session.merge, instance, load=load, options=options
1002
+ )
1003
+
1004
+ async def merge_all(
1005
+ self,
1006
+ instances: Iterable[_O],
1007
+ *,
1008
+ load: bool = True,
1009
+ options: Optional[Sequence[ORMOption]] = None,
1010
+ ) -> Sequence[_O]:
1011
+ """Calls :meth:`.AsyncSession.merge` on multiple instances.
1012
+
1013
+ .. seealso::
1014
+
1015
+ :meth:`_orm.Session.merge_all` - main documentation for merge_all
1016
+
1017
+ """
1018
+ return await greenlet_spawn(
1019
+ self.sync_session.merge_all, instances, load=load, options=options
1020
+ )
1021
+
1022
+ async def flush(self, objects: Optional[Sequence[Any]] = None) -> None:
1023
+ """Flush all the object changes to the database.
1024
+
1025
+ .. seealso::
1026
+
1027
+ :meth:`_orm.Session.flush` - main documentation for flush
1028
+
1029
+ """
1030
+ await greenlet_spawn(self.sync_session.flush, objects=objects)
1031
+
1032
+ def get_transaction(self) -> Optional[AsyncSessionTransaction]:
1033
+ """Return the current root transaction in progress, if any.
1034
+
1035
+ :return: an :class:`_asyncio.AsyncSessionTransaction` object, or
1036
+ ``None``.
1037
+
1038
+ .. versionadded:: 1.4.18
1039
+
1040
+ """
1041
+ trans = self.sync_session.get_transaction()
1042
+ if trans is not None:
1043
+ return AsyncSessionTransaction._retrieve_proxy_for_target(
1044
+ trans, async_session=self
1045
+ )
1046
+ else:
1047
+ return None
1048
+
1049
+ def get_nested_transaction(self) -> Optional[AsyncSessionTransaction]:
1050
+ """Return the current nested transaction in progress, if any.
1051
+
1052
+ :return: an :class:`_asyncio.AsyncSessionTransaction` object, or
1053
+ ``None``.
1054
+
1055
+ .. versionadded:: 1.4.18
1056
+
1057
+ """
1058
+
1059
+ trans = self.sync_session.get_nested_transaction()
1060
+ if trans is not None:
1061
+ return AsyncSessionTransaction._retrieve_proxy_for_target(
1062
+ trans, async_session=self
1063
+ )
1064
+ else:
1065
+ return None
1066
+
1067
+ def get_bind(
1068
+ self,
1069
+ mapper: Optional[_EntityBindKey[_O]] = None,
1070
+ clause: Optional[ClauseElement] = None,
1071
+ bind: Optional[_SessionBind] = None,
1072
+ **kw: Any,
1073
+ ) -> Union[Engine, Connection]:
1074
+ """Return a "bind" to which the synchronous proxied :class:`_orm.Session`
1075
+ is bound.
1076
+
1077
+ Unlike the :meth:`_orm.Session.get_bind` method, this method is
1078
+ currently **not** used by this :class:`.AsyncSession` in any way
1079
+ in order to resolve engines for requests.
1080
+
1081
+ .. note::
1082
+
1083
+ This method proxies directly to the :meth:`_orm.Session.get_bind`
1084
+ method, however is currently **not** useful as an override target,
1085
+ in contrast to that of the :meth:`_orm.Session.get_bind` method.
1086
+ The example below illustrates how to implement custom
1087
+ :meth:`_orm.Session.get_bind` schemes that work with
1088
+ :class:`.AsyncSession` and :class:`.AsyncEngine`.
1089
+
1090
+ The pattern introduced at :ref:`session_custom_partitioning`
1091
+ illustrates how to apply a custom bind-lookup scheme to a
1092
+ :class:`_orm.Session` given a set of :class:`_engine.Engine` objects.
1093
+ To apply a corresponding :meth:`_orm.Session.get_bind` implementation
1094
+ for use with a :class:`.AsyncSession` and :class:`.AsyncEngine`
1095
+ objects, continue to subclass :class:`_orm.Session` and apply it to
1096
+ :class:`.AsyncSession` using
1097
+ :paramref:`.AsyncSession.sync_session_class`. The inner method must
1098
+ continue to return :class:`_engine.Engine` instances, which can be
1099
+ acquired from a :class:`_asyncio.AsyncEngine` using the
1100
+ :attr:`_asyncio.AsyncEngine.sync_engine` attribute::
1101
+
1102
+ # using example from "Custom Vertical Partitioning"
1103
+
1104
+
1105
+ import random
1106
+
1107
+ from sqlalchemy.ext.asyncio import AsyncSession
1108
+ from sqlalchemy.ext.asyncio import create_async_engine
1109
+ from sqlalchemy.ext.asyncio import async_sessionmaker
1110
+ from sqlalchemy.orm import Session
1111
+
1112
+ # construct async engines w/ async drivers
1113
+ engines = {
1114
+ "leader": create_async_engine("sqlite+aiosqlite:///leader.db"),
1115
+ "other": create_async_engine("sqlite+aiosqlite:///other.db"),
1116
+ "follower1": create_async_engine("sqlite+aiosqlite:///follower1.db"),
1117
+ "follower2": create_async_engine("sqlite+aiosqlite:///follower2.db"),
1118
+ }
1119
+
1120
+
1121
+ class RoutingSession(Session):
1122
+ def get_bind(self, mapper=None, clause=None, **kw):
1123
+ # within get_bind(), return sync engines
1124
+ if mapper and issubclass(mapper.class_, MyOtherClass):
1125
+ return engines["other"].sync_engine
1126
+ elif self._flushing or isinstance(clause, (Update, Delete)):
1127
+ return engines["leader"].sync_engine
1128
+ else:
1129
+ return engines[
1130
+ random.choice(["follower1", "follower2"])
1131
+ ].sync_engine
1132
+
1133
+
1134
+ # apply to AsyncSession using sync_session_class
1135
+ AsyncSessionMaker = async_sessionmaker(sync_session_class=RoutingSession)
1136
+
1137
+ The :meth:`_orm.Session.get_bind` method is called in a non-asyncio,
1138
+ implicitly non-blocking context in the same manner as ORM event hooks
1139
+ and functions that are invoked via :meth:`.AsyncSession.run_sync`, so
1140
+ routines that wish to run SQL commands inside of
1141
+ :meth:`_orm.Session.get_bind` can continue to do so using
1142
+ blocking-style code, which will be translated to implicitly async calls
1143
+ at the point of invoking IO on the database drivers.
1144
+
1145
+ .. seealso::
1146
+
1147
+ :meth:`.AsyncSession.get_async_bind`
1148
+
1149
+ """ # noqa: E501
1150
+
1151
+ return self.sync_session.get_bind(
1152
+ mapper=mapper, clause=clause, bind=bind, **kw
1153
+ )
1154
+
1155
+ def get_async_bind(
1156
+ self,
1157
+ mapper: Optional[_EntityBindKey[_O]] = None,
1158
+ clause: Optional[ClauseElement] = None,
1159
+ bind: Optional[_AsyncSessionBind] = None,
1160
+ **kw: Any,
1161
+ ) -> _AsyncSessionBind:
1162
+ """Return a "bind" to which this :class:`.AsyncSession` is bound.
1163
+
1164
+ This is the asyncio-facing counterpart to
1165
+ :meth:`.AsyncSession.get_bind`; the bind resolved against the
1166
+ underlying :attr:`.AsyncSession.sync_session` is translated back into
1167
+ the :class:`.AsyncEngine` or :class:`.AsyncConnection` that it was
1168
+ derived from. :paramref:`.AsyncSession.get_async_bind.bind` is
1169
+ likewise given as an asyncio object, and is translated on the way in.
1170
+
1171
+ Raises :class:`_asyncio.exc.AsyncBindNotFound` if the bind that's
1172
+ resolved has no asyncio counterpart known to this
1173
+ :class:`.AsyncSession`.
1174
+
1175
+ Like :meth:`.AsyncSession.get_bind`, this method is currently
1176
+ **not** used by this :class:`.AsyncSession` in any way in order to
1177
+ resolve engines for requests.
1178
+
1179
+ .. note::
1180
+
1181
+ This method delegates to :meth:`.AsyncSession.get_bind`, and is
1182
+ likewise currently **not** useful as an override target, in
1183
+ contrast to that of the :meth:`_orm.Session.get_bind` method.
1184
+ To apply a custom bind-lookup scheme to an
1185
+ :class:`.AsyncSession`, subclass :class:`_orm.Session` and apply
1186
+ it using :paramref:`.AsyncSession.sync_session_class`, as
1187
+ illustrated at :meth:`.AsyncSession.get_bind`.
1188
+
1189
+ .. versionadded:: 2.1
1190
+
1191
+ .. seealso::
1192
+
1193
+ :meth:`.AsyncSession.get_bind`
1194
+
1195
+ """
1196
+
1197
+ sync_bind: Optional[_SessionBind]
1198
+
1199
+ if bind is not None:
1200
+ sync_bind = engine._get_sync_engine_or_connection(bind)
1201
+ else:
1202
+ sync_bind = None
1203
+
1204
+ resolved = self.get_bind(
1205
+ mapper=mapper, clause=clause, bind=sync_bind, **kw
1206
+ )
1207
+
1208
+ if bind is not None and resolved is sync_bind:
1209
+ # get_bind() hands back a bind given to it as is; return the
1210
+ # asyncio object it was translated from, which need not be one of
1211
+ # this session's own binds for _translate_for_bind() to accept
1212
+ return bind
1213
+
1214
+ return self._translate_for_bind(resolved)
1215
+
1216
+ async def connection(
1217
+ self,
1218
+ bind_arguments: Optional[_BindArguments] = None,
1219
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
1220
+ **kw: Any,
1221
+ ) -> AsyncConnection:
1222
+ r"""Return a :class:`_asyncio.AsyncConnection` object corresponding to
1223
+ this :class:`.Session` object's transactional state.
1224
+
1225
+ This method may also be used to establish execution options for the
1226
+ database connection used by the current transaction.
1227
+
1228
+ .. versionadded:: 1.4.24 Added \**kw arguments which are passed
1229
+ through to the underlying :meth:`_orm.Session.connection` method.
1230
+
1231
+ .. seealso::
1232
+
1233
+ :meth:`_orm.Session.connection` - main documentation for
1234
+ "connection"
1235
+
1236
+ """
1237
+
1238
+ sync_connection = await greenlet_spawn(
1239
+ self.sync_session.connection,
1240
+ bind_arguments=bind_arguments,
1241
+ execution_options=execution_options,
1242
+ **kw,
1243
+ )
1244
+ return engine.AsyncConnection._retrieve_proxy_for_target(
1245
+ sync_connection
1246
+ )
1247
+
1248
+ def begin(self) -> AsyncSessionTransaction:
1249
+ """Return an :class:`_asyncio.AsyncSessionTransaction` object.
1250
+
1251
+ The underlying :class:`_orm.Session` will perform the
1252
+ "begin" action when the :class:`_asyncio.AsyncSessionTransaction`
1253
+ object is entered::
1254
+
1255
+ async with async_session.begin():
1256
+ ... # ORM transaction is begun
1257
+
1258
+ Note that database IO will not normally occur when the session-level
1259
+ transaction is begun, as database transactions begin on an
1260
+ on-demand basis. However, the begin block is async to accommodate
1261
+ for a :meth:`_orm.SessionEvents.after_transaction_create`
1262
+ event hook that may perform IO.
1263
+
1264
+ For a general description of ORM begin, see
1265
+ :meth:`_orm.Session.begin`.
1266
+
1267
+ """
1268
+
1269
+ return AsyncSessionTransaction(self)
1270
+
1271
+ def begin_nested(self) -> AsyncSessionTransaction:
1272
+ """Return an :class:`_asyncio.AsyncSessionTransaction` object
1273
+ which will begin a "nested" transaction, e.g. SAVEPOINT.
1274
+
1275
+ Behavior is the same as that of :meth:`_asyncio.AsyncSession.begin`.
1276
+
1277
+ For a general description of ORM begin nested, see
1278
+ :meth:`_orm.Session.begin_nested`.
1279
+
1280
+ .. seealso::
1281
+
1282
+ :ref:`aiosqlite_serializable` - special workarounds required
1283
+ with the SQLite asyncio driver in order for SAVEPOINT to work
1284
+ correctly.
1285
+
1286
+ """
1287
+
1288
+ return AsyncSessionTransaction(self, nested=True)
1289
+
1290
+ async def rollback(self) -> None:
1291
+ """Rollback the current transaction in progress.
1292
+
1293
+ .. seealso::
1294
+
1295
+ :meth:`_orm.Session.rollback` - main documentation for
1296
+ "rollback"
1297
+ """
1298
+ await greenlet_spawn(self.sync_session.rollback)
1299
+
1300
+ async def commit(self) -> None:
1301
+ """Commit the current transaction in progress.
1302
+
1303
+ .. seealso::
1304
+
1305
+ :meth:`_orm.Session.commit` - main documentation for
1306
+ "commit"
1307
+ """
1308
+ await greenlet_spawn(self.sync_session.commit)
1309
+
1310
+ async def close(self) -> None:
1311
+ """Close out the transactional resources and ORM objects used by this
1312
+ :class:`_asyncio.AsyncSession`.
1313
+
1314
+ .. seealso::
1315
+
1316
+ :meth:`_orm.Session.close` - main documentation for
1317
+ "close"
1318
+
1319
+ :ref:`session_closing` - detail on the semantics of
1320
+ :meth:`_asyncio.AsyncSession.close` and
1321
+ :meth:`_asyncio.AsyncSession.reset`.
1322
+
1323
+ """
1324
+ await greenlet_spawn(self.sync_session.close)
1325
+
1326
+ async def reset(self) -> None:
1327
+ """Close out the transactional resources and ORM objects used by this
1328
+ :class:`_orm.Session`, resetting the session to its initial state.
1329
+
1330
+ .. versionadded:: 2.0.22
1331
+
1332
+ .. seealso::
1333
+
1334
+ :meth:`_orm.Session.reset` - main documentation for
1335
+ "reset"
1336
+
1337
+ :ref:`session_closing` - detail on the semantics of
1338
+ :meth:`_asyncio.AsyncSession.close` and
1339
+ :meth:`_asyncio.AsyncSession.reset`.
1340
+
1341
+ """
1342
+ await greenlet_spawn(self.sync_session.reset)
1343
+
1344
+ async def aclose(self) -> None:
1345
+ """A synonym for :meth:`_asyncio.AsyncSession.close`.
1346
+
1347
+ The :meth:`_asyncio.AsyncSession.aclose` name is specifically
1348
+ to support the Python standard library ``@contextlib.aclosing``
1349
+ context manager function.
1350
+
1351
+ .. versionadded:: 2.0.20
1352
+
1353
+ """
1354
+ await self.close()
1355
+
1356
+ async def invalidate(self) -> None:
1357
+ """Close this Session, using connection invalidation.
1358
+
1359
+ For a complete description, see :meth:`_orm.Session.invalidate`.
1360
+ """
1361
+ await greenlet_spawn(self.sync_session.invalidate)
1362
+
1363
+ @classmethod
1364
+ @util.deprecated(
1365
+ "2.0",
1366
+ "The :meth:`.AsyncSession.close_all` method is deprecated and will be "
1367
+ "removed in a future release. Please refer to "
1368
+ ":func:`_asyncio.close_all_sessions`.",
1369
+ )
1370
+ async def close_all(cls) -> None:
1371
+ """Close all :class:`_asyncio.AsyncSession` sessions."""
1372
+ await close_all_sessions()
1373
+
1374
+ async def __aenter__(self: _AS) -> _AS:
1375
+ return self
1376
+
1377
+ async def __aexit__(self, type_: Any, value: Any, traceback: Any) -> None:
1378
+ task = asyncio.create_task(self.close())
1379
+ await asyncio.shield(task)
1380
+
1381
+ def _maker_context_manager(self: _AS) -> _AsyncSessionContextManager[_AS]:
1382
+ return _AsyncSessionContextManager(self)
1383
+
1384
+ # START PROXY METHODS AsyncSession
1385
+
1386
+ # code within this block is **programmatically,
1387
+ # statically generated** by tools/generate_proxy_methods.py
1388
+
1389
+ def __contains__(self, instance: object) -> bool:
1390
+ r"""Return True if the instance is associated with this session.
1391
+
1392
+ .. container:: class_bases
1393
+
1394
+ Proxied for the :class:`_orm.Session` class on
1395
+ behalf of the :class:`_asyncio.AsyncSession` class.
1396
+
1397
+ The instance may be pending or persistent within the Session for a
1398
+ result of True.
1399
+
1400
+
1401
+ """ # noqa: E501
1402
+
1403
+ return self._proxied.__contains__(instance)
1404
+
1405
+ def __iter__(self) -> Iterator[object]:
1406
+ r"""Iterate over all pending or persistent instances within this
1407
+ Session.
1408
+
1409
+ .. container:: class_bases
1410
+
1411
+ Proxied for the :class:`_orm.Session` class on
1412
+ behalf of the :class:`_asyncio.AsyncSession` class.
1413
+
1414
+
1415
+ """ # noqa: E501
1416
+
1417
+ return self._proxied.__iter__()
1418
+
1419
+ def add(self, instance: object, *, _warn: bool = True) -> None:
1420
+ r"""Place an object into this :class:`_orm.Session`.
1421
+
1422
+ .. container:: class_bases
1423
+
1424
+ Proxied for the :class:`_orm.Session` class on
1425
+ behalf of the :class:`_asyncio.AsyncSession` class.
1426
+
1427
+ Objects that are in the :term:`transient` state when passed to the
1428
+ :meth:`_orm.Session.add` method will move to the
1429
+ :term:`pending` state, until the next flush, at which point they
1430
+ will move to the :term:`persistent` state.
1431
+
1432
+ Objects that are in the :term:`detached` state when passed to the
1433
+ :meth:`_orm.Session.add` method will move to the :term:`persistent`
1434
+ state directly.
1435
+
1436
+ If the transaction used by the :class:`_orm.Session` is rolled back,
1437
+ objects which were transient when they were passed to
1438
+ :meth:`_orm.Session.add` will be moved back to the
1439
+ :term:`transient` state, and will no longer be present within this
1440
+ :class:`_orm.Session`.
1441
+
1442
+ .. seealso::
1443
+
1444
+ :meth:`_orm.Session.add_all`
1445
+
1446
+ :ref:`session_adding` - at :ref:`session_basics`
1447
+
1448
+
1449
+ """ # noqa: E501
1450
+
1451
+ return self._proxied.add(instance, _warn=_warn)
1452
+
1453
+ def add_all(self, instances: Iterable[object]) -> None:
1454
+ r"""Add the given collection of instances to this :class:`_orm.Session`.
1455
+
1456
+ .. container:: class_bases
1457
+
1458
+ Proxied for the :class:`_orm.Session` class on
1459
+ behalf of the :class:`_asyncio.AsyncSession` class.
1460
+
1461
+ See the documentation for :meth:`_orm.Session.add` for a general
1462
+ behavioral description.
1463
+
1464
+ .. seealso::
1465
+
1466
+ :meth:`_orm.Session.add`
1467
+
1468
+ :ref:`session_adding` - at :ref:`session_basics`
1469
+
1470
+
1471
+ """ # noqa: E501
1472
+
1473
+ return self._proxied.add_all(instances)
1474
+
1475
+ def expire(
1476
+ self, instance: object, attribute_names: Optional[Iterable[str]] = None
1477
+ ) -> None:
1478
+ r"""Expire the attributes on an instance.
1479
+
1480
+ .. container:: class_bases
1481
+
1482
+ Proxied for the :class:`_orm.Session` class on
1483
+ behalf of the :class:`_asyncio.AsyncSession` class.
1484
+
1485
+ Marks the attributes of an instance as out of date. When an expired
1486
+ attribute is next accessed, a query will be issued to the
1487
+ :class:`.Session` object's current transactional context in order to
1488
+ load all expired attributes for the given instance. Note that
1489
+ a highly isolated transaction will return the same values as were
1490
+ previously read in that same transaction, regardless of changes
1491
+ in database state outside of that transaction.
1492
+
1493
+ To expire all objects in the :class:`.Session` simultaneously,
1494
+ use :meth:`Session.expire_all`.
1495
+
1496
+ The :class:`.Session` object's default behavior is to
1497
+ expire all state whenever the :meth:`Session.rollback`
1498
+ or :meth:`Session.commit` methods are called, so that new
1499
+ state can be loaded for the new transaction. For this reason,
1500
+ calling :meth:`Session.expire` only makes sense for the specific
1501
+ case that a non-ORM SQL statement was emitted in the current
1502
+ transaction.
1503
+
1504
+ :param instance: The instance to be refreshed.
1505
+ :param attribute_names: optional list of string attribute names
1506
+ indicating a subset of attributes to be expired.
1507
+
1508
+ .. seealso::
1509
+
1510
+ :ref:`session_expire` - introductory material
1511
+
1512
+ :meth:`.Session.expire`
1513
+
1514
+ :meth:`.Session.refresh`
1515
+
1516
+ :meth:`_orm.Query.populate_existing`
1517
+
1518
+
1519
+ """ # noqa: E501
1520
+
1521
+ return self._proxied.expire(instance, attribute_names=attribute_names)
1522
+
1523
+ def expire_all(self) -> None:
1524
+ r"""Expires all persistent instances within this Session.
1525
+
1526
+ .. container:: class_bases
1527
+
1528
+ Proxied for the :class:`_orm.Session` class on
1529
+ behalf of the :class:`_asyncio.AsyncSession` class.
1530
+
1531
+ When any attributes on a persistent instance is next accessed,
1532
+ a query will be issued using the
1533
+ :class:`.Session` object's current transactional context in order to
1534
+ load all expired attributes for the given instance. Note that
1535
+ a highly isolated transaction will return the same values as were
1536
+ previously read in that same transaction, regardless of changes
1537
+ in database state outside of that transaction.
1538
+
1539
+ To expire individual objects and individual attributes
1540
+ on those objects, use :meth:`Session.expire`.
1541
+
1542
+ The :class:`.Session` object's default behavior is to
1543
+ expire all state whenever the :meth:`Session.rollback`
1544
+ or :meth:`Session.commit` methods are called, so that new
1545
+ state can be loaded for the new transaction. For this reason,
1546
+ calling :meth:`Session.expire_all` is not usually needed,
1547
+ assuming the transaction is isolated.
1548
+
1549
+ .. seealso::
1550
+
1551
+ :ref:`session_expire` - introductory material
1552
+
1553
+ :meth:`.Session.expire`
1554
+
1555
+ :meth:`.Session.refresh`
1556
+
1557
+ :meth:`_orm.Query.populate_existing`
1558
+
1559
+
1560
+ """ # noqa: E501
1561
+
1562
+ return self._proxied.expire_all()
1563
+
1564
+ def expunge(self, instance: object) -> None:
1565
+ r"""Remove the `instance` from this ``Session``.
1566
+
1567
+ .. container:: class_bases
1568
+
1569
+ Proxied for the :class:`_orm.Session` class on
1570
+ behalf of the :class:`_asyncio.AsyncSession` class.
1571
+
1572
+ This will free all internal references to the instance. Cascading
1573
+ will be applied according to the *expunge* cascade rule.
1574
+
1575
+
1576
+ """ # noqa: E501
1577
+
1578
+ return self._proxied.expunge(instance)
1579
+
1580
+ def expunge_all(self) -> None:
1581
+ r"""Remove all object instances from this ``Session``.
1582
+
1583
+ .. container:: class_bases
1584
+
1585
+ Proxied for the :class:`_orm.Session` class on
1586
+ behalf of the :class:`_asyncio.AsyncSession` class.
1587
+
1588
+ This is equivalent to calling ``expunge(obj)`` on all objects in this
1589
+ ``Session``.
1590
+
1591
+
1592
+ """ # noqa: E501
1593
+
1594
+ return self._proxied.expunge_all()
1595
+
1596
+ def is_modified(
1597
+ self, instance: object, include_collections: bool = True
1598
+ ) -> bool:
1599
+ r"""Return ``True`` if the given instance has locally
1600
+ modified attributes.
1601
+
1602
+ .. container:: class_bases
1603
+
1604
+ Proxied for the :class:`_orm.Session` class on
1605
+ behalf of the :class:`_asyncio.AsyncSession` class.
1606
+
1607
+ This method retrieves the history for each instrumented
1608
+ attribute on the instance and performs a comparison of the current
1609
+ value to its previously flushed or committed value, if any.
1610
+
1611
+ It is in effect a more expensive and accurate
1612
+ version of checking for the given instance in the
1613
+ :attr:`.Session.dirty` collection; a full test for
1614
+ each attribute's net "dirty" status is performed.
1615
+
1616
+ E.g.::
1617
+
1618
+ return session.is_modified(someobject)
1619
+
1620
+ A few caveats to this method apply:
1621
+
1622
+ * Instances present in the :attr:`.Session.dirty` collection may
1623
+ report ``False`` when tested with this method. This is because
1624
+ the object may have received change events via attribute mutation,
1625
+ thus placing it in :attr:`.Session.dirty`, but ultimately the state
1626
+ is the same as that loaded from the database, resulting in no net
1627
+ change here.
1628
+ * Scalar attributes may not have recorded the previously set
1629
+ value when a new value was applied, if the attribute was not loaded,
1630
+ or was expired, at the time the new value was received - in these
1631
+ cases, the attribute is assumed to have a change, even if there is
1632
+ ultimately no net change against its database value. SQLAlchemy in
1633
+ most cases does not need the "old" value when a set event occurs, so
1634
+ it skips the expense of a SQL call if the old value isn't present,
1635
+ based on the assumption that an UPDATE of the scalar value is
1636
+ usually needed, and in those few cases where it isn't, is less
1637
+ expensive on average than issuing a defensive SELECT.
1638
+
1639
+ The "old" value is fetched unconditionally upon set only if the
1640
+ attribute container has the ``active_history`` flag set to ``True``.
1641
+ This flag is set typically for primary key attributes and scalar
1642
+ object references that are not a simple many-to-one. To set this
1643
+ flag for any arbitrary mapped column, use the ``active_history``
1644
+ argument with :func:`.column_property`.
1645
+
1646
+ :param instance: mapped instance to be tested for pending changes.
1647
+ :param include_collections: Indicates if multivalued collections
1648
+ should be included in the operation. Setting this to ``False`` is a
1649
+ way to detect only local-column based properties (i.e. scalar columns
1650
+ or many-to-one foreign keys) that would result in an UPDATE for this
1651
+ instance upon flush.
1652
+
1653
+
1654
+ """ # noqa: E501
1655
+
1656
+ return self._proxied.is_modified(
1657
+ instance, include_collections=include_collections
1658
+ )
1659
+
1660
+ def in_transaction(self) -> bool:
1661
+ r"""Return True if this :class:`_orm.Session` has begun a transaction.
1662
+
1663
+ .. container:: class_bases
1664
+
1665
+ Proxied for the :class:`_orm.Session` class on
1666
+ behalf of the :class:`_asyncio.AsyncSession` class.
1667
+
1668
+ .. versionadded:: 1.4
1669
+
1670
+ .. seealso::
1671
+
1672
+ :attr:`_orm.Session.is_active`
1673
+
1674
+
1675
+
1676
+ """ # noqa: E501
1677
+
1678
+ return self._proxied.in_transaction()
1679
+
1680
+ def in_nested_transaction(self) -> bool:
1681
+ r"""Return True if this :class:`_orm.Session` has begun a nested
1682
+ transaction, e.g. SAVEPOINT.
1683
+
1684
+ .. container:: class_bases
1685
+
1686
+ Proxied for the :class:`_orm.Session` class on
1687
+ behalf of the :class:`_asyncio.AsyncSession` class.
1688
+
1689
+ .. versionadded:: 1.4
1690
+
1691
+
1692
+ """ # noqa: E501
1693
+
1694
+ return self._proxied.in_nested_transaction()
1695
+
1696
+ @property
1697
+ def dirty(self) -> IdentitySet:
1698
+ r"""The set of all persistent instances considered dirty.
1699
+
1700
+ .. container:: class_bases
1701
+
1702
+ Proxied for the :class:`_orm.Session` class
1703
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1704
+
1705
+ E.g.::
1706
+
1707
+ some_mapped_object in session.dirty
1708
+
1709
+ Instances are considered dirty when they were modified but not
1710
+ deleted.
1711
+
1712
+ Note that this 'dirty' calculation is 'optimistic'; most
1713
+ attribute-setting or collection modification operations will
1714
+ mark an instance as 'dirty' and place it in this set, even if
1715
+ there is no net change to the attribute's value. At flush
1716
+ time, the value of each attribute is compared to its
1717
+ previously saved value, and if there's no net change, no SQL
1718
+ operation will occur (this is a more expensive operation so
1719
+ it's only done at flush time).
1720
+
1721
+ To check if an instance has actionable net changes to its
1722
+ attributes, use the :meth:`.Session.is_modified` method.
1723
+
1724
+
1725
+ """ # noqa: E501
1726
+
1727
+ return self._proxied.dirty
1728
+
1729
+ @property
1730
+ def deleted(self) -> IdentitySet:
1731
+ r"""The set of all instances marked as 'deleted' within this ``Session``
1732
+
1733
+ .. container:: class_bases
1734
+
1735
+ Proxied for the :class:`_orm.Session` class
1736
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1737
+
1738
+ """ # noqa: E501
1739
+
1740
+ return self._proxied.deleted
1741
+
1742
+ @property
1743
+ def new(self) -> IdentitySet:
1744
+ r"""The set of all instances marked as 'new' within this ``Session``.
1745
+
1746
+ .. container:: class_bases
1747
+
1748
+ Proxied for the :class:`_orm.Session` class
1749
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1750
+
1751
+ """ # noqa: E501
1752
+
1753
+ return self._proxied.new
1754
+
1755
+ @property
1756
+ def identity_map(self) -> IdentityMap:
1757
+ r"""Proxy for the :attr:`_orm.Session.identity_map` attribute
1758
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1759
+
1760
+ """ # noqa: E501
1761
+
1762
+ return self._proxied.identity_map
1763
+
1764
+ @identity_map.setter
1765
+ def identity_map(self, attr: IdentityMap) -> None:
1766
+ self._proxied.identity_map = attr
1767
+
1768
+ @property
1769
+ def is_active(self) -> bool:
1770
+ r"""True if this :class:`.Session` not in "partial rollback" state.
1771
+
1772
+ .. container:: class_bases
1773
+
1774
+ Proxied for the :class:`_orm.Session` class
1775
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1776
+
1777
+ .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins
1778
+ a new transaction immediately, so this attribute will be False
1779
+ when the :class:`_orm.Session` is first instantiated.
1780
+
1781
+ "partial rollback" state typically indicates that the flush process
1782
+ of the :class:`_orm.Session` has failed, and that the
1783
+ :meth:`_orm.Session.rollback` method must be emitted in order to
1784
+ fully roll back the transaction.
1785
+
1786
+ If this :class:`_orm.Session` is not in a transaction at all, the
1787
+ :class:`_orm.Session` will autobegin when it is first used, so in this
1788
+ case :attr:`_orm.Session.is_active` will return True.
1789
+
1790
+ Otherwise, if this :class:`_orm.Session` is within a transaction,
1791
+ and that transaction has not been rolled back internally, the
1792
+ :attr:`_orm.Session.is_active` will also return True.
1793
+
1794
+ .. seealso::
1795
+
1796
+ :ref:`faq_session_rollback`
1797
+
1798
+ :meth:`_orm.Session.in_transaction`
1799
+
1800
+
1801
+ """ # noqa: E501
1802
+
1803
+ return self._proxied.is_active
1804
+
1805
+ @property
1806
+ def autoflush(self) -> bool:
1807
+ r"""Proxy for the :attr:`_orm.Session.autoflush` attribute
1808
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1809
+
1810
+ """ # noqa: E501
1811
+
1812
+ return self._proxied.autoflush
1813
+
1814
+ @autoflush.setter
1815
+ def autoflush(self, attr: bool) -> None:
1816
+ self._proxied.autoflush = attr
1817
+
1818
+ @property
1819
+ def no_autoflush(self) -> Any:
1820
+ r"""Return a context manager that disables autoflush.
1821
+
1822
+ .. container:: class_bases
1823
+
1824
+ Proxied for the :class:`_orm.Session` class
1825
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1826
+
1827
+ e.g.::
1828
+
1829
+ with session.no_autoflush:
1830
+
1831
+ some_object = SomeClass()
1832
+ session.add(some_object)
1833
+ # won't autoflush
1834
+ some_object.related_thing = session.query(SomeRelated).first()
1835
+
1836
+ Operations that proceed within the ``with:`` block
1837
+ will not be subject to flushes occurring upon query
1838
+ access. This is useful when initializing a series
1839
+ of objects which involve existing database queries,
1840
+ where the uncompleted object should not yet be flushed.
1841
+
1842
+
1843
+ """ # noqa: E501
1844
+
1845
+ return self._proxied.no_autoflush
1846
+
1847
+ @property
1848
+ def info(self) -> _InfoType:
1849
+ r"""A user-modifiable dictionary.
1850
+
1851
+ .. container:: class_bases
1852
+
1853
+ Proxied for the :class:`_orm.Session` class
1854
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1855
+
1856
+ The initial value of this dictionary can be populated using the
1857
+ ``info`` argument to the :class:`.Session` constructor or
1858
+ :class:`.sessionmaker` constructor or factory methods. The dictionary
1859
+ here is always local to this :class:`.Session` and can be modified
1860
+ independently of all other :class:`.Session` objects.
1861
+
1862
+
1863
+ """ # noqa: E501
1864
+
1865
+ return self._proxied.info
1866
+
1867
+ @property
1868
+ def execution_options(self) -> _ExecuteOptions:
1869
+ r"""Proxy for the :attr:`_orm.Session.execution_options` attribute
1870
+ on behalf of the :class:`_asyncio.AsyncSession` class.
1871
+
1872
+ """ # noqa: E501
1873
+
1874
+ return self._proxied.execution_options
1875
+
1876
+ @execution_options.setter
1877
+ def execution_options(self, attr: _ExecuteOptions) -> None:
1878
+ self._proxied.execution_options = attr
1879
+
1880
+ @classmethod
1881
+ def object_session(cls, instance: object) -> Optional[Session]:
1882
+ r"""Return the :class:`.Session` to which an object belongs.
1883
+
1884
+ .. container:: class_bases
1885
+
1886
+ Proxied for the :class:`_orm.Session` class on
1887
+ behalf of the :class:`_asyncio.AsyncSession` class.
1888
+
1889
+ This is an alias of :func:`.object_session`.
1890
+
1891
+
1892
+ """ # noqa: E501
1893
+
1894
+ return Session.object_session(instance)
1895
+
1896
+ @classmethod
1897
+ def identity_key(
1898
+ cls,
1899
+ class_: Optional[Type[Any]] = None,
1900
+ ident: Union[Any, Tuple[Any, ...]] = None,
1901
+ *,
1902
+ instance: Optional[Any] = None,
1903
+ row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]] = None,
1904
+ identity_token: Optional[Any] = None,
1905
+ ) -> _IdentityKeyType[Any]:
1906
+ r"""Return an identity key.
1907
+
1908
+ .. container:: class_bases
1909
+
1910
+ Proxied for the :class:`_orm.Session` class on
1911
+ behalf of the :class:`_asyncio.AsyncSession` class.
1912
+
1913
+ This is an alias of :func:`.util.identity_key`.
1914
+
1915
+
1916
+ """ # noqa: E501
1917
+
1918
+ return Session.identity_key(
1919
+ class_=class_,
1920
+ ident=ident,
1921
+ instance=instance,
1922
+ row=row,
1923
+ identity_token=identity_token,
1924
+ )
1925
+
1926
+ # END PROXY METHODS AsyncSession
1927
+
1928
+
1929
+ _AS = TypeVar("_AS", bound="AsyncSession")
1930
+
1931
+
1932
+ class async_sessionmaker(Generic[_AS]):
1933
+ """A configurable :class:`.AsyncSession` factory.
1934
+
1935
+ The :class:`.async_sessionmaker` factory works in the same way as the
1936
+ :class:`.sessionmaker` factory, to generate new :class:`.AsyncSession`
1937
+ objects when called, creating them given
1938
+ the configurational arguments established here.
1939
+
1940
+ e.g.::
1941
+
1942
+ from sqlalchemy.ext.asyncio import create_async_engine
1943
+ from sqlalchemy.ext.asyncio import AsyncSession
1944
+ from sqlalchemy.ext.asyncio import async_sessionmaker
1945
+
1946
+
1947
+ async def run_some_sql(
1948
+ async_session: async_sessionmaker[AsyncSession],
1949
+ ) -> None:
1950
+ async with async_session() as session:
1951
+ session.add(SomeObject(data="object"))
1952
+ session.add(SomeOtherObject(name="other object"))
1953
+ await session.commit()
1954
+
1955
+
1956
+ async def main() -> None:
1957
+ # an AsyncEngine, which the AsyncSession will use for connection
1958
+ # resources
1959
+ engine = create_async_engine(
1960
+ "postgresql+asyncpg://scott:tiger@localhost/"
1961
+ )
1962
+
1963
+ # create a reusable factory for new AsyncSession instances
1964
+ async_session = async_sessionmaker(engine)
1965
+
1966
+ await run_some_sql(async_session)
1967
+
1968
+ await engine.dispose()
1969
+
1970
+ The :class:`.async_sessionmaker` is useful so that different parts
1971
+ of a program can create new :class:`.AsyncSession` objects with a
1972
+ fixed configuration established up front. Note that :class:`.AsyncSession`
1973
+ objects may also be instantiated directly when not using
1974
+ :class:`.async_sessionmaker`.
1975
+
1976
+ .. versionadded:: 2.0 :class:`.async_sessionmaker` provides a
1977
+ :class:`.sessionmaker` class that's dedicated to the
1978
+ :class:`.AsyncSession` object, including pep-484 typing support.
1979
+
1980
+ .. seealso::
1981
+
1982
+ :ref:`asyncio_orm` - shows example use
1983
+
1984
+ :class:`.sessionmaker` - general overview of the
1985
+ :class:`.sessionmaker` architecture
1986
+
1987
+
1988
+ :ref:`session_getting` - introductory text on creating
1989
+ sessions using :class:`.sessionmaker`.
1990
+
1991
+ """ # noqa E501
1992
+
1993
+ class_: Type[_AS]
1994
+
1995
+ @overload
1996
+ def __init__(
1997
+ self,
1998
+ bind: Optional[_AsyncSessionBind] = ...,
1999
+ *,
2000
+ class_: Type[_AS],
2001
+ autoflush: bool = ...,
2002
+ expire_on_commit: bool = ...,
2003
+ info: Optional[_InfoType] = ...,
2004
+ **kw: Any,
2005
+ ): ...
2006
+
2007
+ @overload
2008
+ def __init__(
2009
+ self: "async_sessionmaker[AsyncSession]",
2010
+ bind: Optional[_AsyncSessionBind] = ...,
2011
+ *,
2012
+ autoflush: bool = ...,
2013
+ expire_on_commit: bool = ...,
2014
+ info: Optional[_InfoType] = ...,
2015
+ **kw: Any,
2016
+ ): ...
2017
+
2018
+ def __init__(
2019
+ self,
2020
+ bind: Optional[_AsyncSessionBind] = None,
2021
+ *,
2022
+ class_: Type[_AS] = AsyncSession, # type: ignore[assignment]
2023
+ autoflush: bool = True,
2024
+ expire_on_commit: bool = True,
2025
+ info: Optional[_InfoType] = None,
2026
+ **kw: Any,
2027
+ ):
2028
+ r"""Construct a new :class:`.async_sessionmaker`.
2029
+
2030
+ All arguments here except for ``class_`` correspond to arguments
2031
+ accepted by :class:`.Session` directly. See the
2032
+ :meth:`.AsyncSession.__init__` docstring for more details on
2033
+ parameters.
2034
+
2035
+
2036
+ """
2037
+ kw["bind"] = bind
2038
+ kw["autoflush"] = autoflush
2039
+ kw["expire_on_commit"] = expire_on_commit
2040
+ if info is not None:
2041
+ kw["info"] = info
2042
+ self.kw = kw
2043
+ self.class_ = class_
2044
+
2045
+ def begin(self) -> _AsyncSessionContextManager[_AS]:
2046
+ """Produce a context manager that both provides a new
2047
+ :class:`_orm.AsyncSession` as well as a transaction that commits.
2048
+
2049
+
2050
+ e.g.::
2051
+
2052
+ async def main():
2053
+ Session = async_sessionmaker(some_engine)
2054
+
2055
+ async with Session.begin() as session:
2056
+ session.add(some_object)
2057
+
2058
+ # commits transaction, closes session
2059
+
2060
+ """
2061
+
2062
+ session = self()
2063
+ return session._maker_context_manager()
2064
+
2065
+ def __call__(self, **local_kw: Any) -> _AS:
2066
+ """Produce a new :class:`.AsyncSession` object using the configuration
2067
+ established in this :class:`.async_sessionmaker`.
2068
+
2069
+ In Python, the ``__call__`` method is invoked on an object when
2070
+ it is "called" in the same way as a function::
2071
+
2072
+ AsyncSession = async_sessionmaker(async_engine, expire_on_commit=False)
2073
+ session = AsyncSession() # invokes sessionmaker.__call__()
2074
+
2075
+ """ # noqa E501
2076
+ for k, v in self.kw.items():
2077
+ if k == "info" and "info" in local_kw:
2078
+ d = v.copy()
2079
+ d.update(local_kw["info"])
2080
+ local_kw["info"] = d
2081
+ else:
2082
+ local_kw.setdefault(k, v)
2083
+ return self.class_(**local_kw)
2084
+
2085
+ def configure(self, **new_kw: Any) -> None:
2086
+ """(Re)configure the arguments for this async_sessionmaker.
2087
+
2088
+ e.g.::
2089
+
2090
+ AsyncSession = async_sessionmaker(some_engine)
2091
+
2092
+ AsyncSession.configure(bind=create_async_engine("sqlite+aiosqlite://"))
2093
+ """ # noqa E501
2094
+
2095
+ self.kw.update(new_kw)
2096
+
2097
+ def __repr__(self) -> str:
2098
+ return "%s(class_=%r, %s)" % (
2099
+ self.__class__.__name__,
2100
+ self.class_.__name__,
2101
+ ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()),
2102
+ )
2103
+
2104
+
2105
+ class _AsyncSessionContextManager(Generic[_AS]):
2106
+ __slots__ = ("async_session", "trans")
2107
+
2108
+ async_session: _AS
2109
+ trans: AsyncSessionTransaction
2110
+
2111
+ def __init__(self, async_session: _AS):
2112
+ self.async_session = async_session
2113
+
2114
+ async def __aenter__(self) -> _AS:
2115
+ self.trans = self.async_session.begin()
2116
+ await self.trans.__aenter__()
2117
+ return self.async_session
2118
+
2119
+ async def __aexit__(self, type_: Any, value: Any, traceback: Any) -> None:
2120
+ async def go() -> None:
2121
+ await self.trans.__aexit__(type_, value, traceback)
2122
+ await self.async_session.__aexit__(type_, value, traceback)
2123
+
2124
+ task = asyncio.create_task(go())
2125
+ await asyncio.shield(task)
2126
+
2127
+
2128
+ class AsyncSessionTransaction(
2129
+ ReversibleProxy[SessionTransaction],
2130
+ StartableContext["AsyncSessionTransaction"],
2131
+ ):
2132
+ """A wrapper for the ORM :class:`_orm.SessionTransaction` object.
2133
+
2134
+ This object is provided so that a transaction-holding object
2135
+ for the :meth:`_asyncio.AsyncSession.begin` may be returned.
2136
+
2137
+ The object supports both explicit calls to
2138
+ :meth:`_asyncio.AsyncSessionTransaction.commit` and
2139
+ :meth:`_asyncio.AsyncSessionTransaction.rollback`, as well as use as an
2140
+ async context manager.
2141
+
2142
+
2143
+ .. versionadded:: 1.4
2144
+
2145
+ """
2146
+
2147
+ __slots__ = ("session", "sync_transaction", "nested")
2148
+
2149
+ session: AsyncSession
2150
+ sync_transaction: Optional[SessionTransaction]
2151
+
2152
+ def __init__(self, session: AsyncSession, nested: bool = False):
2153
+ self.session = session
2154
+ self.nested = nested
2155
+ self.sync_transaction = None
2156
+
2157
+ @property
2158
+ def is_active(self) -> bool:
2159
+ return (
2160
+ self._sync_transaction() is not None
2161
+ and self._sync_transaction().is_active
2162
+ )
2163
+
2164
+ def _sync_transaction(self) -> SessionTransaction:
2165
+ if not self.sync_transaction:
2166
+ self._raise_for_not_started()
2167
+ return self.sync_transaction
2168
+
2169
+ async def rollback(self) -> None:
2170
+ """Roll back this :class:`_asyncio.AsyncTransaction`."""
2171
+ await greenlet_spawn(self._sync_transaction().rollback)
2172
+
2173
+ async def commit(self) -> None:
2174
+ """Commit this :class:`_asyncio.AsyncTransaction`."""
2175
+
2176
+ await greenlet_spawn(self._sync_transaction().commit)
2177
+
2178
+ @classmethod
2179
+ def _regenerate_proxy_for_target( # type: ignore[override]
2180
+ cls,
2181
+ target: SessionTransaction,
2182
+ async_session: AsyncSession,
2183
+ **additional_kw: Any, # noqa: U100
2184
+ ) -> AsyncSessionTransaction:
2185
+ sync_transaction = target
2186
+ nested = target.nested
2187
+ obj = cls.__new__(cls)
2188
+ obj.session = async_session
2189
+ obj.sync_transaction = obj._assign_proxied(sync_transaction)
2190
+ obj.nested = nested
2191
+ return obj
2192
+
2193
+ async def start(
2194
+ self, is_ctxmanager: bool = False
2195
+ ) -> AsyncSessionTransaction:
2196
+ self.sync_transaction = self._assign_proxied(
2197
+ await greenlet_spawn(
2198
+ self.session.sync_session.begin_nested
2199
+ if self.nested
2200
+ else self.session.sync_session.begin
2201
+ )
2202
+ )
2203
+ if is_ctxmanager:
2204
+ self.sync_transaction.__enter__()
2205
+ return self
2206
+
2207
+ async def __aexit__(self, type_: Any, value: Any, traceback: Any) -> None:
2208
+ await greenlet_spawn(
2209
+ self._sync_transaction().__exit__, type_, value, traceback
2210
+ )
2211
+
2212
+
2213
+ def async_object_session(instance: object) -> Optional[AsyncSession]:
2214
+ """Return the :class:`_asyncio.AsyncSession` to which the given instance
2215
+ belongs.
2216
+
2217
+ This function makes use of the sync-API function
2218
+ :class:`_orm.object_session` to retrieve the :class:`_orm.Session` which
2219
+ refers to the given instance, and from there links it to the original
2220
+ :class:`_asyncio.AsyncSession`.
2221
+
2222
+ If the :class:`_asyncio.AsyncSession` has been garbage collected, the
2223
+ return value is ``None``.
2224
+
2225
+ This functionality is also available from the
2226
+ :attr:`_orm.InstanceState.async_session` accessor.
2227
+
2228
+ :param instance: an ORM mapped instance
2229
+ :return: an :class:`_asyncio.AsyncSession` object, or ``None``.
2230
+
2231
+ .. versionadded:: 1.4.18
2232
+
2233
+ """
2234
+
2235
+ session = object_session(instance)
2236
+ if session is not None:
2237
+ return async_session(session)
2238
+ else:
2239
+ return None
2240
+
2241
+
2242
+ def async_session(session: Session) -> Optional[AsyncSession]:
2243
+ """Return the :class:`_asyncio.AsyncSession` which is proxying the given
2244
+ :class:`_orm.Session` object, if any.
2245
+
2246
+ :param session: a :class:`_orm.Session` instance.
2247
+ :return: a :class:`_asyncio.AsyncSession` instance, or ``None``.
2248
+
2249
+ .. versionadded:: 1.4.18
2250
+
2251
+ """
2252
+ return AsyncSession._retrieve_proxy_for_target(session, regenerate=False)
2253
+
2254
+
2255
+ async def close_all_sessions() -> None:
2256
+ """Close all :class:`_asyncio.AsyncSession` sessions.
2257
+
2258
+ .. versionadded:: 2.0.23
2259
+
2260
+ .. seealso::
2261
+
2262
+ :func:`.session.close_all_sessions`
2263
+
2264
+ """
2265
+ await greenlet_spawn(_sync_close_all_sessions)
2266
+
2267
+
2268
+ _instance_state._async_provider = async_session # type: ignore[attr-defined]