SQLAlchemy 2.0.47__cp313-cp313t-win32.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 (274) hide show
  1. sqlalchemy/__init__.py +283 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +184 -0
  4. sqlalchemy/connectors/asyncio.py +429 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/cyextension/__init__.py +6 -0
  7. sqlalchemy/cyextension/collections.cp313t-win32.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win32.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win32.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win32.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win32.pyd +0 -0
  17. sqlalchemy/cyextension/util.pyx +90 -0
  18. sqlalchemy/dialects/__init__.py +62 -0
  19. sqlalchemy/dialects/_typing.py +30 -0
  20. sqlalchemy/dialects/mssql/__init__.py +88 -0
  21. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  22. sqlalchemy/dialects/mssql/base.py +4093 -0
  23. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  24. sqlalchemy/dialects/mssql/json.py +129 -0
  25. sqlalchemy/dialects/mssql/provision.py +185 -0
  26. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  27. sqlalchemy/dialects/mssql/pyodbc.py +760 -0
  28. sqlalchemy/dialects/mysql/__init__.py +104 -0
  29. sqlalchemy/dialects/mysql/aiomysql.py +250 -0
  30. sqlalchemy/dialects/mysql/asyncmy.py +231 -0
  31. sqlalchemy/dialects/mysql/base.py +3949 -0
  32. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  33. sqlalchemy/dialects/mysql/dml.py +225 -0
  34. sqlalchemy/dialects/mysql/enumerated.py +282 -0
  35. sqlalchemy/dialects/mysql/expression.py +146 -0
  36. sqlalchemy/dialects/mysql/json.py +91 -0
  37. sqlalchemy/dialects/mysql/mariadb.py +72 -0
  38. sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
  39. sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
  40. sqlalchemy/dialects/mysql/mysqldb.py +314 -0
  41. sqlalchemy/dialects/mysql/provision.py +153 -0
  42. sqlalchemy/dialects/mysql/pymysql.py +158 -0
  43. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  44. sqlalchemy/dialects/mysql/reflection.py +727 -0
  45. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  46. sqlalchemy/dialects/mysql/types.py +835 -0
  47. sqlalchemy/dialects/oracle/__init__.py +81 -0
  48. sqlalchemy/dialects/oracle/base.py +3802 -0
  49. sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
  50. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  51. sqlalchemy/dialects/oracle/oracledb.py +941 -0
  52. sqlalchemy/dialects/oracle/provision.py +297 -0
  53. sqlalchemy/dialects/oracle/types.py +316 -0
  54. sqlalchemy/dialects/oracle/vector.py +365 -0
  55. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  56. sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
  57. sqlalchemy/dialects/postgresql/array.py +519 -0
  58. sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
  59. sqlalchemy/dialects/postgresql/base.py +5378 -0
  60. sqlalchemy/dialects/postgresql/dml.py +339 -0
  61. sqlalchemy/dialects/postgresql/ext.py +540 -0
  62. sqlalchemy/dialects/postgresql/hstore.py +406 -0
  63. sqlalchemy/dialects/postgresql/json.py +404 -0
  64. sqlalchemy/dialects/postgresql/named_types.py +524 -0
  65. sqlalchemy/dialects/postgresql/operators.py +129 -0
  66. sqlalchemy/dialects/postgresql/pg8000.py +669 -0
  67. sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
  68. sqlalchemy/dialects/postgresql/provision.py +183 -0
  69. sqlalchemy/dialects/postgresql/psycopg.py +862 -0
  70. sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
  71. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  72. sqlalchemy/dialects/postgresql/ranges.py +1031 -0
  73. sqlalchemy/dialects/postgresql/types.py +313 -0
  74. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  75. sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
  76. sqlalchemy/dialects/sqlite/base.py +3056 -0
  77. sqlalchemy/dialects/sqlite/dml.py +263 -0
  78. sqlalchemy/dialects/sqlite/json.py +92 -0
  79. sqlalchemy/dialects/sqlite/provision.py +229 -0
  80. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  81. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  82. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  83. sqlalchemy/engine/__init__.py +62 -0
  84. sqlalchemy/engine/_py_processors.py +136 -0
  85. sqlalchemy/engine/_py_row.py +128 -0
  86. sqlalchemy/engine/_py_util.py +74 -0
  87. sqlalchemy/engine/base.py +3390 -0
  88. sqlalchemy/engine/characteristics.py +155 -0
  89. sqlalchemy/engine/create.py +893 -0
  90. sqlalchemy/engine/cursor.py +2298 -0
  91. sqlalchemy/engine/default.py +2394 -0
  92. sqlalchemy/engine/events.py +965 -0
  93. sqlalchemy/engine/interfaces.py +3471 -0
  94. sqlalchemy/engine/mock.py +134 -0
  95. sqlalchemy/engine/processors.py +61 -0
  96. sqlalchemy/engine/reflection.py +2102 -0
  97. sqlalchemy/engine/result.py +2399 -0
  98. sqlalchemy/engine/row.py +400 -0
  99. sqlalchemy/engine/strategies.py +16 -0
  100. sqlalchemy/engine/url.py +924 -0
  101. sqlalchemy/engine/util.py +167 -0
  102. sqlalchemy/event/__init__.py +26 -0
  103. sqlalchemy/event/api.py +220 -0
  104. sqlalchemy/event/attr.py +676 -0
  105. sqlalchemy/event/base.py +472 -0
  106. sqlalchemy/event/legacy.py +258 -0
  107. sqlalchemy/event/registry.py +390 -0
  108. sqlalchemy/events.py +17 -0
  109. sqlalchemy/exc.py +832 -0
  110. sqlalchemy/ext/__init__.py +11 -0
  111. sqlalchemy/ext/associationproxy.py +2027 -0
  112. sqlalchemy/ext/asyncio/__init__.py +25 -0
  113. sqlalchemy/ext/asyncio/base.py +281 -0
  114. sqlalchemy/ext/asyncio/engine.py +1471 -0
  115. sqlalchemy/ext/asyncio/exc.py +21 -0
  116. sqlalchemy/ext/asyncio/result.py +965 -0
  117. sqlalchemy/ext/asyncio/scoping.py +1599 -0
  118. sqlalchemy/ext/asyncio/session.py +1947 -0
  119. sqlalchemy/ext/automap.py +1701 -0
  120. sqlalchemy/ext/baked.py +570 -0
  121. sqlalchemy/ext/compiler.py +600 -0
  122. sqlalchemy/ext/declarative/__init__.py +65 -0
  123. sqlalchemy/ext/declarative/extensions.py +564 -0
  124. sqlalchemy/ext/horizontal_shard.py +478 -0
  125. sqlalchemy/ext/hybrid.py +1535 -0
  126. sqlalchemy/ext/indexable.py +364 -0
  127. sqlalchemy/ext/instrumentation.py +450 -0
  128. sqlalchemy/ext/mutable.py +1085 -0
  129. sqlalchemy/ext/mypy/__init__.py +6 -0
  130. sqlalchemy/ext/mypy/apply.py +324 -0
  131. sqlalchemy/ext/mypy/decl_class.py +515 -0
  132. sqlalchemy/ext/mypy/infer.py +590 -0
  133. sqlalchemy/ext/mypy/names.py +335 -0
  134. sqlalchemy/ext/mypy/plugin.py +303 -0
  135. sqlalchemy/ext/mypy/util.py +357 -0
  136. sqlalchemy/ext/orderinglist.py +439 -0
  137. sqlalchemy/ext/serializer.py +185 -0
  138. sqlalchemy/future/__init__.py +16 -0
  139. sqlalchemy/future/engine.py +15 -0
  140. sqlalchemy/inspection.py +174 -0
  141. sqlalchemy/log.py +288 -0
  142. sqlalchemy/orm/__init__.py +171 -0
  143. sqlalchemy/orm/_orm_constructors.py +2661 -0
  144. sqlalchemy/orm/_typing.py +179 -0
  145. sqlalchemy/orm/attributes.py +2845 -0
  146. sqlalchemy/orm/base.py +971 -0
  147. sqlalchemy/orm/bulk_persistence.py +2135 -0
  148. sqlalchemy/orm/clsregistry.py +571 -0
  149. sqlalchemy/orm/collections.py +1627 -0
  150. sqlalchemy/orm/context.py +3334 -0
  151. sqlalchemy/orm/decl_api.py +2004 -0
  152. sqlalchemy/orm/decl_base.py +2192 -0
  153. sqlalchemy/orm/dependency.py +1302 -0
  154. sqlalchemy/orm/descriptor_props.py +1092 -0
  155. sqlalchemy/orm/dynamic.py +300 -0
  156. sqlalchemy/orm/evaluator.py +379 -0
  157. sqlalchemy/orm/events.py +3252 -0
  158. sqlalchemy/orm/exc.py +237 -0
  159. sqlalchemy/orm/identity.py +302 -0
  160. sqlalchemy/orm/instrumentation.py +754 -0
  161. sqlalchemy/orm/interfaces.py +1496 -0
  162. sqlalchemy/orm/loading.py +1686 -0
  163. sqlalchemy/orm/mapped_collection.py +557 -0
  164. sqlalchemy/orm/mapper.py +4444 -0
  165. sqlalchemy/orm/path_registry.py +809 -0
  166. sqlalchemy/orm/persistence.py +1788 -0
  167. sqlalchemy/orm/properties.py +935 -0
  168. sqlalchemy/orm/query.py +3459 -0
  169. sqlalchemy/orm/relationships.py +3508 -0
  170. sqlalchemy/orm/scoping.py +2148 -0
  171. sqlalchemy/orm/session.py +5280 -0
  172. sqlalchemy/orm/state.py +1168 -0
  173. sqlalchemy/orm/state_changes.py +196 -0
  174. sqlalchemy/orm/strategies.py +3470 -0
  175. sqlalchemy/orm/strategy_options.py +2568 -0
  176. sqlalchemy/orm/sync.py +164 -0
  177. sqlalchemy/orm/unitofwork.py +796 -0
  178. sqlalchemy/orm/util.py +2403 -0
  179. sqlalchemy/orm/writeonly.py +674 -0
  180. sqlalchemy/pool/__init__.py +44 -0
  181. sqlalchemy/pool/base.py +1524 -0
  182. sqlalchemy/pool/events.py +375 -0
  183. sqlalchemy/pool/impl.py +588 -0
  184. sqlalchemy/py.typed +0 -0
  185. sqlalchemy/schema.py +69 -0
  186. sqlalchemy/sql/__init__.py +145 -0
  187. sqlalchemy/sql/_dml_constructors.py +132 -0
  188. sqlalchemy/sql/_elements_constructors.py +1872 -0
  189. sqlalchemy/sql/_orm_types.py +20 -0
  190. sqlalchemy/sql/_py_util.py +75 -0
  191. sqlalchemy/sql/_selectable_constructors.py +763 -0
  192. sqlalchemy/sql/_typing.py +482 -0
  193. sqlalchemy/sql/annotation.py +587 -0
  194. sqlalchemy/sql/base.py +2293 -0
  195. sqlalchemy/sql/cache_key.py +1057 -0
  196. sqlalchemy/sql/coercions.py +1404 -0
  197. sqlalchemy/sql/compiler.py +8081 -0
  198. sqlalchemy/sql/crud.py +1752 -0
  199. sqlalchemy/sql/ddl.py +1444 -0
  200. sqlalchemy/sql/default_comparator.py +551 -0
  201. sqlalchemy/sql/dml.py +1850 -0
  202. sqlalchemy/sql/elements.py +5589 -0
  203. sqlalchemy/sql/events.py +458 -0
  204. sqlalchemy/sql/expression.py +159 -0
  205. sqlalchemy/sql/functions.py +2158 -0
  206. sqlalchemy/sql/lambdas.py +1442 -0
  207. sqlalchemy/sql/naming.py +209 -0
  208. sqlalchemy/sql/operators.py +2623 -0
  209. sqlalchemy/sql/roles.py +323 -0
  210. sqlalchemy/sql/schema.py +6222 -0
  211. sqlalchemy/sql/selectable.py +7265 -0
  212. sqlalchemy/sql/sqltypes.py +3930 -0
  213. sqlalchemy/sql/traversals.py +1024 -0
  214. sqlalchemy/sql/type_api.py +2368 -0
  215. sqlalchemy/sql/util.py +1485 -0
  216. sqlalchemy/sql/visitors.py +1164 -0
  217. sqlalchemy/testing/__init__.py +96 -0
  218. sqlalchemy/testing/assertions.py +994 -0
  219. sqlalchemy/testing/assertsql.py +520 -0
  220. sqlalchemy/testing/asyncio.py +135 -0
  221. sqlalchemy/testing/config.py +434 -0
  222. sqlalchemy/testing/engines.py +483 -0
  223. sqlalchemy/testing/entities.py +117 -0
  224. sqlalchemy/testing/exclusions.py +476 -0
  225. sqlalchemy/testing/fixtures/__init__.py +28 -0
  226. sqlalchemy/testing/fixtures/base.py +384 -0
  227. sqlalchemy/testing/fixtures/mypy.py +332 -0
  228. sqlalchemy/testing/fixtures/orm.py +227 -0
  229. sqlalchemy/testing/fixtures/sql.py +482 -0
  230. sqlalchemy/testing/pickleable.py +155 -0
  231. sqlalchemy/testing/plugin/__init__.py +6 -0
  232. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  233. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  234. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  235. sqlalchemy/testing/profiling.py +329 -0
  236. sqlalchemy/testing/provision.py +603 -0
  237. sqlalchemy/testing/requirements.py +1945 -0
  238. sqlalchemy/testing/schema.py +198 -0
  239. sqlalchemy/testing/suite/__init__.py +19 -0
  240. sqlalchemy/testing/suite/test_cte.py +237 -0
  241. sqlalchemy/testing/suite/test_ddl.py +389 -0
  242. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  243. sqlalchemy/testing/suite/test_dialect.py +776 -0
  244. sqlalchemy/testing/suite/test_insert.py +630 -0
  245. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  246. sqlalchemy/testing/suite/test_results.py +504 -0
  247. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  248. sqlalchemy/testing/suite/test_select.py +2010 -0
  249. sqlalchemy/testing/suite/test_sequence.py +317 -0
  250. sqlalchemy/testing/suite/test_types.py +2147 -0
  251. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  252. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  253. sqlalchemy/testing/util.py +535 -0
  254. sqlalchemy/testing/warnings.py +52 -0
  255. sqlalchemy/types.py +74 -0
  256. sqlalchemy/util/__init__.py +162 -0
  257. sqlalchemy/util/_collections.py +712 -0
  258. sqlalchemy/util/_concurrency_py3k.py +288 -0
  259. sqlalchemy/util/_has_cy.py +40 -0
  260. sqlalchemy/util/_py_collections.py +541 -0
  261. sqlalchemy/util/compat.py +421 -0
  262. sqlalchemy/util/concurrency.py +110 -0
  263. sqlalchemy/util/deprecations.py +401 -0
  264. sqlalchemy/util/langhelpers.py +2203 -0
  265. sqlalchemy/util/preloaded.py +150 -0
  266. sqlalchemy/util/queue.py +322 -0
  267. sqlalchemy/util/tool_support.py +201 -0
  268. sqlalchemy/util/topological.py +120 -0
  269. sqlalchemy/util/typing.py +734 -0
  270. sqlalchemy-2.0.47.dist-info/METADATA +243 -0
  271. sqlalchemy-2.0.47.dist-info/RECORD +274 -0
  272. sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
  273. sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
  274. sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
