SQLAlchemy 2.1.0__cp315-cp315-win_amd64.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.cp315-win_amd64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_amd64.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.cp315-win_amd64.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.cp315-win_amd64.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.cp315-win_amd64.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.cp315-win_amd64.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,1489 @@
1
+ # ext/asyncio/engine.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
+ import contextlib
11
+ from typing import Any
12
+ from typing import AsyncIterator
13
+ from typing import Callable
14
+ from typing import Concatenate
15
+ from typing import Dict
16
+ from typing import Generator
17
+ from typing import NoReturn
18
+ from typing import Optional
19
+ from typing import overload
20
+ from typing import ParamSpec
21
+ from typing import Type
22
+ from typing import TYPE_CHECKING
23
+ from typing import TypeVar
24
+ from typing import Union
25
+
26
+ from . import exc as async_exc
27
+ from .base import asyncstartablecontext
28
+ from .base import GeneratorStartableContext
29
+ from .base import ProxyComparable
30
+ from .base import StartableContext
31
+ from .result import _ensure_sync_result
32
+ from .result import AsyncResult
33
+ from .result import AsyncScalarResult
34
+ from ... import exc
35
+ from ... import inspection
36
+ from ... import util
37
+ from ...engine import Connection
38
+ from ...engine import create_engine as _create_engine
39
+ from ...engine import create_pool_from_url as _create_pool_from_url
40
+ from ...engine import Engine
41
+ from ...engine.base import NestedTransaction
42
+ from ...engine.base import Transaction
43
+ from ...exc import ArgumentError
44
+ from ...util import immutabledict
45
+ from ...util.concurrency import greenlet_spawn
46
+ from ...util.typing import Never
47
+ from ...util.typing import TupleAny
48
+ from ...util.typing import TypeVarTuple
49
+ from ...util.typing import Unpack
50
+
51
+ if TYPE_CHECKING:
52
+ from ...engine.cursor import CursorResult
53
+ from ...engine.interfaces import _CoreAnyExecuteParams
54
+ from ...engine.interfaces import _CoreSingleExecuteParams
55
+ from ...engine.interfaces import _DBAPIAnyExecuteParams
56
+ from ...engine.interfaces import _ExecuteOptions
57
+ from ...engine.interfaces import CompiledCacheType
58
+ from ...engine.interfaces import CoreExecuteOptionsParameter
59
+ from ...engine.interfaces import Dialect
60
+ from ...engine.interfaces import IsolationLevel
61
+ from ...engine.interfaces import SchemaTranslateMapType
62
+ from ...engine.result import ScalarResult
63
+ from ...engine.url import URL
64
+ from ...pool import Pool
65
+ from ...pool import PoolProxiedConnection
66
+ from ...sql._typing import _InfoType
67
+ from ...sql.base import Executable
68
+ from ...sql.selectable import TypedReturnsRows
69
+
70
+ _P = ParamSpec("_P")
71
+ _T = TypeVar("_T", bound=Any)
72
+ _Ts = TypeVarTuple("_Ts")
73
+ _stream_results = immutabledict(stream_results=True)
74
+
75
+
76
+ def create_async_engine(url: Union[str, URL], **kw: Any) -> AsyncEngine:
77
+ """Create a new async engine instance.
78
+
79
+ Arguments passed to :func:`_asyncio.create_async_engine` are mostly
80
+ identical to those passed to the :func:`_sa.create_engine` function.
81
+ The specified dialect must be an asyncio-compatible dialect
82
+ such as :ref:`dialect-postgresql-asyncpg`.
83
+
84
+ .. versionadded:: 1.4
85
+
86
+ :param async_creator: an async callable which returns a driver-level
87
+ asyncio connection. If given, the function should take no arguments,
88
+ and return a new asyncio connection from the underlying asyncio
89
+ database driver; the connection will be wrapped in the appropriate
90
+ structures to be used with the :class:`.AsyncEngine`. Note that the
91
+ parameters specified in the URL are not applied here, and the creator
92
+ function should use its own connection parameters.
93
+
94
+ This parameter is the asyncio equivalent of the
95
+ :paramref:`_sa.create_engine.creator` parameter of the
96
+ :func:`_sa.create_engine` function.
97
+
98
+ .. versionadded:: 2.0.16
99
+
100
+ """
101
+
102
+ if kw.get("server_side_cursors", False):
103
+ raise async_exc.AsyncMethodRequired(
104
+ "Can't set server_side_cursors for async engine globally; "
105
+ "use the connection.stream() method for an async "
106
+ "streaming result set"
107
+ )
108
+ kw["_is_async"] = True
109
+ async_creator = kw.pop("async_creator", None)
110
+ if async_creator:
111
+ if kw.get("creator", None):
112
+ raise ArgumentError(
113
+ "Can only specify one of 'async_creator' or 'creator', "
114
+ "not both."
115
+ )
116
+
117
+ def creator() -> Any:
118
+ # note that to send adapted arguments like
119
+ # prepared_statement_cache_size, user would use
120
+ # "creator" and emulate this form here
121
+ return sync_engine.dialect.dbapi.connect( # type: ignore[union-attr] # noqa: E501
122
+ async_creator_fn=async_creator
123
+ )
124
+
125
+ kw["creator"] = creator
126
+ sync_engine = _create_engine(url, **kw)
127
+ return AsyncEngine(sync_engine)
128
+
129
+
130
+ def async_engine_from_config(
131
+ configuration: Dict[str, Any], prefix: str = "sqlalchemy.", **kwargs: Any
132
+ ) -> AsyncEngine:
133
+ """Create a new AsyncEngine instance using a configuration dictionary.
134
+
135
+ This function is analogous to the :func:`_sa.engine_from_config` function
136
+ in SQLAlchemy Core, except that the requested dialect must be an
137
+ asyncio-compatible dialect such as :ref:`dialect-postgresql-asyncpg`.
138
+ The argument signature of the function is identical to that
139
+ of :func:`_sa.engine_from_config`.
140
+
141
+ .. versionadded:: 1.4.29
142
+
143
+ """
144
+ options = {
145
+ key[len(prefix) :]: value
146
+ for key, value in configuration.items()
147
+ if key.startswith(prefix)
148
+ }
149
+ options["_coerce_config"] = True
150
+ options.update(kwargs)
151
+ url = options.pop("url")
152
+ return create_async_engine(url, **options)
153
+
154
+
155
+ def create_async_pool_from_url(url: Union[str, URL], **kwargs: Any) -> Pool:
156
+ """Create a new async engine instance.
157
+
158
+ Arguments passed to :func:`_asyncio.create_async_pool_from_url` are mostly
159
+ identical to those passed to the :func:`_sa.create_pool_from_url` function.
160
+ The specified dialect must be an asyncio-compatible dialect
161
+ such as :ref:`dialect-postgresql-asyncpg`.
162
+
163
+ .. versionadded:: 2.0.10
164
+
165
+ """
166
+ kwargs["_is_async"] = True
167
+ return _create_pool_from_url(url, **kwargs)
168
+
169
+
170
+ class AsyncConnectable:
171
+ __slots__ = "_slots_dispatch", "__weakref__"
172
+
173
+ sync_engine: Engine
174
+
175
+ @classmethod
176
+ def _no_async_engine_events(cls) -> NoReturn:
177
+ raise NotImplementedError(
178
+ "asynchronous events are not implemented at this time. Apply "
179
+ "synchronous listeners to the AsyncEngine.sync_engine or "
180
+ "AsyncConnection.sync_connection attributes."
181
+ )
182
+
183
+
184
+ @util.create_proxy_methods(
185
+ Connection,
186
+ ":class:`_engine.Connection`",
187
+ ":class:`_asyncio.AsyncConnection`",
188
+ classmethods=[],
189
+ methods=[],
190
+ attributes=[
191
+ "closed",
192
+ "invalidated",
193
+ "dialect",
194
+ "default_isolation_level",
195
+ ],
196
+ )
197
+ # "Class has incompatible disjoint bases" - no idea
198
+ class AsyncConnection( # type: ignore[misc]
199
+ ProxyComparable[Connection],
200
+ StartableContext["AsyncConnection"],
201
+ AsyncConnectable,
202
+ ):
203
+ """An asyncio proxy for a :class:`_engine.Connection`.
204
+
205
+ :class:`_asyncio.AsyncConnection` is acquired using the
206
+ :meth:`_asyncio.AsyncEngine.connect`
207
+ method of :class:`_asyncio.AsyncEngine`::
208
+
209
+ from sqlalchemy.ext.asyncio import create_async_engine
210
+
211
+ engine = create_async_engine("postgresql+asyncpg://user:pass@host/dbname")
212
+
213
+ async with engine.connect() as conn:
214
+ result = await conn.execute(select(table))
215
+
216
+ .. versionadded:: 1.4
217
+
218
+ """ # noqa
219
+
220
+ # AsyncConnection is a thin proxy; no state should be added here
221
+ # that is not retrievable from the "sync" engine / connection, e.g.
222
+ # current transaction, info, etc. It should be possible to
223
+ # create a new AsyncConnection that matches this one given only the
224
+ # "sync" elements.
225
+ __slots__ = (
226
+ "engine",
227
+ "sync_engine",
228
+ "sync_connection",
229
+ )
230
+
231
+ def __init__(
232
+ self,
233
+ async_engine: AsyncEngine,
234
+ sync_connection: Optional[Connection] = None,
235
+ ):
236
+ self.engine = async_engine
237
+ self.sync_engine = async_engine.sync_engine
238
+ self.sync_connection = self._assign_proxied(sync_connection)
239
+
240
+ sync_connection: Optional[Connection]
241
+ """Reference to the sync-style :class:`_engine.Connection` this
242
+ :class:`_asyncio.AsyncConnection` proxies requests towards.
243
+
244
+ This instance can be used as an event target.
245
+
246
+ .. seealso::
247
+
248
+ :ref:`asyncio_events`
249
+
250
+ """
251
+
252
+ sync_engine: Engine
253
+ """Reference to the sync-style :class:`_engine.Engine` this
254
+ :class:`_asyncio.AsyncConnection` is associated with via its underlying
255
+ :class:`_engine.Connection`.
256
+
257
+ This instance can be used as an event target.
258
+
259
+ .. seealso::
260
+
261
+ :ref:`asyncio_events`
262
+
263
+ """
264
+
265
+ @classmethod
266
+ def _regenerate_proxy_for_target(
267
+ cls, target: Connection, **additional_kw: Any # noqa: U100
268
+ ) -> AsyncConnection:
269
+ return AsyncConnection(
270
+ AsyncEngine._retrieve_proxy_for_target(target.engine), target
271
+ )
272
+
273
+ async def start(
274
+ self, is_ctxmanager: bool = False # noqa: U100
275
+ ) -> AsyncConnection:
276
+ """Start this :class:`_asyncio.AsyncConnection` object's context
277
+ outside of using a Python ``with:`` block.
278
+
279
+ """
280
+ if self.sync_connection:
281
+ raise exc.InvalidRequestError("connection is already started")
282
+ self.sync_connection = self._assign_proxied(
283
+ await greenlet_spawn(self.sync_engine.connect)
284
+ )
285
+ return self
286
+
287
+ @property
288
+ def connection(self) -> NoReturn:
289
+ """Not implemented for async; call
290
+ :meth:`_asyncio.AsyncConnection.get_raw_connection`.
291
+ """
292
+ raise exc.InvalidRequestError(
293
+ "AsyncConnection.connection accessor is not implemented as the "
294
+ "attribute may need to reconnect on an invalidated connection. "
295
+ "Use the get_raw_connection() method."
296
+ )
297
+
298
+ async def get_raw_connection(self) -> PoolProxiedConnection:
299
+ """Return the pooled DBAPI-level connection in use by this
300
+ :class:`_asyncio.AsyncConnection`.
301
+
302
+ This is a SQLAlchemy connection-pool proxied connection
303
+ which then has the attribute
304
+ :attr:`_pool._ConnectionFairy.driver_connection` that refers to the
305
+ actual driver connection. Its
306
+ :attr:`_pool._ConnectionFairy.dbapi_connection` refers instead
307
+ to an :class:`_engine.AdaptedConnection` instance that
308
+ adapts the driver connection to the DBAPI protocol.
309
+
310
+ """
311
+
312
+ return await greenlet_spawn(getattr, self._proxied, "connection")
313
+
314
+ @util.ro_non_memoized_property
315
+ def info(self) -> _InfoType:
316
+ """Return the :attr:`_engine.Connection.info` dictionary of the
317
+ underlying :class:`_engine.Connection`.
318
+
319
+ This dictionary is freely writable for user-defined state to be
320
+ associated with the database connection.
321
+
322
+ This attribute is only available if the :class:`.AsyncConnection` is
323
+ currently connected. If the :attr:`.AsyncConnection.closed` attribute
324
+ is ``True``, then accessing this attribute will raise
325
+ :class:`.ResourceClosedError`.
326
+
327
+ .. versionadded:: 1.4.0b2
328
+
329
+ """
330
+ return self._proxied.info
331
+
332
+ @util.ro_non_memoized_property
333
+ def _proxied(self) -> Connection:
334
+ if not self.sync_connection:
335
+ self._raise_for_not_started()
336
+ return self.sync_connection
337
+
338
+ def begin(self) -> AsyncTransaction:
339
+ """Begin a transaction prior to autobegin occurring."""
340
+ assert self._proxied
341
+ return AsyncTransaction(self)
342
+
343
+ def begin_nested(self) -> AsyncTransaction:
344
+ """Begin a nested transaction and return a transaction handle."""
345
+ assert self._proxied
346
+ return AsyncTransaction(self, nested=True)
347
+
348
+ async def invalidate(
349
+ self, exception: Optional[BaseException] = None
350
+ ) -> None:
351
+ """Invalidate the underlying DBAPI connection associated with
352
+ this :class:`_engine.Connection`.
353
+
354
+ See the method :meth:`_engine.Connection.invalidate` for full
355
+ detail on this method.
356
+
357
+ """
358
+
359
+ return await greenlet_spawn(
360
+ self._proxied.invalidate, exception=exception
361
+ )
362
+
363
+ async def get_isolation_level(self) -> IsolationLevel:
364
+ return await greenlet_spawn(self._proxied.get_isolation_level)
365
+
366
+ def in_transaction(self) -> bool:
367
+ """Return True if a transaction is in progress."""
368
+
369
+ return self._proxied.in_transaction()
370
+
371
+ def in_nested_transaction(self) -> bool:
372
+ """Return True if a transaction is in progress.
373
+
374
+ .. versionadded:: 1.4.0b2
375
+
376
+ """
377
+ return self._proxied.in_nested_transaction()
378
+
379
+ def get_transaction(self) -> Optional[AsyncTransaction]:
380
+ """Return an :class:`.AsyncTransaction` representing the current
381
+ transaction, if any.
382
+
383
+ This makes use of the underlying synchronous connection's
384
+ :meth:`_engine.Connection.get_transaction` method to get the current
385
+ :class:`_engine.Transaction`, which is then proxied in a new
386
+ :class:`.AsyncTransaction` object.
387
+
388
+ .. versionadded:: 1.4.0b2
389
+
390
+ """
391
+
392
+ trans = self._proxied.get_transaction()
393
+ if trans is not None:
394
+ return AsyncTransaction._retrieve_proxy_for_target(trans)
395
+ else:
396
+ return None
397
+
398
+ def get_nested_transaction(self) -> Optional[AsyncTransaction]:
399
+ """Return an :class:`.AsyncTransaction` representing the current
400
+ nested (savepoint) transaction, if any.
401
+
402
+ This makes use of the underlying synchronous connection's
403
+ :meth:`_engine.Connection.get_nested_transaction` method to get the
404
+ current :class:`_engine.Transaction`, which is then proxied in a new
405
+ :class:`.AsyncTransaction` object.
406
+
407
+ .. versionadded:: 1.4.0b2
408
+
409
+ """
410
+
411
+ trans = self._proxied.get_nested_transaction()
412
+ if trans is not None:
413
+ return AsyncTransaction._retrieve_proxy_for_target(trans)
414
+ else:
415
+ return None
416
+
417
+ @overload
418
+ async def execution_options(
419
+ self,
420
+ *,
421
+ compiled_cache: Optional[CompiledCacheType] = ...,
422
+ logging_token: str = ...,
423
+ isolation_level: IsolationLevel = ...,
424
+ no_parameters: bool = False,
425
+ stream_results: bool = False,
426
+ max_row_buffer: int = ...,
427
+ yield_per: int = ...,
428
+ insertmanyvalues_page_size: int = ...,
429
+ schema_translate_map: Optional[SchemaTranslateMapType] = ...,
430
+ preserve_rowcount: bool = False,
431
+ driver_column_names: bool = False,
432
+ **opt: Any,
433
+ ) -> AsyncConnection: ...
434
+
435
+ @overload
436
+ async def execution_options(self, **opt: Any) -> AsyncConnection: ...
437
+
438
+ async def execution_options(self, **opt: Any) -> AsyncConnection:
439
+ r"""Set non-SQL options for the connection which take effect
440
+ during execution.
441
+
442
+ This returns this :class:`_asyncio.AsyncConnection` object with
443
+ the new options added.
444
+
445
+ See :meth:`_engine.Connection.execution_options` for full details
446
+ on this method.
447
+
448
+ """
449
+
450
+ conn = self._proxied
451
+ c2 = await greenlet_spawn(conn.execution_options, **opt)
452
+ assert c2 is conn
453
+ return self
454
+
455
+ async def commit(self) -> None:
456
+ """Commit the transaction that is currently in progress.
457
+
458
+ This method commits the current transaction if one has been started.
459
+ If no transaction was started, the method has no effect, assuming
460
+ the connection is in a non-invalidated state.
461
+
462
+ A transaction is begun on a :class:`_engine.Connection` automatically
463
+ whenever a statement is first executed, or when the
464
+ :meth:`_engine.Connection.begin` method is called.
465
+
466
+ """
467
+ await greenlet_spawn(self._proxied.commit)
468
+
469
+ async def rollback(self) -> None:
470
+ """Roll back the transaction that is currently in progress.
471
+
472
+ This method rolls back the current transaction if one has been started.
473
+ If no transaction was started, the method has no effect. If a
474
+ transaction was started and the connection is in an invalidated state,
475
+ the transaction is cleared using this method.
476
+
477
+ A transaction is begun on a :class:`_engine.Connection` automatically
478
+ whenever a statement is first executed, or when the
479
+ :meth:`_engine.Connection.begin` method is called.
480
+
481
+
482
+ """
483
+ await greenlet_spawn(self._proxied.rollback)
484
+
485
+ async def close(self) -> None:
486
+ """Close this :class:`_asyncio.AsyncConnection`.
487
+
488
+ This has the effect of also rolling back the transaction if one
489
+ is in place.
490
+
491
+ """
492
+ await greenlet_spawn(self._proxied.close)
493
+
494
+ async def aclose(self) -> None:
495
+ """A synonym for :meth:`_asyncio.AsyncConnection.close`.
496
+
497
+ The :meth:`_asyncio.AsyncConnection.aclose` name is specifically
498
+ to support the Python standard library ``@contextlib.aclosing``
499
+ context manager function.
500
+
501
+ .. versionadded:: 2.0.20
502
+
503
+ """
504
+ await self.close()
505
+
506
+ async def exec_driver_sql(
507
+ self,
508
+ statement: str,
509
+ parameters: Optional[_DBAPIAnyExecuteParams] = None,
510
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
511
+ ) -> CursorResult[Any]:
512
+ r"""Executes a driver-level SQL string and return buffered
513
+ :class:`_engine.Result`.
514
+
515
+ """
516
+
517
+ result = await greenlet_spawn(
518
+ self._proxied.exec_driver_sql,
519
+ statement,
520
+ parameters,
521
+ execution_options,
522
+ _require_await=True,
523
+ )
524
+
525
+ return await _ensure_sync_result(result, self.exec_driver_sql)
526
+
527
+ @overload
528
+ def stream(
529
+ self,
530
+ statement: TypedReturnsRows[Unpack[_Ts]],
531
+ parameters: Optional[_CoreAnyExecuteParams] = None,
532
+ *,
533
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
534
+ ) -> GeneratorStartableContext[AsyncResult[Unpack[_Ts]]]: ...
535
+
536
+ @overload
537
+ def stream(
538
+ self,
539
+ statement: Executable,
540
+ parameters: Optional[_CoreAnyExecuteParams] = None,
541
+ *,
542
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
543
+ ) -> GeneratorStartableContext[AsyncResult[Unpack[TupleAny]]]: ...
544
+
545
+ @asyncstartablecontext
546
+ async def stream(
547
+ self,
548
+ statement: Executable,
549
+ parameters: Optional[_CoreAnyExecuteParams] = None,
550
+ *,
551
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
552
+ ) -> AsyncIterator[AsyncResult[Unpack[TupleAny]]]:
553
+ """Execute a statement and return an awaitable yielding a
554
+ :class:`_asyncio.AsyncResult` object.
555
+
556
+ E.g.::
557
+
558
+ result = await conn.stream(stmt)
559
+ async for row in result:
560
+ print(f"{row}")
561
+
562
+ The :meth:`.AsyncConnection.stream`
563
+ method supports optional context manager use against the
564
+ :class:`.AsyncResult` object, as in::
565
+
566
+ async with conn.stream(stmt) as result:
567
+ async for row in result:
568
+ print(f"{row}")
569
+
570
+ In the above pattern, the :meth:`.AsyncResult.close` method is
571
+ invoked unconditionally, even if the iterator is interrupted by an
572
+ exception throw. Context manager use remains optional, however,
573
+ and the function may be called in either an ``async with fn():`` or
574
+ ``await fn()`` style.
575
+
576
+ .. versionadded:: 2.0.0b3 added context manager support
577
+
578
+
579
+ :return: an awaitable object that will yield an
580
+ :class:`_asyncio.AsyncResult` object.
581
+
582
+ .. seealso::
583
+
584
+ :meth:`.AsyncConnection.stream_scalars`
585
+
586
+ """
587
+ if not self.dialect.supports_server_side_cursors:
588
+ raise exc.InvalidRequestError(
589
+ "Can't use `stream` or `stream_scalars` with the current "
590
+ "dialect since it does not support server side cursors."
591
+ )
592
+
593
+ result = await greenlet_spawn(
594
+ self._proxied.execute,
595
+ statement,
596
+ parameters,
597
+ execution_options=util.EMPTY_DICT.merge_with(
598
+ execution_options, _stream_results
599
+ ),
600
+ _require_await=True,
601
+ )
602
+ assert result.context._is_server_side
603
+ ar = AsyncResult(result)
604
+ try:
605
+ yield ar
606
+ except GeneratorExit:
607
+ pass
608
+ else:
609
+ task = asyncio.create_task(ar.close())
610
+ await asyncio.shield(task)
611
+
612
+ @overload
613
+ async def execute(
614
+ self,
615
+ statement: TypedReturnsRows[Unpack[_Ts]],
616
+ parameters: Optional[_CoreAnyExecuteParams] = None,
617
+ *,
618
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
619
+ ) -> CursorResult[Unpack[_Ts]]: ...
620
+
621
+ @overload
622
+ async def execute(
623
+ self,
624
+ statement: Executable,
625
+ parameters: Optional[_CoreAnyExecuteParams] = None,
626
+ *,
627
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
628
+ ) -> CursorResult[Unpack[TupleAny]]: ...
629
+
630
+ async def execute(
631
+ self,
632
+ statement: Executable,
633
+ parameters: Optional[_CoreAnyExecuteParams] = None,
634
+ *,
635
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
636
+ ) -> CursorResult[Unpack[TupleAny]]:
637
+ r"""Executes a SQL statement construct and return a buffered
638
+ :class:`_engine.Result`.
639
+
640
+ :param object: The statement to be executed. This is always
641
+ an object that is in both the :class:`_expression.ClauseElement` and
642
+ :class:`_expression.Executable` hierarchies, including:
643
+
644
+ * :class:`_expression.Select`
645
+ * :class:`_expression.Insert`, :class:`_expression.Update`,
646
+ :class:`_expression.Delete`
647
+ * :class:`_expression.TextClause` and
648
+ :class:`_expression.TextualSelect`
649
+ * :class:`_schema.DDL` and objects which inherit from
650
+ :class:`_schema.ExecutableDDLElement`
651
+
652
+ :param parameters: parameters which will be bound into the statement.
653
+ This may be either a dictionary of parameter names to values,
654
+ or a mutable sequence (e.g. a list) of dictionaries. When a
655
+ list of dictionaries is passed, the underlying statement execution
656
+ will make use of the DBAPI ``cursor.executemany()`` method.
657
+ When a single dictionary is passed, the DBAPI ``cursor.execute()``
658
+ method will be used.
659
+
660
+ :param execution_options: optional dictionary of execution options,
661
+ which will be associated with the statement execution. This
662
+ dictionary can provide a subset of the options that are accepted
663
+ by :meth:`_engine.Connection.execution_options`.
664
+
665
+ :return: a :class:`_engine.Result` object.
666
+
667
+ """
668
+ result = await greenlet_spawn(
669
+ self._proxied.execute,
670
+ statement,
671
+ parameters,
672
+ execution_options=execution_options,
673
+ _require_await=True,
674
+ )
675
+ return await _ensure_sync_result(result, self.execute)
676
+
677
+ # special case to handle mypy issue:
678
+ # https://github.com/python/mypy/issues/20651
679
+ @overload
680
+ async def scalar(
681
+ self,
682
+ statement: TypedReturnsRows[Never],
683
+ parameters: Optional[_CoreSingleExecuteParams] = None,
684
+ *,
685
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
686
+ ) -> Optional[Any]: ...
687
+
688
+ @overload
689
+ async def scalar(
690
+ self,
691
+ statement: TypedReturnsRows[_T],
692
+ parameters: Optional[_CoreSingleExecuteParams] = None,
693
+ *,
694
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
695
+ ) -> Optional[_T]: ...
696
+
697
+ @overload
698
+ async def scalar(
699
+ self,
700
+ statement: Executable,
701
+ parameters: Optional[_CoreSingleExecuteParams] = None,
702
+ *,
703
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
704
+ ) -> Any: ...
705
+
706
+ async def scalar(
707
+ self,
708
+ statement: Executable,
709
+ parameters: Optional[_CoreSingleExecuteParams] = None,
710
+ *,
711
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
712
+ ) -> Any:
713
+ r"""Executes a SQL statement construct and returns a scalar object.
714
+
715
+ This method is shorthand for invoking the
716
+ :meth:`_engine.Result.scalar` method after invoking the
717
+ :meth:`_engine.Connection.execute` method. Parameters are equivalent.
718
+
719
+ :return: a scalar Python value representing the first column of the
720
+ first row returned.
721
+
722
+ """
723
+ result = await self.execute(
724
+ statement, parameters, execution_options=execution_options
725
+ )
726
+ return result.scalar()
727
+
728
+ @overload
729
+ async def scalars(
730
+ self,
731
+ statement: TypedReturnsRows[_T],
732
+ parameters: Optional[_CoreAnyExecuteParams] = None,
733
+ *,
734
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
735
+ ) -> ScalarResult[_T]: ...
736
+
737
+ @overload
738
+ async def scalars(
739
+ self,
740
+ statement: Executable,
741
+ parameters: Optional[_CoreAnyExecuteParams] = None,
742
+ *,
743
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
744
+ ) -> ScalarResult[Any]: ...
745
+
746
+ async def scalars(
747
+ self,
748
+ statement: Executable,
749
+ parameters: Optional[_CoreAnyExecuteParams] = None,
750
+ *,
751
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
752
+ ) -> ScalarResult[Any]:
753
+ r"""Executes a SQL statement construct and returns a scalar objects.
754
+
755
+ This method is shorthand for invoking the
756
+ :meth:`_engine.Result.scalars` method after invoking the
757
+ :meth:`_engine.Connection.execute` method. Parameters are equivalent.
758
+
759
+ :return: a :class:`_engine.ScalarResult` object.
760
+
761
+ .. versionadded:: 1.4.24
762
+
763
+ """
764
+ result = await self.execute(
765
+ statement, parameters, execution_options=execution_options
766
+ )
767
+ return result.scalars()
768
+
769
+ @overload
770
+ def stream_scalars(
771
+ self,
772
+ statement: TypedReturnsRows[_T],
773
+ parameters: Optional[_CoreSingleExecuteParams] = None,
774
+ *,
775
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
776
+ ) -> GeneratorStartableContext[AsyncScalarResult[_T]]: ...
777
+
778
+ @overload
779
+ def stream_scalars(
780
+ self,
781
+ statement: Executable,
782
+ parameters: Optional[_CoreSingleExecuteParams] = None,
783
+ *,
784
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
785
+ ) -> GeneratorStartableContext[AsyncScalarResult[Any]]: ...
786
+
787
+ @asyncstartablecontext
788
+ async def stream_scalars(
789
+ self,
790
+ statement: Executable,
791
+ parameters: Optional[_CoreSingleExecuteParams] = None,
792
+ *,
793
+ execution_options: Optional[CoreExecuteOptionsParameter] = None,
794
+ ) -> AsyncIterator[AsyncScalarResult[Any]]:
795
+ r"""Execute a statement and return an awaitable yielding a
796
+ :class:`_asyncio.AsyncScalarResult` object.
797
+
798
+ E.g.::
799
+
800
+ result = await conn.stream_scalars(stmt)
801
+ async for scalar in result:
802
+ print(f"{scalar}")
803
+
804
+ This method is shorthand for invoking the
805
+ :meth:`_engine.AsyncResult.scalars` method after invoking the
806
+ :meth:`_engine.Connection.stream` method. Parameters are equivalent.
807
+
808
+ The :meth:`.AsyncConnection.stream_scalars`
809
+ method supports optional context manager use against the
810
+ :class:`.AsyncScalarResult` object, as in::
811
+
812
+ async with conn.stream_scalars(stmt) as result:
813
+ async for scalar in result:
814
+ print(f"{scalar}")
815
+
816
+ In the above pattern, the :meth:`.AsyncScalarResult.close` method is
817
+ invoked unconditionally, even if the iterator is interrupted by an
818
+ exception throw. Context manager use remains optional, however,
819
+ and the function may be called in either an ``async with fn():`` or
820
+ ``await fn()`` style.
821
+
822
+ .. versionadded:: 2.0.0b3 added context manager support
823
+
824
+ :return: an awaitable object that will yield an
825
+ :class:`_asyncio.AsyncScalarResult` object.
826
+
827
+ .. versionadded:: 1.4.24
828
+
829
+ .. seealso::
830
+
831
+ :meth:`.AsyncConnection.stream`
832
+
833
+ """
834
+
835
+ async with self.stream(
836
+ statement, parameters, execution_options=execution_options
837
+ ) as result:
838
+ yield result.scalars()
839
+
840
+ async def run_sync(
841
+ self,
842
+ fn: Callable[Concatenate[Connection, _P], _T],
843
+ *arg: _P.args,
844
+ **kw: _P.kwargs,
845
+ ) -> _T:
846
+ '''Invoke the given synchronous (i.e. not async) callable,
847
+ passing a synchronous-style :class:`_engine.Connection` as the first
848
+ argument.
849
+
850
+ This method allows traditional synchronous SQLAlchemy functions to
851
+ run within the context of an asyncio application.
852
+
853
+ E.g.::
854
+
855
+ def do_something_with_core(conn: Connection, arg1: int, arg2: str) -> str:
856
+ """A synchronous function that does not require awaiting
857
+
858
+ :param conn: a Core SQLAlchemy Connection, used synchronously
859
+
860
+ :return: an optional return value is supported
861
+
862
+ """
863
+ conn.execute(some_table.insert().values(int_col=arg1, str_col=arg2))
864
+ return "success"
865
+
866
+
867
+ async def do_something_async(async_engine: AsyncEngine) -> None:
868
+ """an async function that uses awaiting"""
869
+
870
+ async with async_engine.begin() as async_conn:
871
+ # run do_something_with_core() with a sync-style
872
+ # Connection, proxied into an awaitable
873
+ return_code = await async_conn.run_sync(
874
+ do_something_with_core, 5, "strval"
875
+ )
876
+ print(return_code)
877
+
878
+ This method maintains the asyncio event loop all the way through
879
+ to the database connection by running the given callable in a
880
+ specially instrumented greenlet.
881
+
882
+ The most rudimentary use of :meth:`.AsyncConnection.run_sync` is to
883
+ invoke methods such as :meth:`_schema.MetaData.create_all`, given
884
+ an :class:`.AsyncConnection` that needs to be provided to
885
+ :meth:`_schema.MetaData.create_all` as a :class:`_engine.Connection`
886
+ object::
887
+
888
+ # run metadata.create_all(conn) with a sync-style Connection,
889
+ # proxied into an awaitable
890
+ with async_engine.begin() as conn:
891
+ await conn.run_sync(metadata.create_all)
892
+
893
+ .. note::
894
+
895
+ The provided callable is invoked inline within the asyncio event
896
+ loop, and will block on traditional IO calls. IO within this
897
+ callable should only call into SQLAlchemy's asyncio database
898
+ APIs which will be properly adapted to the greenlet context.
899
+
900
+ .. seealso::
901
+
902
+ :meth:`.AsyncSession.run_sync`
903
+
904
+ :ref:`session_run_sync`
905
+
906
+ ''' # noqa: E501
907
+
908
+ return await greenlet_spawn(
909
+ fn, self._proxied, *arg, _require_await=False, **kw
910
+ )
911
+
912
+ def __await__(self) -> Generator[Any, None, AsyncConnection]:
913
+ return self.start().__await__()
914
+
915
+ async def __aexit__(self, type_: Any, value: Any, traceback: Any) -> None:
916
+ task = asyncio.create_task(self.close())
917
+ await asyncio.shield(task)
918
+
919
+ # START PROXY METHODS AsyncConnection
920
+
921
+ # code within this block is **programmatically,
922
+ # statically generated** by tools/generate_proxy_methods.py
923
+
924
+ @property
925
+ def closed(self) -> bool:
926
+ r"""Return True if this connection is closed.
927
+
928
+ .. container:: class_bases
929
+
930
+ Proxied for the :class:`_engine.Connection` class
931
+ on behalf of the :class:`_asyncio.AsyncConnection` class.
932
+
933
+ """ # noqa: E501
934
+
935
+ return self._proxied.closed
936
+
937
+ @property
938
+ def invalidated(self) -> bool:
939
+ r"""Return True if this connection was invalidated.
940
+
941
+ .. container:: class_bases
942
+
943
+ Proxied for the :class:`_engine.Connection` class
944
+ on behalf of the :class:`_asyncio.AsyncConnection` class.
945
+
946
+ This does not indicate whether or not the connection was
947
+ invalidated at the pool level, however
948
+
949
+
950
+ """ # noqa: E501
951
+
952
+ return self._proxied.invalidated
953
+
954
+ @property
955
+ def dialect(self) -> Dialect:
956
+ r"""Proxy for the :attr:`_engine.Connection.dialect` attribute
957
+ on behalf of the :class:`_asyncio.AsyncConnection` class.
958
+
959
+ """ # noqa: E501
960
+
961
+ return self._proxied.dialect
962
+
963
+ @dialect.setter
964
+ def dialect(self, attr: Dialect) -> None:
965
+ self._proxied.dialect = attr
966
+
967
+ @property
968
+ def default_isolation_level(self) -> Optional[IsolationLevel]:
969
+ r"""The initial-connection time isolation level associated with the
970
+ :class:`_engine.Dialect` in use.
971
+
972
+ .. container:: class_bases
973
+
974
+ Proxied for the :class:`_engine.Connection` class
975
+ on behalf of the :class:`_asyncio.AsyncConnection` class.
976
+
977
+ This value is independent of the
978
+ :paramref:`.Connection.execution_options.isolation_level` and
979
+ :paramref:`.Engine.execution_options.isolation_level` execution
980
+ options, and is determined by the :class:`_engine.Dialect` when the
981
+ first connection is created, by performing a SQL query against the
982
+ database for the current isolation level before any additional commands
983
+ have been emitted.
984
+
985
+ Calling this accessor does not invoke any new SQL queries.
986
+
987
+ .. seealso::
988
+
989
+ :meth:`_engine.Connection.get_isolation_level`
990
+ - view current actual isolation level
991
+
992
+ :paramref:`_sa.create_engine.isolation_level`
993
+ - set per :class:`_engine.Engine` isolation level
994
+
995
+ :paramref:`.Connection.execution_options.isolation_level`
996
+ - set per :class:`_engine.Connection` isolation level
997
+
998
+
999
+ """ # noqa: E501
1000
+
1001
+ return self._proxied.default_isolation_level
1002
+
1003
+ # END PROXY METHODS AsyncConnection
1004
+
1005
+
1006
+ @util.create_proxy_methods(
1007
+ Engine,
1008
+ ":class:`_engine.Engine`",
1009
+ ":class:`_asyncio.AsyncEngine`",
1010
+ classmethods=[],
1011
+ methods=[
1012
+ "clear_compiled_cache",
1013
+ "update_execution_options",
1014
+ "get_execution_options",
1015
+ ],
1016
+ attributes=["url", "pool", "dialect", "engine", "name", "driver", "echo"],
1017
+ )
1018
+ # "Class has incompatible disjoint bases" - no idea
1019
+ class AsyncEngine(ProxyComparable[Engine], AsyncConnectable): # type: ignore[misc] # noqa:E501
1020
+ """An asyncio proxy for a :class:`_engine.Engine`.
1021
+
1022
+ :class:`_asyncio.AsyncEngine` is acquired using the
1023
+ :func:`_asyncio.create_async_engine` function::
1024
+
1025
+ from sqlalchemy.ext.asyncio import create_async_engine
1026
+
1027
+ engine = create_async_engine("postgresql+asyncpg://user:pass@host/dbname")
1028
+
1029
+ .. versionadded:: 1.4
1030
+
1031
+ """ # noqa
1032
+
1033
+ # AsyncEngine is a thin proxy; no state should be added here
1034
+ # that is not retrievable from the "sync" engine / connection, e.g.
1035
+ # current transaction, info, etc. It should be possible to
1036
+ # create a new AsyncEngine that matches this one given only the
1037
+ # "sync" elements.
1038
+ __slots__ = "sync_engine"
1039
+
1040
+ _connection_cls: Type[AsyncConnection] = AsyncConnection
1041
+
1042
+ sync_engine: Engine
1043
+ """Reference to the sync-style :class:`_engine.Engine` this
1044
+ :class:`_asyncio.AsyncEngine` proxies requests towards.
1045
+
1046
+ This instance can be used as an event target.
1047
+
1048
+ .. seealso::
1049
+
1050
+ :ref:`asyncio_events`
1051
+ """
1052
+
1053
+ def __init__(self, sync_engine: Engine):
1054
+ if not sync_engine.dialect.is_async:
1055
+ raise exc.InvalidRequestError(
1056
+ "The asyncio extension requires an async driver to be used. "
1057
+ f"The loaded {sync_engine.dialect.driver!r} is not async."
1058
+ )
1059
+ self.sync_engine = self._assign_proxied(sync_engine)
1060
+
1061
+ @util.ro_non_memoized_property
1062
+ def _proxied(self) -> Engine:
1063
+ return self.sync_engine
1064
+
1065
+ @classmethod
1066
+ def _regenerate_proxy_for_target(
1067
+ cls, target: Engine, **additional_kw: Any # noqa: U100
1068
+ ) -> AsyncEngine:
1069
+ return AsyncEngine(target)
1070
+
1071
+ @contextlib.asynccontextmanager
1072
+ async def begin(self) -> AsyncIterator[AsyncConnection]:
1073
+ """Return a context manager which when entered will deliver an
1074
+ :class:`_asyncio.AsyncConnection` with an
1075
+ :class:`_asyncio.AsyncTransaction` established.
1076
+
1077
+ E.g.::
1078
+
1079
+ async with async_engine.begin() as conn:
1080
+ await conn.execute(
1081
+ text("insert into table (x, y, z) values (1, 2, 3)")
1082
+ )
1083
+ await conn.execute(text("my_special_procedure(5)"))
1084
+
1085
+ """
1086
+ conn = self.connect()
1087
+
1088
+ async with conn:
1089
+ async with conn.begin():
1090
+ yield conn
1091
+
1092
+ def connect(self) -> AsyncConnection:
1093
+ """Return an :class:`_asyncio.AsyncConnection` object.
1094
+
1095
+ The :class:`_asyncio.AsyncConnection` will procure a database
1096
+ connection from the underlying connection pool when it is entered
1097
+ as an async context manager::
1098
+
1099
+ async with async_engine.connect() as conn:
1100
+ result = await conn.execute(select(user_table))
1101
+
1102
+ The :class:`_asyncio.AsyncConnection` may also be started outside of a
1103
+ context manager by invoking its :meth:`_asyncio.AsyncConnection.start`
1104
+ method.
1105
+
1106
+ """
1107
+
1108
+ return self._connection_cls(self)
1109
+
1110
+ async def raw_connection(self) -> PoolProxiedConnection:
1111
+ """Return a "raw" DBAPI connection from the connection pool.
1112
+
1113
+ .. seealso::
1114
+
1115
+ :ref:`dbapi_connections`
1116
+
1117
+ """
1118
+ return await greenlet_spawn(self.sync_engine.raw_connection)
1119
+
1120
+ @overload
1121
+ def execution_options(
1122
+ self,
1123
+ *,
1124
+ compiled_cache: Optional[CompiledCacheType] = ...,
1125
+ logging_token: str = ...,
1126
+ isolation_level: IsolationLevel = ...,
1127
+ insertmanyvalues_page_size: int = ...,
1128
+ schema_translate_map: Optional[SchemaTranslateMapType] = ...,
1129
+ **opt: Any,
1130
+ ) -> AsyncEngine: ...
1131
+
1132
+ @overload
1133
+ def execution_options(self, **opt: Any) -> AsyncEngine: ...
1134
+
1135
+ def execution_options(self, **opt: Any) -> AsyncEngine:
1136
+ """Return a new :class:`_asyncio.AsyncEngine` that will provide
1137
+ :class:`_asyncio.AsyncConnection` objects with the given execution
1138
+ options.
1139
+
1140
+ Proxied from :meth:`_engine.Engine.execution_options`. See that
1141
+ method for details.
1142
+
1143
+ """
1144
+
1145
+ return AsyncEngine(self.sync_engine.execution_options(**opt))
1146
+
1147
+ async def dispose(self, close: bool = True) -> None:
1148
+ """Dispose of the connection pool used by this
1149
+ :class:`_asyncio.AsyncEngine`.
1150
+
1151
+ :param close: if left at its default of ``True``, has the
1152
+ effect of fully closing all **currently checked in**
1153
+ database connections. Connections that are still checked out
1154
+ will **not** be closed, however they will no longer be associated
1155
+ with this :class:`_engine.Engine`,
1156
+ so when they are closed individually, eventually the
1157
+ :class:`_pool.Pool` which they are associated with will
1158
+ be garbage collected and they will be closed out fully, if
1159
+ not already closed on checkin.
1160
+
1161
+ If set to ``False``, the previous connection pool is de-referenced,
1162
+ and otherwise not touched in any way.
1163
+
1164
+ .. seealso::
1165
+
1166
+ :meth:`_engine.Engine.dispose`
1167
+
1168
+ """
1169
+
1170
+ await greenlet_spawn(self.sync_engine.dispose, close=close)
1171
+
1172
+ # START PROXY METHODS AsyncEngine
1173
+
1174
+ # code within this block is **programmatically,
1175
+ # statically generated** by tools/generate_proxy_methods.py
1176
+
1177
+ def clear_compiled_cache(self) -> None:
1178
+ r"""Clear the compiled cache associated with the dialect.
1179
+
1180
+ .. container:: class_bases
1181
+
1182
+ Proxied for the :class:`_engine.Engine` class on
1183
+ behalf of the :class:`_asyncio.AsyncEngine` class.
1184
+
1185
+ This applies **only** to the built-in cache that is established
1186
+ via the :paramref:`_engine.create_engine.query_cache_size` parameter.
1187
+ It will not impact any dictionary caches that were passed via the
1188
+ :paramref:`.Connection.execution_options.compiled_cache` parameter.
1189
+
1190
+ .. versionadded:: 1.4
1191
+
1192
+
1193
+ """ # noqa: E501
1194
+
1195
+ return self._proxied.clear_compiled_cache()
1196
+
1197
+ def update_execution_options(self, **opt: Any) -> None:
1198
+ r"""Update the default execution_options dictionary
1199
+ of this :class:`_engine.Engine`.
1200
+
1201
+ .. container:: class_bases
1202
+
1203
+ Proxied for the :class:`_engine.Engine` class on
1204
+ behalf of the :class:`_asyncio.AsyncEngine` class.
1205
+
1206
+ The given keys/values in \**opt are added to the
1207
+ default execution options that will be used for
1208
+ all connections. The initial contents of this dictionary
1209
+ can be sent via the ``execution_options`` parameter
1210
+ to :func:`_sa.create_engine`.
1211
+
1212
+ .. seealso::
1213
+
1214
+ :meth:`_engine.Connection.execution_options`
1215
+
1216
+ :meth:`_engine.Engine.execution_options`
1217
+
1218
+
1219
+ """ # noqa: E501
1220
+
1221
+ return self._proxied.update_execution_options(**opt)
1222
+
1223
+ def get_execution_options(self) -> _ExecuteOptions:
1224
+ r"""Get the non-SQL options which will take effect during execution.
1225
+
1226
+ .. container:: class_bases
1227
+
1228
+ Proxied for the :class:`_engine.Engine` class on
1229
+ behalf of the :class:`_asyncio.AsyncEngine` class.
1230
+
1231
+ .. seealso::
1232
+
1233
+ :meth:`_engine.Engine.execution_options`
1234
+
1235
+ """ # noqa: E501
1236
+
1237
+ return self._proxied.get_execution_options()
1238
+
1239
+ @property
1240
+ def url(self) -> URL:
1241
+ r"""Proxy for the :attr:`_engine.Engine.url` attribute
1242
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1243
+
1244
+ """ # noqa: E501
1245
+
1246
+ return self._proxied.url
1247
+
1248
+ @url.setter
1249
+ def url(self, attr: URL) -> None:
1250
+ self._proxied.url = attr
1251
+
1252
+ @property
1253
+ def pool(self) -> Pool:
1254
+ r"""Proxy for the :attr:`_engine.Engine.pool` attribute
1255
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1256
+
1257
+ """ # noqa: E501
1258
+
1259
+ return self._proxied.pool
1260
+
1261
+ @pool.setter
1262
+ def pool(self, attr: Pool) -> None:
1263
+ self._proxied.pool = attr
1264
+
1265
+ @property
1266
+ def dialect(self) -> Dialect:
1267
+ r"""Proxy for the :attr:`_engine.Engine.dialect` attribute
1268
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1269
+
1270
+ """ # noqa: E501
1271
+
1272
+ return self._proxied.dialect
1273
+
1274
+ @dialect.setter
1275
+ def dialect(self, attr: Dialect) -> None:
1276
+ self._proxied.dialect = attr
1277
+
1278
+ @property
1279
+ def engine(self) -> Engine:
1280
+ r"""Returns this :class:`.Engine`.
1281
+
1282
+ .. container:: class_bases
1283
+
1284
+ Proxied for the :class:`_engine.Engine` class
1285
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1286
+
1287
+ Used for legacy schemes that accept :class:`.Connection` /
1288
+ :class:`.Engine` objects within the same variable.
1289
+
1290
+
1291
+ """ # noqa: E501
1292
+
1293
+ return self._proxied.engine
1294
+
1295
+ @property
1296
+ def name(self) -> str:
1297
+ r"""String name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
1298
+ in use by this :class:`Engine`.
1299
+
1300
+ .. container:: class_bases
1301
+
1302
+ Proxied for the :class:`_engine.Engine` class
1303
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1304
+
1305
+
1306
+ """ # noqa: E501
1307
+
1308
+ return self._proxied.name
1309
+
1310
+ @property
1311
+ def driver(self) -> str:
1312
+ r"""Driver name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
1313
+ in use by this :class:`Engine`.
1314
+
1315
+ .. container:: class_bases
1316
+
1317
+ Proxied for the :class:`_engine.Engine` class
1318
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1319
+
1320
+
1321
+ """ # noqa: E501
1322
+
1323
+ return self._proxied.driver
1324
+
1325
+ @property
1326
+ def echo(self) -> Any:
1327
+ r"""When ``True``, enable log output for this element.
1328
+
1329
+ .. container:: class_bases
1330
+
1331
+ Proxied for the :class:`_engine.Engine` class
1332
+ on behalf of the :class:`_asyncio.AsyncEngine` class.
1333
+
1334
+ This has the effect of setting the Python logging level for the namespace
1335
+ of this element's class and object reference. A value of boolean ``True``
1336
+ indicates that the loglevel ``logging.INFO`` will be set for the logger,
1337
+ whereas the string value ``debug`` will set the loglevel to
1338
+ ``logging.DEBUG``.
1339
+
1340
+ """ # noqa: E501
1341
+
1342
+ return self._proxied.echo
1343
+
1344
+ @echo.setter
1345
+ def echo(self, attr: Any) -> None:
1346
+ self._proxied.echo = attr
1347
+
1348
+ # END PROXY METHODS AsyncEngine
1349
+
1350
+
1351
+ class AsyncTransaction(
1352
+ ProxyComparable[Transaction], StartableContext["AsyncTransaction"]
1353
+ ):
1354
+ """An asyncio proxy for a :class:`_engine.Transaction`."""
1355
+
1356
+ __slots__ = ("connection", "sync_transaction", "nested")
1357
+
1358
+ sync_transaction: Optional[Transaction]
1359
+ connection: AsyncConnection
1360
+ nested: bool
1361
+
1362
+ def __init__(self, connection: AsyncConnection, nested: bool = False):
1363
+ self.connection = connection
1364
+ self.sync_transaction = None
1365
+ self.nested = nested
1366
+
1367
+ @classmethod
1368
+ def _regenerate_proxy_for_target(
1369
+ cls, target: Transaction, **additional_kw: Any # noqa: U100
1370
+ ) -> AsyncTransaction:
1371
+ sync_connection = target.connection
1372
+ sync_transaction = target
1373
+ nested = isinstance(target, NestedTransaction)
1374
+
1375
+ async_connection = AsyncConnection._retrieve_proxy_for_target(
1376
+ sync_connection
1377
+ )
1378
+ assert async_connection is not None
1379
+
1380
+ obj = cls.__new__(cls)
1381
+ obj.connection = async_connection
1382
+ obj.sync_transaction = obj._assign_proxied(sync_transaction)
1383
+ obj.nested = nested
1384
+ return obj
1385
+
1386
+ @util.ro_non_memoized_property
1387
+ def _proxied(self) -> Transaction:
1388
+ if not self.sync_transaction:
1389
+ self._raise_for_not_started()
1390
+ return self.sync_transaction
1391
+
1392
+ @property
1393
+ def is_valid(self) -> bool:
1394
+ return self._proxied.is_valid
1395
+
1396
+ @property
1397
+ def is_active(self) -> bool:
1398
+ return self._proxied.is_active
1399
+
1400
+ async def close(self) -> None:
1401
+ """Close this :class:`.AsyncTransaction`.
1402
+
1403
+ If this transaction is the base transaction in a begin/commit
1404
+ nesting, the transaction will rollback(). Otherwise, the
1405
+ method returns.
1406
+
1407
+ This is used to cancel a Transaction without affecting the scope of
1408
+ an enclosing transaction.
1409
+
1410
+ """
1411
+ await greenlet_spawn(self._proxied.close)
1412
+
1413
+ async def rollback(self) -> None:
1414
+ """Roll back this :class:`.AsyncTransaction`."""
1415
+ await greenlet_spawn(self._proxied.rollback)
1416
+
1417
+ async def commit(self) -> None:
1418
+ """Commit this :class:`.AsyncTransaction`."""
1419
+
1420
+ await greenlet_spawn(self._proxied.commit)
1421
+
1422
+ async def start(self, is_ctxmanager: bool = False) -> AsyncTransaction:
1423
+ """Start this :class:`_asyncio.AsyncTransaction` object's context
1424
+ outside of using a Python ``with:`` block.
1425
+
1426
+ """
1427
+
1428
+ self.sync_transaction = self._assign_proxied(
1429
+ await greenlet_spawn(
1430
+ self.connection._proxied.begin_nested
1431
+ if self.nested
1432
+ else self.connection._proxied.begin
1433
+ )
1434
+ )
1435
+ if is_ctxmanager:
1436
+ self.sync_transaction.__enter__()
1437
+ return self
1438
+
1439
+ async def __aexit__(self, type_: Any, value: Any, traceback: Any) -> None:
1440
+ await greenlet_spawn(self._proxied.__exit__, type_, value, traceback)
1441
+
1442
+
1443
+ @overload
1444
+ def _get_sync_engine_or_connection(async_engine: AsyncEngine) -> Engine: ...
1445
+
1446
+
1447
+ @overload
1448
+ def _get_sync_engine_or_connection(
1449
+ async_engine: AsyncConnection,
1450
+ ) -> Connection: ...
1451
+
1452
+
1453
+ def _get_sync_engine_or_connection(
1454
+ async_engine: Union[AsyncEngine, AsyncConnection],
1455
+ ) -> Union[Engine, Connection]:
1456
+ if isinstance(async_engine, AsyncConnection):
1457
+ return async_engine._proxied
1458
+
1459
+ try:
1460
+ return async_engine.sync_engine
1461
+ except AttributeError as e:
1462
+ raise exc.ArgumentError(
1463
+ "AsyncEngine expected, got %r" % async_engine
1464
+ ) from e
1465
+
1466
+
1467
+ @inspection._inspects(AsyncConnection)
1468
+ def _no_insp_for_async_conn_yet(
1469
+ subject: AsyncConnection, # noqa: U100
1470
+ ) -> NoReturn:
1471
+ raise exc.NoInspectionAvailable(
1472
+ "Inspection on an AsyncConnection is currently not supported. "
1473
+ "Please use ``run_sync`` to pass a callable where it's possible "
1474
+ "to call ``inspect`` on the passed connection.",
1475
+ code="xd3s",
1476
+ )
1477
+
1478
+
1479
+ @inspection._inspects(AsyncEngine)
1480
+ def _no_insp_for_async_engine_xyet(
1481
+ subject: AsyncEngine, # noqa: U100
1482
+ ) -> NoReturn:
1483
+ raise exc.NoInspectionAvailable(
1484
+ "Inspection on an AsyncEngine is currently not supported. "
1485
+ "Please obtain a connection then use ``conn.run_sync`` to pass a "
1486
+ "callable where it's possible to call ``inspect`` on the "
1487
+ "passed connection.",
1488
+ code="xd3s",
1489
+ )