SQLAlchemy 2.1.0b2__cp313-cp313t-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.
- sqlalchemy/__init__.py +298 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +171 -0
- sqlalchemy/connectors/asyncio.py +476 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +89 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4166 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +140 -0
- sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
- sqlalchemy/dialects/mssql/provision.py +196 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +698 -0
- sqlalchemy/dialects/mysql/__init__.py +106 -0
- sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
- sqlalchemy/dialects/mysql/aiomysql.py +226 -0
- sqlalchemy/dialects/mysql/asyncmy.py +214 -0
- sqlalchemy/dialects/mysql/base.py +3877 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +279 -0
- sqlalchemy/dialects/mysql/enumerated.py +277 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +92 -0
- sqlalchemy/dialects/mysql/mariadb.py +67 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
- sqlalchemy/dialects/mysql/mysqldb.py +312 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +157 -0
- sqlalchemy/dialects/mysql/pyodbc.py +156 -0
- sqlalchemy/dialects/mysql/reflection.py +724 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +845 -0
- sqlalchemy/dialects/oracle/__init__.py +85 -0
- sqlalchemy/dialects/oracle/base.py +3977 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/json.py +158 -0
- sqlalchemy/dialects/oracle/oracledb.py +909 -0
- sqlalchemy/dialects/oracle/provision.py +288 -0
- sqlalchemy/dialects/oracle/types.py +367 -0
- sqlalchemy/dialects/oracle/vector.py +368 -0
- sqlalchemy/dialects/postgresql/__init__.py +171 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
- sqlalchemy/dialects/postgresql/array.py +534 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
- sqlalchemy/dialects/postgresql/base.py +5789 -0
- sqlalchemy/dialects/postgresql/bitstring.py +327 -0
- sqlalchemy/dialects/postgresql/dml.py +360 -0
- sqlalchemy/dialects/postgresql/ext.py +593 -0
- sqlalchemy/dialects/postgresql/hstore.py +423 -0
- sqlalchemy/dialects/postgresql/json.py +408 -0
- sqlalchemy/dialects/postgresql/named_types.py +521 -0
- sqlalchemy/dialects/postgresql/operators.py +130 -0
- sqlalchemy/dialects/postgresql/pg8000.py +670 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
- sqlalchemy/dialects/postgresql/provision.py +184 -0
- sqlalchemy/dialects/postgresql/psycopg.py +799 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1002 -0
- sqlalchemy/dialects/postgresql/types.py +388 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
- sqlalchemy/dialects/sqlite/base.py +3063 -0
- sqlalchemy/dialects/sqlite/dml.py +279 -0
- sqlalchemy/dialects/sqlite/json.py +100 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_processors_cy.py +92 -0
- sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_result_cy.py +633 -0
- sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_row_cy.py +232 -0
- sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_util_cy.py +136 -0
- sqlalchemy/engine/base.py +3354 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +877 -0
- sqlalchemy/engine/cursor.py +2421 -0
- sqlalchemy/engine/default.py +2402 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3495 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +82 -0
- sqlalchemy/engine/reflection.py +2100 -0
- sqlalchemy/engine/result.py +1966 -0
- sqlalchemy/engine/row.py +397 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +922 -0
- sqlalchemy/engine/util.py +156 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +674 -0
- sqlalchemy/event/base.py +472 -0
- sqlalchemy/event/legacy.py +258 -0
- sqlalchemy/event/registry.py +390 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +922 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2072 -0
- sqlalchemy/ext/asyncio/__init__.py +29 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1487 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +994 -0
- sqlalchemy/ext/asyncio/scoping.py +1679 -0
- sqlalchemy/ext/asyncio/session.py +2007 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +559 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +560 -0
- sqlalchemy/ext/horizontal_shard.py +481 -0
- sqlalchemy/ext/hybrid.py +1877 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1081 -0
- sqlalchemy/ext/orderinglist.py +439 -0
- sqlalchemy/ext/serializer.py +185 -0
- sqlalchemy/future/__init__.py +16 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +174 -0
- sqlalchemy/log.py +283 -0
- sqlalchemy/orm/__init__.py +176 -0
- sqlalchemy/orm/_orm_constructors.py +2694 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2868 -0
- sqlalchemy/orm/base.py +976 -0
- sqlalchemy/orm/bulk_persistence.py +2152 -0
- sqlalchemy/orm/clsregistry.py +582 -0
- sqlalchemy/orm/collections.py +1568 -0
- sqlalchemy/orm/context.py +3471 -0
- sqlalchemy/orm/decl_api.py +2280 -0
- sqlalchemy/orm/decl_base.py +2309 -0
- sqlalchemy/orm/dependency.py +1306 -0
- sqlalchemy/orm/descriptor_props.py +1183 -0
- sqlalchemy/orm/dynamic.py +307 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3386 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +746 -0
- sqlalchemy/orm/interfaces.py +1589 -0
- sqlalchemy/orm/loading.py +1684 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4411 -0
- sqlalchemy/orm/path_registry.py +829 -0
- sqlalchemy/orm/persistence.py +1789 -0
- sqlalchemy/orm/properties.py +973 -0
- sqlalchemy/orm/query.py +3528 -0
- sqlalchemy/orm/relationships.py +3570 -0
- sqlalchemy/orm/scoping.py +2232 -0
- sqlalchemy/orm/session.py +5403 -0
- sqlalchemy/orm/state.py +1175 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3492 -0
- sqlalchemy/orm/strategy_options.py +2562 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +798 -0
- sqlalchemy/orm/util.py +2438 -0
- sqlalchemy/orm/writeonly.py +694 -0
- sqlalchemy/pool/__init__.py +41 -0
- sqlalchemy/pool/base.py +1522 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +582 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +74 -0
- sqlalchemy/sql/__init__.py +156 -0
- sqlalchemy/sql/_annotated_cols.py +397 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +2164 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_selectable_constructors.py +840 -0
- sqlalchemy/sql/_typing.py +487 -0
- sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/sql/_util_cy.py +127 -0
- sqlalchemy/sql/annotation.py +590 -0
- sqlalchemy/sql/base.py +2699 -0
- sqlalchemy/sql/cache_key.py +1066 -0
- sqlalchemy/sql/coercions.py +1373 -0
- sqlalchemy/sql/compiler.py +8327 -0
- sqlalchemy/sql/crud.py +1815 -0
- sqlalchemy/sql/ddl.py +1928 -0
- sqlalchemy/sql/default_comparator.py +654 -0
- sqlalchemy/sql/dml.py +1977 -0
- sqlalchemy/sql/elements.py +6033 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +172 -0
- sqlalchemy/sql/functions.py +2305 -0
- sqlalchemy/sql/lambdas.py +1443 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2897 -0
- sqlalchemy/sql/roles.py +332 -0
- sqlalchemy/sql/schema.py +6703 -0
- sqlalchemy/sql/selectable.py +7553 -0
- sqlalchemy/sql/sqltypes.py +4093 -0
- sqlalchemy/sql/traversals.py +1042 -0
- sqlalchemy/sql/type_api.py +2446 -0
- sqlalchemy/sql/util.py +1495 -0
- sqlalchemy/sql/visitors.py +1157 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +1007 -0
- sqlalchemy/testing/assertsql.py +519 -0
- sqlalchemy/testing/asyncio.py +128 -0
- sqlalchemy/testing/config.py +440 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +30 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +247 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +538 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +51 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +892 -0
- sqlalchemy/testing/profiling.py +329 -0
- sqlalchemy/testing/provision.py +613 -0
- sqlalchemy/testing/requirements.py +1978 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +420 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3557 -0
- sqlalchemy/testing/suite/test_results.py +660 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2112 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_table_via_select.py +686 -0
- sqlalchemy/testing/suite/test_types.py +2271 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +535 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +76 -0
- sqlalchemy/util/__init__.py +158 -0
- sqlalchemy/util/_collections.py +688 -0
- sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/util/_collections_cy.pxd +8 -0
- sqlalchemy/util/_collections_cy.py +516 -0
- sqlalchemy/util/_has_cython.py +46 -0
- sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/util/_immutabledict_cy.py +240 -0
- sqlalchemy/util/compat.py +299 -0
- sqlalchemy/util/concurrency.py +322 -0
- sqlalchemy/util/cython.py +79 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2320 -0
- sqlalchemy/util/preloaded.py +152 -0
- sqlalchemy/util/queue.py +304 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +711 -0
- sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
- sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
- sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
- sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.1.0b2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# util/concurrency.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
|
+
"""asyncio-related concurrency functions."""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import sys
|
|
15
|
+
from typing import Any
|
|
16
|
+
from typing import Awaitable
|
|
17
|
+
from typing import Callable
|
|
18
|
+
from typing import Coroutine
|
|
19
|
+
from typing import Literal
|
|
20
|
+
from typing import NoReturn
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
from typing import TypeGuard
|
|
23
|
+
from typing import TypeVar
|
|
24
|
+
from typing import Union
|
|
25
|
+
|
|
26
|
+
from .compat import py311
|
|
27
|
+
from .langhelpers import memoized_property
|
|
28
|
+
from .typing import Self
|
|
29
|
+
from .. import exc
|
|
30
|
+
|
|
31
|
+
_T = TypeVar("_T")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_exit_exception(e: BaseException) -> bool:
|
|
35
|
+
# note asyncio.CancelledError is already BaseException
|
|
36
|
+
# so was an exit exception in any case
|
|
37
|
+
return not isinstance(e, Exception) or isinstance(
|
|
38
|
+
e, (asyncio.TimeoutError, asyncio.CancelledError)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_ERROR_MESSAGE = (
|
|
43
|
+
"The SQLAlchemy asyncio module requires that the Python 'greenlet' "
|
|
44
|
+
"library is installed. In order to ensure this dependency is "
|
|
45
|
+
"available, use the 'sqlalchemy[asyncio]' install target: "
|
|
46
|
+
"'pip install sqlalchemy[asyncio]'"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _not_implemented(*arg: Any, **kw: Any) -> NoReturn:
|
|
51
|
+
raise ImportError(_ERROR_MESSAGE)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _concurrency_shim_cls:
|
|
55
|
+
"""Late import shim for greenlet"""
|
|
56
|
+
|
|
57
|
+
__slots__ = (
|
|
58
|
+
"_has_greenlet",
|
|
59
|
+
"greenlet",
|
|
60
|
+
"_AsyncIoGreenlet",
|
|
61
|
+
"getcurrent",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def _initialize(self, *, raise_: bool = True) -> None:
|
|
65
|
+
"""Import greenlet and initialize the class"""
|
|
66
|
+
if "greenlet" in globals():
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if not TYPE_CHECKING:
|
|
70
|
+
global getcurrent, greenlet, _AsyncIoGreenlet
|
|
71
|
+
global _has_gr_context
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
from greenlet import getcurrent
|
|
75
|
+
from greenlet import greenlet
|
|
76
|
+
except ImportError as e:
|
|
77
|
+
if not TYPE_CHECKING:
|
|
78
|
+
# set greenlet in the global scope to prevent re-init
|
|
79
|
+
greenlet = None
|
|
80
|
+
self._has_greenlet = False
|
|
81
|
+
self._initialize_no_greenlet()
|
|
82
|
+
if raise_:
|
|
83
|
+
raise ImportError(_ERROR_MESSAGE) from e
|
|
84
|
+
else:
|
|
85
|
+
self._has_greenlet = True
|
|
86
|
+
# If greenlet.gr_context is present in current version of greenlet,
|
|
87
|
+
# it will be set with the current context on creation.
|
|
88
|
+
# Refs: https://github.com/python-greenlet/greenlet/pull/198
|
|
89
|
+
_has_gr_context = hasattr(getcurrent(), "gr_context")
|
|
90
|
+
|
|
91
|
+
# implementation based on snaury gist at
|
|
92
|
+
# https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
|
|
93
|
+
# Issue for context: https://github.com/python-greenlet/greenlet/issues/173 # noqa: E501
|
|
94
|
+
|
|
95
|
+
class _AsyncIoGreenlet(greenlet):
|
|
96
|
+
dead: bool
|
|
97
|
+
|
|
98
|
+
__sqlalchemy_greenlet_provider__ = True
|
|
99
|
+
|
|
100
|
+
def __init__(self, fn: Callable[..., Any], driver: greenlet):
|
|
101
|
+
greenlet.__init__(self, fn, driver)
|
|
102
|
+
if _has_gr_context:
|
|
103
|
+
self.gr_context = driver.gr_context
|
|
104
|
+
|
|
105
|
+
self.greenlet = greenlet
|
|
106
|
+
self.getcurrent = getcurrent
|
|
107
|
+
self._AsyncIoGreenlet = _AsyncIoGreenlet
|
|
108
|
+
|
|
109
|
+
def _initialize_no_greenlet(self):
|
|
110
|
+
self.getcurrent = _not_implemented
|
|
111
|
+
self.greenlet = _not_implemented # type: ignore[assignment]
|
|
112
|
+
self._AsyncIoGreenlet = _not_implemented # type: ignore[assignment]
|
|
113
|
+
|
|
114
|
+
def __getattr__(self, key: str) -> Any:
|
|
115
|
+
if key in self.__slots__:
|
|
116
|
+
self._initialize()
|
|
117
|
+
return getattr(self, key)
|
|
118
|
+
else:
|
|
119
|
+
raise AttributeError(key)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
_concurrency_shim = _concurrency_shim_cls()
|
|
123
|
+
|
|
124
|
+
if TYPE_CHECKING:
|
|
125
|
+
_T_co = TypeVar("_T_co", covariant=True)
|
|
126
|
+
|
|
127
|
+
def iscoroutine(
|
|
128
|
+
awaitable: Awaitable[_T_co],
|
|
129
|
+
) -> TypeGuard[Coroutine[Any, Any, _T_co]]: ...
|
|
130
|
+
|
|
131
|
+
else:
|
|
132
|
+
iscoroutine = asyncio.iscoroutine
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _safe_cancel_awaitable(awaitable: Awaitable[Any]) -> None:
|
|
136
|
+
# https://docs.python.org/3/reference/datamodel.html#coroutine.close
|
|
137
|
+
|
|
138
|
+
if iscoroutine(awaitable):
|
|
139
|
+
awaitable.close()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def in_greenlet() -> bool:
|
|
143
|
+
current = _concurrency_shim.getcurrent()
|
|
144
|
+
return getattr(current, "__sqlalchemy_greenlet_provider__", False)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def await_(awaitable: Awaitable[_T]) -> _T:
|
|
148
|
+
"""Awaits an async function in a sync method.
|
|
149
|
+
|
|
150
|
+
The sync method must be inside a :func:`greenlet_spawn` context.
|
|
151
|
+
:func:`await_` calls cannot be nested.
|
|
152
|
+
|
|
153
|
+
:param awaitable: The coroutine to call.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
# this is called in the context greenlet while running fn
|
|
157
|
+
current = _concurrency_shim.getcurrent()
|
|
158
|
+
if not getattr(current, "__sqlalchemy_greenlet_provider__", False):
|
|
159
|
+
_safe_cancel_awaitable(awaitable)
|
|
160
|
+
|
|
161
|
+
raise exc.MissingGreenlet(
|
|
162
|
+
"greenlet_spawn has not been called; can't call await_() "
|
|
163
|
+
"here. Was IO attempted in an unexpected place?"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# returns the control to the driver greenlet passing it
|
|
167
|
+
# a coroutine to run. Once the awaitable is done, the driver greenlet
|
|
168
|
+
# switches back to this greenlet with the result of awaitable that is
|
|
169
|
+
# then returned to the caller (or raised as error)
|
|
170
|
+
assert current.parent
|
|
171
|
+
return current.parent.switch(awaitable) # type: ignore[no-any-return]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
await_only = await_ # old name. deprecated on 2.2
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
async def greenlet_spawn(
|
|
178
|
+
fn: Callable[..., _T],
|
|
179
|
+
*args: Any,
|
|
180
|
+
_require_await: bool = False,
|
|
181
|
+
**kwargs: Any,
|
|
182
|
+
) -> _T:
|
|
183
|
+
"""Runs a sync function ``fn`` in a new greenlet.
|
|
184
|
+
|
|
185
|
+
The sync function can then use :func:`await_` to wait for async
|
|
186
|
+
functions.
|
|
187
|
+
|
|
188
|
+
:param fn: The sync callable to call.
|
|
189
|
+
:param \\*args: Positional arguments to pass to the ``fn`` callable.
|
|
190
|
+
:param \\*\\*kwargs: Keyword arguments to pass to the ``fn`` callable.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
result: Any
|
|
194
|
+
context = _concurrency_shim._AsyncIoGreenlet(
|
|
195
|
+
fn, _concurrency_shim.getcurrent()
|
|
196
|
+
)
|
|
197
|
+
# runs the function synchronously in gl greenlet. If the execution
|
|
198
|
+
# is interrupted by await_, context is not dead and result is a
|
|
199
|
+
# coroutine to wait. If the context is dead the function has
|
|
200
|
+
# returned, and its result can be returned.
|
|
201
|
+
switch_occurred = False
|
|
202
|
+
|
|
203
|
+
result = context.switch(*args, **kwargs)
|
|
204
|
+
while not context.dead:
|
|
205
|
+
switch_occurred = True
|
|
206
|
+
try:
|
|
207
|
+
# wait for a coroutine from await_ and then return its
|
|
208
|
+
# result back to it.
|
|
209
|
+
value = await result
|
|
210
|
+
except BaseException:
|
|
211
|
+
# this allows an exception to be raised within
|
|
212
|
+
# the moderated greenlet so that it can continue
|
|
213
|
+
# its expected flow.
|
|
214
|
+
result = context.throw(*sys.exc_info())
|
|
215
|
+
else:
|
|
216
|
+
result = context.switch(value)
|
|
217
|
+
|
|
218
|
+
if _require_await and not switch_occurred:
|
|
219
|
+
raise exc.AwaitRequired(
|
|
220
|
+
"The current operation required an async execution but none was "
|
|
221
|
+
"detected. This will usually happen when using a non compatible "
|
|
222
|
+
"DBAPI driver. Please ensure that an async DBAPI is used."
|
|
223
|
+
)
|
|
224
|
+
return result # type: ignore[no-any-return]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class AsyncAdaptedLock:
|
|
228
|
+
@memoized_property
|
|
229
|
+
def mutex(self) -> asyncio.Lock:
|
|
230
|
+
# there should not be a race here for coroutines creating the
|
|
231
|
+
# new lock as we are not using await, so therefore no concurrency
|
|
232
|
+
return asyncio.Lock()
|
|
233
|
+
|
|
234
|
+
def __enter__(self) -> bool:
|
|
235
|
+
# await is used to acquire the lock only after the first calling
|
|
236
|
+
# coroutine has created the mutex.
|
|
237
|
+
return await_(self.mutex.acquire())
|
|
238
|
+
|
|
239
|
+
def __exit__(self, *arg: Any, **kw: Any) -> None:
|
|
240
|
+
self.mutex.release()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
if not TYPE_CHECKING and py311:
|
|
244
|
+
_Runner = asyncio.Runner
|
|
245
|
+
else:
|
|
246
|
+
|
|
247
|
+
class _Runner:
|
|
248
|
+
"""Runner implementation for test only"""
|
|
249
|
+
|
|
250
|
+
_loop: Union[None, asyncio.AbstractEventLoop, Literal[False]]
|
|
251
|
+
|
|
252
|
+
def __init__(self) -> None:
|
|
253
|
+
self._loop = None
|
|
254
|
+
|
|
255
|
+
def __enter__(self) -> Self:
|
|
256
|
+
self._lazy_init()
|
|
257
|
+
return self
|
|
258
|
+
|
|
259
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
260
|
+
self.close()
|
|
261
|
+
|
|
262
|
+
def close(self) -> None:
|
|
263
|
+
if self._loop:
|
|
264
|
+
try:
|
|
265
|
+
self._loop.run_until_complete(
|
|
266
|
+
self._loop.shutdown_asyncgens()
|
|
267
|
+
)
|
|
268
|
+
finally:
|
|
269
|
+
self._loop.close()
|
|
270
|
+
self._loop = False
|
|
271
|
+
|
|
272
|
+
def get_loop(self) -> asyncio.AbstractEventLoop:
|
|
273
|
+
"""Return embedded event loop."""
|
|
274
|
+
self._lazy_init()
|
|
275
|
+
assert self._loop
|
|
276
|
+
return self._loop
|
|
277
|
+
|
|
278
|
+
def run(self, coro: Coroutine[Any, Any, _T]) -> _T:
|
|
279
|
+
self._lazy_init()
|
|
280
|
+
assert self._loop
|
|
281
|
+
return self._loop.run_until_complete(coro)
|
|
282
|
+
|
|
283
|
+
def _lazy_init(self) -> None:
|
|
284
|
+
if self._loop is False:
|
|
285
|
+
raise RuntimeError("Runner is closed")
|
|
286
|
+
if self._loop is None:
|
|
287
|
+
self._loop = asyncio.new_event_loop()
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class _AsyncUtil:
|
|
291
|
+
"""Asyncio util for test suite/ util only"""
|
|
292
|
+
|
|
293
|
+
def __init__(self) -> None:
|
|
294
|
+
self.runner = _Runner() # runner it lazy so it can be created here
|
|
295
|
+
|
|
296
|
+
def run(
|
|
297
|
+
self,
|
|
298
|
+
fn: Callable[..., Coroutine[Any, Any, _T]],
|
|
299
|
+
*args: Any,
|
|
300
|
+
**kwargs: Any,
|
|
301
|
+
) -> _T:
|
|
302
|
+
"""Run coroutine on the loop"""
|
|
303
|
+
return self.runner.run(fn(*args, **kwargs))
|
|
304
|
+
|
|
305
|
+
def run_in_greenlet(
|
|
306
|
+
self, fn: Callable[..., _T], *args: Any, **kwargs: Any
|
|
307
|
+
) -> _T:
|
|
308
|
+
"""Run sync function in greenlet. Support nested calls"""
|
|
309
|
+
_concurrency_shim._initialize(raise_=False)
|
|
310
|
+
|
|
311
|
+
if _concurrency_shim._has_greenlet:
|
|
312
|
+
if self.runner.get_loop().is_running():
|
|
313
|
+
# allow for a wrapped test function to call another
|
|
314
|
+
assert in_greenlet()
|
|
315
|
+
return fn(*args, **kwargs)
|
|
316
|
+
else:
|
|
317
|
+
return self.runner.run(greenlet_spawn(fn, *args, **kwargs))
|
|
318
|
+
else:
|
|
319
|
+
return fn(*args, **kwargs)
|
|
320
|
+
|
|
321
|
+
def close(self) -> None:
|
|
322
|
+
self.runner.close()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# util/cython.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
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import Callable
|
|
11
|
+
from typing import Type
|
|
12
|
+
from typing import TypeVar
|
|
13
|
+
|
|
14
|
+
_T = TypeVar("_T")
|
|
15
|
+
_NO_OP = Callable[[_T], _T]
|
|
16
|
+
|
|
17
|
+
# cython module shims
|
|
18
|
+
# --
|
|
19
|
+
IS_SHIM = True
|
|
20
|
+
# constants
|
|
21
|
+
compiled = False
|
|
22
|
+
|
|
23
|
+
# types
|
|
24
|
+
int = int # noqa: A001
|
|
25
|
+
char = int
|
|
26
|
+
bint = bool
|
|
27
|
+
longlong = int
|
|
28
|
+
ulonglong = int
|
|
29
|
+
Py_ssize_t = int
|
|
30
|
+
uint = int
|
|
31
|
+
float = float # noqa: A001
|
|
32
|
+
double = float
|
|
33
|
+
void = Any
|
|
34
|
+
NULL = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# functions
|
|
38
|
+
def _no_op(fn: _T) -> _T:
|
|
39
|
+
return fn
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
cclass = _no_op # equivalent to "cdef class"
|
|
43
|
+
ccall = _no_op # equivalent to "cpdef" function
|
|
44
|
+
cfunc = _no_op # equivalent to "cdef" function
|
|
45
|
+
inline = _no_op
|
|
46
|
+
final = _no_op
|
|
47
|
+
pointer = _no_op # not sure how to express a pointer to a type
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def declare(t: Type[_T], value: Any = None, **kw: Any) -> _T:
|
|
51
|
+
return value # type: ignore[no-any-return]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def annotation_typing(_: bool) -> _NO_OP[_T]:
|
|
55
|
+
return _no_op
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def exceptval(value: Any = None, *, check: bool = False) -> _NO_OP[_T]:
|
|
59
|
+
return _no_op
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def cast(type_: Type[_T], value: Any, *, typecheck: bool = False) -> _T:
|
|
63
|
+
return value # type: ignore[no-any-return]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def returns(_: type) -> _NO_OP[_T]:
|
|
67
|
+
return _no_op
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def locals(**kwargs: Any) -> _NO_OP[_T]: # noqa: A001
|
|
71
|
+
return _no_op
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def wraparound(value: bool) -> _NO_OP[_T]:
|
|
75
|
+
return _no_op
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def boundscheck(value: bool) -> _NO_OP[_T]:
|
|
79
|
+
return _no_op
|