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,429 @@
1
+ # connectors/asyncio.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
+
8
+ """generic asyncio-adapted versions of DBAPI connection and cursor"""
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import collections
14
+ import sys
15
+ from typing import Any
16
+ from typing import AsyncIterator
17
+ from typing import Deque
18
+ from typing import Iterator
19
+ from typing import NoReturn
20
+ from typing import Optional
21
+ from typing import Sequence
22
+ from typing import Tuple
23
+ from typing import Type
24
+ from typing import TYPE_CHECKING
25
+
26
+ from ..engine import AdaptedConnection
27
+ from ..util import EMPTY_DICT
28
+ from ..util.concurrency import await_fallback
29
+ from ..util.concurrency import await_only
30
+ from ..util.concurrency import in_greenlet
31
+ from ..util.typing import Protocol
32
+
33
+ if TYPE_CHECKING:
34
+ from ..engine.interfaces import _DBAPICursorDescription
35
+ from ..engine.interfaces import _DBAPIMultiExecuteParams
36
+ from ..engine.interfaces import _DBAPISingleExecuteParams
37
+ from ..engine.interfaces import DBAPIModule
38
+ from ..util.typing import Self
39
+
40
+
41
+ class AsyncIODBAPIConnection(Protocol):
42
+ """protocol representing an async adapted version of a
43
+ :pep:`249` database connection.
44
+
45
+
46
+ """
47
+
48
+ # note that async DBAPIs dont agree if close() should be awaitable,
49
+ # so it is omitted here and picked up by the __getattr__ hook below
50
+
51
+ async def commit(self) -> None: ...
52
+
53
+ def cursor(self, *args: Any, **kwargs: Any) -> AsyncIODBAPICursor: ...
54
+
55
+ async def rollback(self) -> None: ...
56
+
57
+ def __getattr__(self, key: str) -> Any: ...
58
+
59
+ def __setattr__(self, key: str, value: Any) -> None: ...
60
+
61
+
62
+ class AsyncIODBAPICursor(Protocol):
63
+ """protocol representing an async adapted version
64
+ of a :pep:`249` database cursor.
65
+
66
+
67
+ """
68
+
69
+ def __aenter__(self) -> Any: ...
70
+
71
+ @property
72
+ def description(
73
+ self,
74
+ ) -> _DBAPICursorDescription:
75
+ """The description attribute of the Cursor."""
76
+ ...
77
+
78
+ @property
79
+ def rowcount(self) -> int: ...
80
+
81
+ arraysize: int
82
+
83
+ lastrowid: int
84
+
85
+ async def close(self) -> None: ...
86
+
87
+ async def execute(
88
+ self,
89
+ operation: Any,
90
+ parameters: Optional[_DBAPISingleExecuteParams] = None,
91
+ ) -> Any: ...
92
+
93
+ async def executemany(
94
+ self,
95
+ operation: Any,
96
+ parameters: _DBAPIMultiExecuteParams,
97
+ ) -> Any: ...
98
+
99
+ async def fetchone(self) -> Optional[Any]: ...
100
+
101
+ async def fetchmany(self, size: Optional[int] = ...) -> Sequence[Any]: ...
102
+
103
+ async def fetchall(self) -> Sequence[Any]: ...
104
+
105
+ async def setinputsizes(self, sizes: Sequence[Any]) -> None: ...
106
+
107
+ def setoutputsize(self, size: Any, column: Any) -> None: ...
108
+
109
+ async def callproc(
110
+ self, procname: str, parameters: Sequence[Any] = ...
111
+ ) -> Any: ...
112
+
113
+ async def nextset(self) -> Optional[bool]: ...
114
+
115
+ def __aiter__(self) -> AsyncIterator[Any]: ...
116
+
117
+
118
+ class AsyncAdapt_dbapi_module:
119
+ if TYPE_CHECKING:
120
+ Error = DBAPIModule.Error
121
+ OperationalError = DBAPIModule.OperationalError
122
+ InterfaceError = DBAPIModule.InterfaceError
123
+ IntegrityError = DBAPIModule.IntegrityError
124
+
125
+ def __getattr__(self, key: str) -> Any: ...
126
+
127
+
128
+ class AsyncAdapt_dbapi_cursor:
129
+ server_side = False
130
+ __slots__ = (
131
+ "_adapt_connection",
132
+ "_connection",
133
+ "await_",
134
+ "_cursor",
135
+ "_rows",
136
+ "_soft_closed_memoized",
137
+ )
138
+
139
+ _awaitable_cursor_close: bool = True
140
+
141
+ _cursor: AsyncIODBAPICursor
142
+ _adapt_connection: AsyncAdapt_dbapi_connection
143
+ _connection: AsyncIODBAPIConnection
144
+ _rows: Deque[Any]
145
+
146
+ def __init__(self, adapt_connection: AsyncAdapt_dbapi_connection):
147
+ self._adapt_connection = adapt_connection
148
+ self._connection = adapt_connection._connection
149
+
150
+ self.await_ = adapt_connection.await_
151
+
152
+ cursor = self._make_new_cursor(self._connection)
153
+ self._cursor = self._aenter_cursor(cursor)
154
+ self._soft_closed_memoized = EMPTY_DICT
155
+ if not self.server_side:
156
+ self._rows = collections.deque()
157
+
158
+ def _aenter_cursor(self, cursor: AsyncIODBAPICursor) -> AsyncIODBAPICursor:
159
+ return self.await_(cursor.__aenter__()) # type: ignore[no-any-return]
160
+
161
+ def _make_new_cursor(
162
+ self, connection: AsyncIODBAPIConnection
163
+ ) -> AsyncIODBAPICursor:
164
+ return connection.cursor()
165
+
166
+ @property
167
+ def description(self) -> Optional[_DBAPICursorDescription]:
168
+ if "description" in self._soft_closed_memoized:
169
+ return self._soft_closed_memoized["description"] # type: ignore[no-any-return] # noqa: E501
170
+ return self._cursor.description
171
+
172
+ @property
173
+ def rowcount(self) -> int:
174
+ return self._cursor.rowcount
175
+
176
+ @property
177
+ def arraysize(self) -> int:
178
+ return self._cursor.arraysize
179
+
180
+ @arraysize.setter
181
+ def arraysize(self, value: int) -> None:
182
+ self._cursor.arraysize = value
183
+
184
+ @property
185
+ def lastrowid(self) -> int:
186
+ return self._cursor.lastrowid
187
+
188
+ async def _async_soft_close(self) -> None:
189
+ """close the cursor but keep the results pending, and memoize the
190
+ description.
191
+
192
+ .. versionadded:: 2.0.44
193
+
194
+ """
195
+
196
+ if not self._awaitable_cursor_close or self.server_side:
197
+ return
198
+
199
+ self._soft_closed_memoized = self._soft_closed_memoized.union(
200
+ {
201
+ "description": self._cursor.description,
202
+ }
203
+ )
204
+ await self._cursor.close()
205
+
206
+ def close(self) -> None:
207
+ self._rows.clear()
208
+
209
+ # updated as of 2.0.44
210
+ # try to "close" the cursor based on what we know about the driver
211
+ # and if we are able to. otherwise, hope that the asyncio
212
+ # extension called _async_soft_close() if the cursor is going into
213
+ # a sync context
214
+ if self._cursor is None or bool(self._soft_closed_memoized):
215
+ return
216
+
217
+ if not self._awaitable_cursor_close:
218
+ self._cursor.close() # type: ignore[unused-coroutine]
219
+ elif in_greenlet():
220
+ self.await_(self._cursor.close())
221
+
222
+ def execute(
223
+ self,
224
+ operation: Any,
225
+ parameters: Optional[_DBAPISingleExecuteParams] = None,
226
+ ) -> Any:
227
+ try:
228
+ return self.await_(self._execute_async(operation, parameters))
229
+ except Exception as error:
230
+ self._adapt_connection._handle_exception(error)
231
+
232
+ def executemany(
233
+ self,
234
+ operation: Any,
235
+ seq_of_parameters: _DBAPIMultiExecuteParams,
236
+ ) -> Any:
237
+ try:
238
+ return self.await_(
239
+ self._executemany_async(operation, seq_of_parameters)
240
+ )
241
+ except Exception as error:
242
+ self._adapt_connection._handle_exception(error)
243
+
244
+ async def _execute_async(
245
+ self, operation: Any, parameters: Optional[_DBAPISingleExecuteParams]
246
+ ) -> Any:
247
+ async with self._adapt_connection._execute_mutex:
248
+ if parameters is None:
249
+ result = await self._cursor.execute(operation)
250
+ else:
251
+ result = await self._cursor.execute(operation, parameters)
252
+
253
+ if self._cursor.description and not self.server_side:
254
+ self._rows = collections.deque(await self._cursor.fetchall())
255
+ return result
256
+
257
+ async def _executemany_async(
258
+ self,
259
+ operation: Any,
260
+ seq_of_parameters: _DBAPIMultiExecuteParams,
261
+ ) -> Any:
262
+ async with self._adapt_connection._execute_mutex:
263
+ return await self._cursor.executemany(operation, seq_of_parameters)
264
+
265
+ def nextset(self) -> None:
266
+ self.await_(self._cursor.nextset())
267
+ if self._cursor.description and not self.server_side:
268
+ self._rows = collections.deque(
269
+ self.await_(self._cursor.fetchall())
270
+ )
271
+
272
+ def setinputsizes(self, *inputsizes: Any) -> None:
273
+ # NOTE: this is overridden in aioodbc due to
274
+ # see https://github.com/aio-libs/aioodbc/issues/451
275
+ # right now
276
+
277
+ return self.await_(self._cursor.setinputsizes(*inputsizes))
278
+
279
+ def __enter__(self) -> Self:
280
+ return self
281
+
282
+ def __exit__(self, type_: Any, value: Any, traceback: Any) -> None:
283
+ self.close()
284
+
285
+ def __iter__(self) -> Iterator[Any]:
286
+ while self._rows:
287
+ yield self._rows.popleft()
288
+
289
+ def fetchone(self) -> Optional[Any]:
290
+ if self._rows:
291
+ return self._rows.popleft()
292
+ else:
293
+ return None
294
+
295
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[Any]:
296
+ if size is None:
297
+ size = self.arraysize
298
+ rr = self._rows
299
+ return [rr.popleft() for _ in range(min(size, len(rr)))]
300
+
301
+ def fetchall(self) -> Sequence[Any]:
302
+ retval = list(self._rows)
303
+ self._rows.clear()
304
+ return retval
305
+
306
+
307
+ class AsyncAdapt_dbapi_ss_cursor(AsyncAdapt_dbapi_cursor):
308
+ __slots__ = ()
309
+ server_side = True
310
+
311
+ def close(self) -> None:
312
+ if self._cursor is not None:
313
+ self.await_(self._cursor.close())
314
+ self._cursor = None # type: ignore
315
+
316
+ def fetchone(self) -> Optional[Any]:
317
+ return self.await_(self._cursor.fetchone())
318
+
319
+ def fetchmany(self, size: Optional[int] = None) -> Any:
320
+ return self.await_(self._cursor.fetchmany(size=size))
321
+
322
+ def fetchall(self) -> Sequence[Any]:
323
+ return self.await_(self._cursor.fetchall())
324
+
325
+ def __iter__(self) -> Iterator[Any]:
326
+ iterator = self._cursor.__aiter__()
327
+ while True:
328
+ try:
329
+ yield self.await_(iterator.__anext__())
330
+ except StopAsyncIteration:
331
+ break
332
+
333
+
334
+ class AsyncAdapt_dbapi_connection(AdaptedConnection):
335
+ _cursor_cls = AsyncAdapt_dbapi_cursor
336
+ _ss_cursor_cls = AsyncAdapt_dbapi_ss_cursor
337
+
338
+ await_ = staticmethod(await_only)
339
+
340
+ __slots__ = ("dbapi", "_execute_mutex")
341
+
342
+ _connection: AsyncIODBAPIConnection
343
+
344
+ def __init__(self, dbapi: Any, connection: AsyncIODBAPIConnection):
345
+ self.dbapi = dbapi
346
+ self._connection = connection
347
+ self._execute_mutex = asyncio.Lock()
348
+
349
+ def cursor(self, server_side: bool = False) -> AsyncAdapt_dbapi_cursor:
350
+ if server_side:
351
+ return self._ss_cursor_cls(self)
352
+ else:
353
+ return self._cursor_cls(self)
354
+
355
+ def execute(
356
+ self,
357
+ operation: Any,
358
+ parameters: Optional[_DBAPISingleExecuteParams] = None,
359
+ ) -> Any:
360
+ """lots of DBAPIs seem to provide this, so include it"""
361
+ cursor = self.cursor()
362
+ cursor.execute(operation, parameters)
363
+ return cursor
364
+
365
+ def _handle_exception(self, error: Exception) -> NoReturn:
366
+ exc_info = sys.exc_info()
367
+
368
+ raise error.with_traceback(exc_info[2])
369
+
370
+ def rollback(self) -> None:
371
+ try:
372
+ self.await_(self._connection.rollback())
373
+ except Exception as error:
374
+ self._handle_exception(error)
375
+
376
+ def commit(self) -> None:
377
+ try:
378
+ self.await_(self._connection.commit())
379
+ except Exception as error:
380
+ self._handle_exception(error)
381
+
382
+ def close(self) -> None:
383
+ self.await_(self._connection.close())
384
+
385
+
386
+ class AsyncAdaptFallback_dbapi_connection(AsyncAdapt_dbapi_connection):
387
+ __slots__ = ()
388
+
389
+ await_ = staticmethod(await_fallback)
390
+
391
+
392
+ class AsyncAdapt_terminate:
393
+ """Mixin for a AsyncAdapt_dbapi_connection to add terminate support."""
394
+
395
+ __slots__ = ()
396
+
397
+ def terminate(self) -> None:
398
+ if in_greenlet():
399
+ # in a greenlet; this is the connection was invalidated case.
400
+ try:
401
+ # try to gracefully close; see #10717
402
+ self.await_(asyncio.shield(self._terminate_graceful_close())) # type: ignore[attr-defined] # noqa: E501
403
+ except self._terminate_handled_exceptions() as e:
404
+ # in the case where we are recycling an old connection
405
+ # that may have already been disconnected, close() will
406
+ # fail. In this case, terminate
407
+ # the connection without any further waiting.
408
+ # see issue #8419
409
+ self._terminate_force_close()
410
+ if isinstance(e, asyncio.CancelledError):
411
+ # re-raise CancelledError if we were cancelled
412
+ raise
413
+ else:
414
+ # not in a greenlet; this is the gc cleanup case
415
+ self._terminate_force_close()
416
+
417
+ def _terminate_handled_exceptions(self) -> Tuple[Type[BaseException], ...]:
418
+ """Returns the exceptions that should be handled when
419
+ calling _graceful_close.
420
+ """
421
+ return (asyncio.TimeoutError, asyncio.CancelledError, OSError)
422
+
423
+ async def _terminate_graceful_close(self) -> None:
424
+ """Try to close connection gracefully"""
425
+ raise NotImplementedError
426
+
427
+ def _terminate_force_close(self) -> None:
428
+ """Terminate the connection"""
429
+ raise NotImplementedError
@@ -0,0 +1,250 @@
1
+ # connectors/pyodbc.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
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ import typing
12
+ from typing import Any
13
+ from typing import Dict
14
+ from typing import List
15
+ from typing import Optional
16
+ from typing import Tuple
17
+ from typing import Union
18
+ from urllib.parse import unquote_plus
19
+
20
+ from . import Connector
21
+ from .. import ExecutionContext
22
+ from .. import pool
23
+ from .. import util
24
+ from ..engine import ConnectArgsType
25
+ from ..engine import Connection
26
+ from ..engine import interfaces
27
+ from ..engine import URL
28
+ from ..sql.type_api import TypeEngine
29
+
30
+ if typing.TYPE_CHECKING:
31
+ from ..engine.interfaces import DBAPIModule
32
+ from ..engine.interfaces import IsolationLevel
33
+
34
+
35
+ class PyODBCConnector(Connector):
36
+ driver = "pyodbc"
37
+
38
+ # this is no longer False for pyodbc in general
39
+ supports_sane_rowcount_returning = True
40
+ supports_sane_multi_rowcount = False
41
+
42
+ supports_native_decimal = True
43
+ default_paramstyle = "named"
44
+
45
+ fast_executemany = False
46
+
47
+ # for non-DSN connections, this *may* be used to
48
+ # hold the desired driver name
49
+ pyodbc_driver_name: Optional[str] = None
50
+
51
+ def __init__(self, use_setinputsizes: bool = False, **kw: Any):
52
+ super().__init__(**kw)
53
+ if use_setinputsizes:
54
+ self.bind_typing = interfaces.BindTyping.SETINPUTSIZES
55
+
56
+ @classmethod
57
+ def import_dbapi(cls) -> DBAPIModule:
58
+ return __import__("pyodbc")
59
+
60
+ def create_connect_args(self, url: URL) -> ConnectArgsType:
61
+ opts = url.translate_connect_args(username="user")
62
+ opts.update(url.query)
63
+
64
+ keys = opts
65
+
66
+ query = url.query
67
+
68
+ connect_args: Dict[str, Any] = {}
69
+ connectors: List[str]
70
+
71
+ for param in ("ansi", "unicode_results", "autocommit"):
72
+ if param in keys:
73
+ connect_args[param] = util.asbool(keys.pop(param))
74
+
75
+ if "odbc_connect" in keys:
76
+ connectors = [unquote_plus(keys.pop("odbc_connect"))]
77
+ else:
78
+
79
+ def check_quote(token: str) -> str:
80
+ if ";" in str(token) or str(token).startswith("{"):
81
+ token = "{%s}" % token.replace("}", "}}")
82
+ return token
83
+
84
+ keys = {k: check_quote(v) for k, v in keys.items()}
85
+
86
+ dsn_connection = "dsn" in keys or (
87
+ "host" in keys and "database" not in keys
88
+ )
89
+ if dsn_connection:
90
+ connectors = [
91
+ "dsn=%s" % (keys.pop("host", "") or keys.pop("dsn", ""))
92
+ ]
93
+ else:
94
+ port = ""
95
+ if "port" in keys and "port" not in query:
96
+ port = ",%d" % int(keys.pop("port"))
97
+
98
+ connectors = []
99
+ driver = keys.pop("driver", self.pyodbc_driver_name)
100
+ if driver is None and keys:
101
+ # note if keys is empty, this is a totally blank URL
102
+ util.warn(
103
+ "No driver name specified; "
104
+ "this is expected by PyODBC when using "
105
+ "DSN-less connections"
106
+ )
107
+ else:
108
+ connectors.append("DRIVER={%s}" % driver)
109
+
110
+ connectors.extend(
111
+ [
112
+ "Server=%s%s" % (keys.pop("host", ""), port),
113
+ "Database=%s" % keys.pop("database", ""),
114
+ ]
115
+ )
116
+
117
+ user = keys.pop("user", None)
118
+ if user:
119
+ connectors.append("UID=%s" % user)
120
+ pwd = keys.pop("password", "")
121
+ if pwd:
122
+ connectors.append("PWD=%s" % pwd)
123
+ else:
124
+ authentication = keys.pop("authentication", None)
125
+ if authentication:
126
+ connectors.append("Authentication=%s" % authentication)
127
+ else:
128
+ connectors.append("Trusted_Connection=Yes")
129
+
130
+ # if set to 'Yes', the ODBC layer will try to automagically
131
+ # convert textual data from your database encoding to your
132
+ # client encoding. This should obviously be set to 'No' if
133
+ # you query a cp1253 encoded database from a latin1 client...
134
+ if "odbc_autotranslate" in keys:
135
+ connectors.append(
136
+ "AutoTranslate=%s" % keys.pop("odbc_autotranslate")
137
+ )
138
+
139
+ connectors.extend(["%s=%s" % (k, v) for k, v in keys.items()])
140
+
141
+ return ((";".join(connectors),), connect_args)
142
+
143
+ def is_disconnect(
144
+ self,
145
+ e: Exception,
146
+ connection: Optional[
147
+ Union[pool.PoolProxiedConnection, interfaces.DBAPIConnection]
148
+ ],
149
+ cursor: Optional[interfaces.DBAPICursor],
150
+ ) -> bool:
151
+ if isinstance(e, self.loaded_dbapi.ProgrammingError):
152
+ return "The cursor's connection has been closed." in str(
153
+ e
154
+ ) or "Attempt to use a closed connection." in str(e)
155
+ else:
156
+ return False
157
+
158
+ def _dbapi_version(self) -> interfaces.VersionInfoType:
159
+ if not self.dbapi:
160
+ return ()
161
+ return self._parse_dbapi_version(self.dbapi.version)
162
+
163
+ def _parse_dbapi_version(self, vers: str) -> interfaces.VersionInfoType:
164
+ m = re.match(r"(?:py.*-)?([\d\.]+)(?:-(\w+))?", vers)
165
+ if not m:
166
+ return ()
167
+ vers_tuple: interfaces.VersionInfoType = tuple(
168
+ [int(x) for x in m.group(1).split(".")]
169
+ )
170
+ if m.group(2):
171
+ vers_tuple += (m.group(2),)
172
+ return vers_tuple
173
+
174
+ def _get_server_version_info(
175
+ self, connection: Connection
176
+ ) -> interfaces.VersionInfoType:
177
+ # NOTE: this function is not reliable, particularly when
178
+ # freetds is in use. Implement database-specific server version
179
+ # queries.
180
+ dbapi_con = connection.connection.dbapi_connection
181
+ version: Tuple[Union[int, str], ...] = ()
182
+ r = re.compile(r"[.\-]")
183
+ for n in r.split(dbapi_con.getinfo(self.dbapi.SQL_DBMS_VER)): # type: ignore[union-attr] # noqa: E501
184
+ try:
185
+ version += (int(n),)
186
+ except ValueError:
187
+ pass
188
+ return tuple(version)
189
+
190
+ def do_set_input_sizes(
191
+ self,
192
+ cursor: interfaces.DBAPICursor,
193
+ list_of_tuples: List[Tuple[str, Any, TypeEngine[Any]]],
194
+ context: ExecutionContext,
195
+ ) -> None:
196
+ # the rules for these types seems a little strange, as you can pass
197
+ # non-tuples as well as tuples, however it seems to assume "0"
198
+ # for the subsequent values if you don't pass a tuple which fails
199
+ # for types such as pyodbc.SQL_WLONGVARCHAR, which is the datatype
200
+ # that ticket #5649 is targeting.
201
+
202
+ # NOTE: as of #6058, this won't be called if the use_setinputsizes
203
+ # parameter were not passed to the dialect, or if no types were
204
+ # specified in list_of_tuples
205
+
206
+ # as of #8177 for 2.0 we assume use_setinputsizes=True and only
207
+ # omit the setinputsizes calls for .executemany() with
208
+ # fast_executemany=True
209
+
210
+ if (
211
+ context.execute_style is interfaces.ExecuteStyle.EXECUTEMANY
212
+ and self.fast_executemany
213
+ ):
214
+ return
215
+
216
+ cursor.setinputsizes(
217
+ [
218
+ (
219
+ (dbtype, None, None)
220
+ if not isinstance(dbtype, tuple)
221
+ else dbtype
222
+ )
223
+ for key, dbtype, sqltype in list_of_tuples
224
+ ]
225
+ )
226
+
227
+ def get_isolation_level_values(
228
+ self, dbapi_conn: interfaces.DBAPIConnection
229
+ ) -> List[IsolationLevel]:
230
+ return [*super().get_isolation_level_values(dbapi_conn), "AUTOCOMMIT"]
231
+
232
+ def set_isolation_level(
233
+ self,
234
+ dbapi_connection: interfaces.DBAPIConnection,
235
+ level: IsolationLevel,
236
+ ) -> None:
237
+ # adjust for ConnectionFairy being present
238
+ # allows attribute set e.g. "connection.autocommit = True"
239
+ # to work properly
240
+
241
+ if level == "AUTOCOMMIT":
242
+ dbapi_connection.autocommit = True
243
+ else:
244
+ dbapi_connection.autocommit = False
245
+ super().set_isolation_level(dbapi_connection, level)
246
+
247
+ def detect_autocommit_setting(
248
+ self, dbapi_conn: interfaces.DBAPIConnection
249
+ ) -> bool:
250
+ return bool(dbapi_conn.autocommit)
@@ -0,0 +1,6 @@
1
+ # cyextension/__init__.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