@@ -0,0 +1,288 @@
1
+ # util/_concurrency_py3k.py
2
+ # Copyright (C) 2005-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
+ # mypy: allow-untyped-defs, allow-untyped-calls
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ from contextvars import Context
13
+ import sys
14
+ import typing
15
+ from typing import Any
16
+ from typing import Awaitable
17
+ from typing import Callable
18
+ from typing import Coroutine
19
+ from typing import Optional
20
+ from typing import TYPE_CHECKING
21
+ from typing import TypeVar
22
+ from typing import Union
23
+
24
+ from .langhelpers import memoized_property
25
+ from .. import exc
26
+ from ..util import py311
27
+ from ..util.typing import Literal
28
+ from ..util.typing import Protocol
29
+ from ..util.typing import Self
30
+ from ..util.typing import TypeGuard
31
+
32
+ _T = TypeVar("_T")
33
+
34
+ if typing.TYPE_CHECKING:
35
+
36
+ class greenlet(Protocol):
37
+ dead: bool
38
+ gr_context: Optional[Context]
39
+
40
+ def __init__(self, fn: Callable[..., Any], driver: greenlet): ...
41
+
42
+ def throw(self, *arg: Any) -> Any:
43
+ return None
44
+
45
+ def switch(self, value: Any) -> Any:
46
+ return None
47
+
48
+ def getcurrent() -> greenlet: ...
49
+
50
+ else:
51
+ from greenlet import getcurrent
52
+ from greenlet import greenlet
53
+
54
+
55
+ # If greenlet.gr_context is present in current version of greenlet,
56
+ # it will be set with the current context on creation.
57
+ # Refs: https://github.com/python-greenlet/greenlet/pull/198
58
+ _has_gr_context = hasattr(getcurrent(), "gr_context")
59
+
60
+
61
+ def is_exit_exception(e: BaseException) -> bool:
62
+ # note asyncio.CancelledError is already BaseException
63
+ # so was an exit exception in any case
64
+ return not isinstance(e, Exception) or isinstance(
65
+ e, (asyncio.TimeoutError, asyncio.CancelledError)
66
+ )
67
+
68
+
69
+ # implementation based on snaury gist at
70
+ # https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
71
+ # Issue for context: https://github.com/python-greenlet/greenlet/issues/173
72
+
73
+
74
+ class _AsyncIoGreenlet(greenlet):
75
+ dead: bool
76
+
77
+ __sqlalchemy_greenlet_provider__ = True
78
+
79
+ def __init__(self, fn: Callable[..., Any], driver: greenlet):
80
+ greenlet.__init__(self, fn, driver)
81
+ if _has_gr_context:
82
+ self.gr_context = driver.gr_context
83
+
84
+
85
+ _T_co = TypeVar("_T_co", covariant=True)
86
+
87
+ if TYPE_CHECKING:
88
+
89
+ def iscoroutine(
90
+ awaitable: Awaitable[_T_co],
91
+ ) -> TypeGuard[Coroutine[Any, Any, _T_co]]: ...
92
+
93
+ else:
94
+ iscoroutine = asyncio.iscoroutine
95
+
96
+
97
+ def _safe_cancel_awaitable(awaitable: Awaitable[Any]) -> None:
98
+ # https://docs.python.org/3/reference/datamodel.html#coroutine.close
99
+
100
+ if iscoroutine(awaitable):
101
+ awaitable.close()
102
+
103
+
104
+ def in_greenlet() -> bool:
105
+ current = getcurrent()
106
+ return getattr(current, "__sqlalchemy_greenlet_provider__", False)
107
+
108
+
109
+ def await_only(awaitable: Awaitable[_T]) -> _T:
110
+ """Awaits an async function in a sync method.
111
+
112
+ The sync method must be inside a :func:`greenlet_spawn` context.
113
+ :func:`await_only` calls cannot be nested.
114
+
115
+ :param awaitable: The coroutine to call.
116
+
117
+ """
118
+ # this is called in the context greenlet while running fn
119
+ current = getcurrent()
120
+ if not getattr(current, "__sqlalchemy_greenlet_provider__", False):
121
+ _safe_cancel_awaitable(awaitable)
122
+
123
+ raise exc.MissingGreenlet(
124
+ "greenlet_spawn has not been called; can't call await_only() "
125
+ "here. Was IO attempted in an unexpected place?"
126
+ )
127
+
128
+ # returns the control to the driver greenlet passing it
129
+ # a coroutine to run. Once the awaitable is done, the driver greenlet
130
+ # switches back to this greenlet with the result of awaitable that is
131
+ # then returned to the caller (or raised as error)
132
+ return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
133
+
134
+
135
+ def await_fallback(awaitable: Awaitable[_T]) -> _T:
136
+ """Awaits an async function in a sync method.
137
+
138
+ The sync method must be inside a :func:`greenlet_spawn` context.
139
+ :func:`await_fallback` calls cannot be nested.
140
+
141
+ :param awaitable: The coroutine to call.
142
+
143
+ .. deprecated:: 2.0.24 The ``await_fallback()`` function will be removed
144
+ in SQLAlchemy 2.1. Use :func:`_util.await_only` instead, running the
145
+ function / program / etc. within a top-level greenlet that is set up
146
+ using :func:`_util.greenlet_spawn`.
147
+
148
+ """
149
+
150
+ # this is called in the context greenlet while running fn
151
+ current = getcurrent()
152
+ if not getattr(current, "__sqlalchemy_greenlet_provider__", False):
153
+ loop = get_event_loop()
154
+ if loop.is_running():
155
+ _safe_cancel_awaitable(awaitable)
156
+
157
+ raise exc.MissingGreenlet(
158
+ "greenlet_spawn has not been called and asyncio event "
159
+ "loop is already running; can't call await_fallback() here. "
160
+ "Was IO attempted in an unexpected place?"
161
+ )
162
+ return loop.run_until_complete(awaitable)
163
+
164
+ return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
165
+
166
+
167
+ async def greenlet_spawn(
168
+ fn: Callable[..., _T],
169
+ *args: Any,
170
+ _require_await: bool = False,
171
+ **kwargs: Any,
172
+ ) -> _T:
173
+ """Runs a sync function ``fn`` in a new greenlet.
174
+
175
+ The sync function can then use :func:`await_only` to wait for async
176
+ functions.
177
+
178
+ :param fn: The sync callable to call.
179
+ :param \\*args: Positional arguments to pass to the ``fn`` callable.
180
+ :param \\*\\*kwargs: Keyword arguments to pass to the ``fn`` callable.
181
+ """
182
+
183
+ result: Any
184
+ context = _AsyncIoGreenlet(fn, getcurrent())
185
+ # runs the function synchronously in gl greenlet. If the execution
186
+ # is interrupted by await_only, context is not dead and result is a
187
+ # coroutine to wait. If the context is dead the function has
188
+ # returned, and its result can be returned.
189
+ switch_occurred = False
190
+ result = context.switch(*args, **kwargs)
191
+ while not context.dead:
192
+ switch_occurred = True
193
+ try:
194
+ # wait for a coroutine from await_only and then return its
195
+ # result back to it.
196
+ value = await result
197
+ except BaseException:
198
+ # this allows an exception to be raised within
199
+ # the moderated greenlet so that it can continue
200
+ # its expected flow.
201
+ result = context.throw(*sys.exc_info())
202
+ else:
203
+ result = context.switch(value)
204
+
205
+ if _require_await and not switch_occurred:
206
+ raise exc.AwaitRequired(
207
+ "The current operation required an async execution but none was "
208
+ "detected. This will usually happen when using a non compatible "
209
+ "DBAPI driver. Please ensure that an async DBAPI is used."
210
+ )
211
+ return result # type: ignore[no-any-return]
212
+
213
+
214
+ class AsyncAdaptedLock:
215
+ @memoized_property
216
+ def mutex(self) -> asyncio.Lock:
217
+ # there should not be a race here for coroutines creating the
218
+ # new lock as we are not using await, so therefore no concurrency
219
+ return asyncio.Lock()
220
+
221
+ def __enter__(self) -> bool:
222
+ # await is used to acquire the lock only after the first calling
223
+ # coroutine has created the mutex.
224
+ return await_fallback(self.mutex.acquire())
225
+
226
+ def __exit__(self, *arg: Any, **kw: Any) -> None:
227
+ self.mutex.release()
228
+
229
+
230
+ def get_event_loop() -> asyncio.AbstractEventLoop:
231
+ """vendor asyncio.get_event_loop() for python 3.7 and above.
232
+
233
+ Python 3.10 deprecates get_event_loop() as a standalone.
234
+
235
+ """
236
+ try:
237
+ return asyncio.get_running_loop()
238
+ except RuntimeError:
239
+ # avoid "During handling of the above exception, another exception..."
240
+ pass
241
+ return asyncio.get_event_loop_policy().get_event_loop()
242
+
243
+
244
+ if not TYPE_CHECKING and py311:
245
+ _Runner = asyncio.Runner
246
+ else:
247
+
248
+ class _Runner:
249
+ """Runner implementation for test only"""
250
+
251
+ _loop: Union[None, asyncio.AbstractEventLoop, Literal[False]]
252
+
253
+ def __init__(self) -> None:
254
+ self._loop = None
255
+
256
+ def __enter__(self) -> Self:
257
+ self._lazy_init()
258
+ return self
259
+
260
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
261
+ self.close()
262
+
263
+ def close(self) -> None:
264
+ if self._loop:
265
+ try:
266
+ self._loop.run_until_complete(
267
+ self._loop.shutdown_asyncgens()
268
+ )
269
+ finally:
270
+ self._loop.close()
271
+ self._loop = False
272
+
273
+ def get_loop(self) -> asyncio.AbstractEventLoop:
274
+ """Return embedded event loop."""
275
+ self._lazy_init()
276
+ assert self._loop
277
+ return self._loop
278
+
279
+ def run(self, coro: Coroutine[Any, Any, _T]) -> _T:
280
+ self._lazy_init()
281
+ assert self._loop
282
+ return self._loop.run_until_complete(coro)
283
+
284
+ def _lazy_init(self) -> None:
285
+ if self._loop is False:
286
+ raise RuntimeError("Runner is closed")
287
+ if self._loop is None:
288
+ self._loop = asyncio.new_event_loop()
@@ -0,0 +1,40 @@
1
+ # util/_has_cy.py
2
+ # Copyright (C) 2005-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
+ # mypy: ignore-errors
8
+
9
+ import os
10
+ import typing
11
+
12
+
13
+ def _import_cy_extensions():
14
+ # all cython extension extension modules are treated as optional by the
15
+ # setup, so to ensure that all are compiled, all should be imported here
16
+ from ..cyextension import collections
17
+ from ..cyextension import immutabledict
18
+ from ..cyextension import processors
19
+ from ..cyextension import resultproxy
20
+ from ..cyextension import util
21
+
22
+ return (collections, immutabledict, processors, resultproxy, util)
23
+
24
+
25
+ _CYEXTENSION_MSG: str
26
+ if not typing.TYPE_CHECKING:
27
+ if os.environ.get("DISABLE_SQLALCHEMY_CEXT_RUNTIME"):
28
+ HAS_CYEXTENSION = False
29
+ _CYEXTENSION_MSG = "DISABLE_SQLALCHEMY_CEXT_RUNTIME is set"
30
+ else:
31
+ try:
32
+ _import_cy_extensions()
33
+ except ImportError as err:
34
+ HAS_CYEXTENSION = False
35
+ _CYEXTENSION_MSG = str(err)
36
+ else:
37
+ _CYEXTENSION_MSG = "Loaded"
38
+ HAS_CYEXTENSION = True
39
+ else:
40
+ HAS_CYEXTENSION = False