SQLAlchemy 2.0.53__py3-none-any.whl → 2.1.0__py3-none-any.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 (246) hide show
  1. sqlalchemy/__init__.py +20 -4
  2. sqlalchemy/connectors/aioodbc.py +10 -32
  3. sqlalchemy/connectors/asyncio.py +81 -34
  4. sqlalchemy/connectors/pyodbc.py +7 -18
  5. sqlalchemy/dialects/__init__.py +1 -1
  6. sqlalchemy/dialects/mssql/__init__.py +1 -0
  7. sqlalchemy/dialects/mssql/aioodbc.py +2 -2
  8. sqlalchemy/dialects/mssql/base.py +1253 -532
  9. sqlalchemy/dialects/mssql/information_schema.py +61 -0
  10. sqlalchemy/dialects/mssql/json.py +26 -15
  11. sqlalchemy/dialects/mssql/mssqlpython.py +251 -0
  12. sqlalchemy/dialects/mssql/provision.py +13 -2
  13. sqlalchemy/dialects/mssql/pymssql.py +4 -1
  14. sqlalchemy/dialects/mssql/pyodbc.py +6 -68
  15. sqlalchemy/dialects/mysql/__init__.py +2 -0
  16. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  17. sqlalchemy/dialects/mysql/aiomysql.py +22 -36
  18. sqlalchemy/dialects/mysql/asyncmy.py +21 -33
  19. sqlalchemy/dialects/mysql/base.py +311 -366
  20. sqlalchemy/dialects/mysql/dml.py +74 -19
  21. sqlalchemy/dialects/mysql/enumerated.py +9 -14
  22. sqlalchemy/dialects/mysql/json.py +2 -1
  23. sqlalchemy/dialects/mysql/mariadb.py +8 -13
  24. sqlalchemy/dialects/mysql/mariadbconnector.py +14 -23
  25. sqlalchemy/dialects/mysql/mysqlconnector.py +12 -24
  26. sqlalchemy/dialects/mysql/mysqldb.py +26 -23
  27. sqlalchemy/dialects/mysql/pymysql.py +12 -3
  28. sqlalchemy/dialects/mysql/pyodbc.py +1 -2
  29. sqlalchemy/dialects/mysql/reflection.py +21 -24
  30. sqlalchemy/dialects/mysql/types.py +24 -14
  31. sqlalchemy/dialects/oracle/__init__.py +5 -1
  32. sqlalchemy/dialects/oracle/base.py +213 -172
  33. sqlalchemy/dialects/oracle/cx_oracle.py +274 -94
  34. sqlalchemy/dialects/oracle/json.py +157 -0
  35. sqlalchemy/dialects/oracle/oracledb.py +46 -87
  36. sqlalchemy/dialects/oracle/provision.py +0 -9
  37. sqlalchemy/dialects/oracle/types.py +51 -0
  38. sqlalchemy/dialects/oracle/vector.py +3 -0
  39. sqlalchemy/dialects/postgresql/__init__.py +5 -1
  40. sqlalchemy/dialects/postgresql/_psycopg_common.py +10 -3
  41. sqlalchemy/dialects/postgresql/array.py +17 -2
  42. sqlalchemy/dialects/postgresql/asyncpg.py +224 -193
  43. sqlalchemy/dialects/postgresql/base.py +764 -279
  44. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  45. sqlalchemy/dialects/postgresql/dml.py +39 -17
  46. sqlalchemy/dialects/postgresql/ext.py +69 -16
  47. sqlalchemy/dialects/postgresql/hstore.py +56 -39
  48. sqlalchemy/dialects/postgresql/json.py +12 -8
  49. sqlalchemy/dialects/postgresql/named_types.py +68 -57
  50. sqlalchemy/dialects/postgresql/operators.py +1 -0
  51. sqlalchemy/dialects/postgresql/pg8000.py +15 -27
  52. sqlalchemy/dialects/postgresql/pg_catalog.py +19 -0
  53. sqlalchemy/dialects/postgresql/provision.py +18 -0
  54. sqlalchemy/dialects/postgresql/psycopg.py +111 -157
  55. sqlalchemy/dialects/postgresql/psycopg2.py +11 -17
  56. sqlalchemy/dialects/postgresql/ranges.py +14 -43
  57. sqlalchemy/dialects/postgresql/types.py +87 -12
  58. sqlalchemy/dialects/sqlite/__init__.py +2 -0
  59. sqlalchemy/dialects/sqlite/aiosqlite.py +61 -199
  60. sqlalchemy/dialects/sqlite/base.py +98 -37
  61. sqlalchemy/dialects/sqlite/dml.py +80 -29
  62. sqlalchemy/dialects/sqlite/json.py +59 -17
  63. sqlalchemy/dialects/sqlite/provision.py +8 -0
  64. sqlalchemy/dialects/sqlite/pysqlcipher.py +6 -0
  65. sqlalchemy/dialects/sqlite/pysqlite.py +138 -13
  66. sqlalchemy/engine/_processors_cy.py +92 -0
  67. sqlalchemy/engine/_result_cy.py +711 -0
  68. sqlalchemy/engine/_row_cy.py +232 -0
  69. sqlalchemy/engine/_util_cy.py +136 -0
  70. sqlalchemy/engine/base.py +166 -201
  71. sqlalchemy/engine/create.py +9 -31
  72. sqlalchemy/engine/cursor.py +246 -128
  73. sqlalchemy/engine/default.py +261 -28
  74. sqlalchemy/engine/events.py +8 -8
  75. sqlalchemy/engine/interfaces.py +199 -23
  76. sqlalchemy/engine/processors.py +61 -40
  77. sqlalchemy/engine/reflection.py +45 -16
  78. sqlalchemy/engine/result.py +144 -581
  79. sqlalchemy/engine/row.py +43 -46
  80. sqlalchemy/engine/url.py +5 -7
  81. sqlalchemy/engine/util.py +18 -21
  82. sqlalchemy/event/attr.py +1 -3
  83. sqlalchemy/event/base.py +1 -1
  84. sqlalchemy/exc.py +115 -9
  85. sqlalchemy/ext/associationproxy.py +78 -33
  86. sqlalchemy/ext/asyncio/__init__.py +4 -0
  87. sqlalchemy/ext/asyncio/base.py +5 -5
  88. sqlalchemy/ext/asyncio/engine.py +36 -18
  89. sqlalchemy/ext/asyncio/exc.py +9 -0
  90. sqlalchemy/ext/asyncio/result.py +60 -31
  91. sqlalchemy/ext/asyncio/scoping.py +205 -20
  92. sqlalchemy/ext/asyncio/session.py +343 -22
  93. sqlalchemy/ext/automap.py +13 -13
  94. sqlalchemy/ext/baked.py +0 -11
  95. sqlalchemy/ext/declarative/extensions.py +5 -9
  96. sqlalchemy/ext/horizontal_shard.py +10 -8
  97. sqlalchemy/ext/hybrid.py +416 -74
  98. sqlalchemy/ext/indexable.py +2 -2
  99. sqlalchemy/ext/instrumentation.py +1 -1
  100. sqlalchemy/ext/mutable.py +4 -8
  101. sqlalchemy/ext/orderinglist.py +1 -1
  102. sqlalchemy/ext/serializer.py +2 -2
  103. sqlalchemy/inspection.py +16 -3
  104. sqlalchemy/log.py +5 -14
  105. sqlalchemy/orm/__init__.py +5 -0
  106. sqlalchemy/orm/_orm_constructors.py +71 -21
  107. sqlalchemy/orm/_typing.py +14 -13
  108. sqlalchemy/orm/attributes.py +107 -84
  109. sqlalchemy/orm/base.py +34 -14
  110. sqlalchemy/orm/bulk_persistence.py +54 -37
  111. sqlalchemy/orm/clsregistry.py +100 -41
  112. sqlalchemy/orm/collections.py +12 -71
  113. sqlalchemy/orm/context.py +240 -99
  114. sqlalchemy/orm/decl_api.py +673 -345
  115. sqlalchemy/orm/decl_base.py +686 -592
  116. sqlalchemy/orm/dependency.py +54 -50
  117. sqlalchemy/orm/descriptor_props.py +152 -39
  118. sqlalchemy/orm/dynamic.py +16 -16
  119. sqlalchemy/orm/events.py +209 -75
  120. sqlalchemy/orm/exc.py +1 -1
  121. sqlalchemy/orm/identity.py +2 -2
  122. sqlalchemy/orm/instrumentation.py +12 -16
  123. sqlalchemy/orm/interfaces.py +142 -43
  124. sqlalchemy/orm/loading.py +76 -50
  125. sqlalchemy/orm/mapped_collection.py +3 -3
  126. sqlalchemy/orm/mapper.py +157 -137
  127. sqlalchemy/orm/path_registry.py +103 -62
  128. sqlalchemy/orm/persistence.py +15 -14
  129. sqlalchemy/orm/properties.py +90 -52
  130. sqlalchemy/orm/query.py +142 -80
  131. sqlalchemy/orm/relationships.py +192 -99
  132. sqlalchemy/orm/scoping.py +137 -35
  133. sqlalchemy/orm/session.py +347 -158
  134. sqlalchemy/orm/state.py +22 -15
  135. sqlalchemy/orm/state_changes.py +1 -1
  136. sqlalchemy/orm/strategies.py +201 -131
  137. sqlalchemy/orm/strategy_options.py +128 -71
  138. sqlalchemy/orm/sync.py +6 -6
  139. sqlalchemy/orm/unitofwork.py +27 -25
  140. sqlalchemy/orm/util.py +137 -63
  141. sqlalchemy/orm/writeonly.py +40 -13
  142. sqlalchemy/pool/__init__.py +0 -3
  143. sqlalchemy/pool/base.py +43 -41
  144. sqlalchemy/pool/impl.py +1 -7
  145. sqlalchemy/schema.py +7 -1
  146. sqlalchemy/sql/__init__.py +11 -0
  147. sqlalchemy/sql/_annotated_cols.py +402 -0
  148. sqlalchemy/sql/_cache_key_cy.py +363 -0
  149. sqlalchemy/sql/_elements_constructors.py +355 -49
  150. sqlalchemy/sql/_orm_types.py +1 -1
  151. sqlalchemy/sql/_selectable_constructors.py +135 -58
  152. sqlalchemy/sql/_typing.py +29 -11
  153. sqlalchemy/{cyextension/__init__.py → sql/_util_cy.pxd} +6 -1
  154. sqlalchemy/sql/_util_cy.py +127 -0
  155. sqlalchemy/sql/annotation.py +15 -12
  156. sqlalchemy/sql/base.py +753 -193
  157. sqlalchemy/sql/cache_key.py +163 -305
  158. sqlalchemy/sql/coercions.py +27 -58
  159. sqlalchemy/sql/compiler.py +538 -180
  160. sqlalchemy/sql/crud.py +76 -13
  161. sqlalchemy/sql/ddl.py +599 -82
  162. sqlalchemy/sql/default_comparator.py +247 -138
  163. sqlalchemy/sql/dml.py +251 -87
  164. sqlalchemy/sql/elements.py +849 -384
  165. sqlalchemy/sql/expression.py +13 -0
  166. sqlalchemy/sql/functions.py +223 -42
  167. sqlalchemy/sql/lambdas.py +4 -3
  168. sqlalchemy/sql/naming.py +1 -6
  169. sqlalchemy/sql/operators.py +356 -70
  170. sqlalchemy/sql/roles.py +17 -8
  171. sqlalchemy/sql/schema.py +1181 -324
  172. sqlalchemy/sql/selectable.py +644 -260
  173. sqlalchemy/sql/sqltypes.py +410 -214
  174. sqlalchemy/sql/traversals.py +25 -7
  175. sqlalchemy/sql/type_api.py +124 -42
  176. sqlalchemy/sql/util.py +33 -23
  177. sqlalchemy/sql/visitors.py +15 -26
  178. sqlalchemy/testing/__init__.py +0 -1
  179. sqlalchemy/testing/assertions.py +14 -1
  180. sqlalchemy/testing/assertsql.py +0 -1
  181. sqlalchemy/testing/asyncio.py +0 -7
  182. sqlalchemy/testing/cancellation.py +12 -12
  183. sqlalchemy/testing/config.py +10 -11
  184. sqlalchemy/testing/engines.py +29 -15
  185. sqlalchemy/testing/exclusions.py +28 -3
  186. sqlalchemy/testing/fixtures/__init__.py +2 -0
  187. sqlalchemy/testing/fixtures/base.py +37 -0
  188. sqlalchemy/testing/fixtures/mypy.py +25 -111
  189. sqlalchemy/testing/fixtures/orm.py +1 -1
  190. sqlalchemy/testing/fixtures/sql.py +62 -6
  191. sqlalchemy/testing/plugin/plugin_base.py +0 -4
  192. sqlalchemy/testing/plugin/pytestplugin.py +3 -26
  193. sqlalchemy/testing/profiles_file.py +350 -0
  194. sqlalchemy/testing/profiling.py +98 -132
  195. sqlalchemy/testing/provision.py +37 -6
  196. sqlalchemy/testing/requirements.py +68 -46
  197. sqlalchemy/testing/suite/__init__.py +1 -1
  198. sqlalchemy/testing/suite/test_ddl.py +31 -0
  199. sqlalchemy/testing/suite/test_reflection.py +177 -56
  200. sqlalchemy/testing/suite/test_results.py +156 -0
  201. sqlalchemy/testing/suite/test_select.py +117 -15
  202. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  203. sqlalchemy/testing/suite/test_types.py +134 -10
  204. sqlalchemy/testing/util.py +1 -1
  205. sqlalchemy/types.py +2 -0
  206. sqlalchemy/util/__init__.py +9 -8
  207. sqlalchemy/util/_collections.py +13 -37
  208. sqlalchemy/{cyextension/immutabledict.pxd → util/_collections_cy.pxd} +3 -3
  209. sqlalchemy/util/_collections_cy.py +516 -0
  210. sqlalchemy/util/_has_cython.py +48 -0
  211. sqlalchemy/util/_immutabledict_cy.py +240 -0
  212. sqlalchemy/util/compat.py +27 -149
  213. sqlalchemy/util/concurrency.py +225 -63
  214. sqlalchemy/util/cython.py +95 -0
  215. sqlalchemy/util/deprecations.py +3 -3
  216. sqlalchemy/util/langhelpers.py +603 -109
  217. sqlalchemy/util/preloaded.py +2 -0
  218. sqlalchemy/util/queue.py +5 -23
  219. sqlalchemy/util/typing.py +49 -76
  220. {sqlalchemy-2.0.53.dist-info → sqlalchemy-2.1.0.dist-info}/METADATA +51 -27
  221. sqlalchemy-2.1.0.dist-info/RECORD +268 -0
  222. sqlalchemy/cyextension/collections.pyx +0 -409
  223. sqlalchemy/cyextension/immutabledict.pyx +0 -133
  224. sqlalchemy/cyextension/processors.pyx +0 -68
  225. sqlalchemy/cyextension/resultproxy.pyx +0 -102
  226. sqlalchemy/cyextension/util.pyx +0 -90
  227. sqlalchemy/engine/_py_processors.py +0 -135
  228. sqlalchemy/engine/_py_row.py +0 -128
  229. sqlalchemy/engine/_py_util.py +0 -74
  230. sqlalchemy/ext/mypy/__init__.py +0 -6
  231. sqlalchemy/ext/mypy/apply.py +0 -324
  232. sqlalchemy/ext/mypy/decl_class.py +0 -515
  233. sqlalchemy/ext/mypy/infer.py +0 -590
  234. sqlalchemy/ext/mypy/names.py +0 -335
  235. sqlalchemy/ext/mypy/plugin.py +0 -304
  236. sqlalchemy/ext/mypy/util.py +0 -357
  237. sqlalchemy/sql/_py_util.py +0 -75
  238. sqlalchemy/testing/suite/test_deprecations.py +0 -153
  239. sqlalchemy/util/_concurrency_py3k.py +0 -288
  240. sqlalchemy/util/_has_cy.py +0 -40
  241. sqlalchemy/util/_py_collections.py +0 -541
  242. sqlalchemy-2.0.53.dist-info/RECORD +0 -271
  243. {sqlalchemy-2.0.53.dist-info → sqlalchemy-2.1.0.dist-info}/WHEEL +0 -0
  244. {sqlalchemy-2.0.53.dist-info → sqlalchemy-2.1.0.dist-info}/licenses/AUTHORS +0 -0
  245. {sqlalchemy-2.0.53.dist-info → sqlalchemy-2.1.0.dist-info}/licenses/LICENSE +0 -0
  246. {sqlalchemy-2.0.53.dist-info → sqlalchemy-2.1.0.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py CHANGED
@@ -47,9 +47,6 @@ from .engine import URL as URL
47
47
  from .inspection import inspect as inspect
48
48
  from .pool import AssertionPool as AssertionPool
49
49
  from .pool import AsyncAdaptedQueuePool as AsyncAdaptedQueuePool
50
- from .pool import (
51
- FallbackAsyncAdaptedQueuePool as FallbackAsyncAdaptedQueuePool,
52
- )
53
50
  from .pool import NullPool as NullPool
54
51
  from .pool import Pool as Pool
55
52
  from .pool import PoolProxiedConnection as PoolProxiedConnection
@@ -60,29 +57,40 @@ from .pool import StaticPool as StaticPool
60
57
  from .schema import BaseDDLElement as BaseDDLElement
61
58
  from .schema import BLANK_SCHEMA as BLANK_SCHEMA
62
59
  from .schema import CheckConstraint as CheckConstraint
60
+ from .schema import CheckFirst as CheckFirst
63
61
  from .schema import Column as Column
64
62
  from .schema import ColumnDefault as ColumnDefault
65
63
  from .schema import Computed as Computed
66
64
  from .schema import Constraint as Constraint
65
+ from .schema import CreateTable as CreateTable
66
+ from .schema import CreateTableAs as CreateTableAs
67
+ from .schema import CreateView as CreateView
67
68
  from .schema import DDL as DDL
68
69
  from .schema import DDLElement as DDLElement
69
70
  from .schema import DefaultClause as DefaultClause
71
+ from .schema import DropTable as DropTable
72
+ from .schema import DropView as DropView
70
73
  from .schema import ExecutableDDLElement as ExecutableDDLElement
71
74
  from .schema import FetchedValue as FetchedValue
72
75
  from .schema import ForeignKey as ForeignKey
73
76
  from .schema import ForeignKeyConstraint as ForeignKeyConstraint
77
+ from .schema import ForeignKeyTarget as ForeignKeyTarget
74
78
  from .schema import Identity as Identity
75
79
  from .schema import Index as Index
76
80
  from .schema import insert_sentinel as insert_sentinel
77
81
  from .schema import MetaData as MetaData
82
+ from .schema import Named as Named
78
83
  from .schema import PrimaryKeyConstraint as PrimaryKeyConstraint
79
84
  from .schema import Sequence as Sequence
80
85
  from .schema import Table as Table
86
+ from .schema import TypedColumns as TypedColumns
81
87
  from .schema import UniqueConstraint as UniqueConstraint
82
88
  from .sql import ColumnExpressionArgument as ColumnExpressionArgument
83
89
  from .sql import NotNullable as NotNullable
84
90
  from .sql import Nullable as Nullable
85
91
  from .sql import SelectLabelStyle as SelectLabelStyle
92
+ from .sql.expression import aggregate_order_by as aggregate_order_by
93
+ from .sql.expression import AggregateOrderBy as AggregateOrderBy
86
94
  from .sql.expression import Alias as Alias
87
95
  from .sql.expression import alias as alias
88
96
  from .sql.expression import AliasedReturnsRows as AliasedReturnsRows
@@ -127,6 +135,9 @@ from .sql.expression import Extract as Extract
127
135
  from .sql.expression import extract as extract
128
136
  from .sql.expression import false as false
129
137
  from .sql.expression import False_ as False_
138
+ from .sql.expression import FrameClause as FrameClause
139
+ from .sql.expression import FrameClauseType as FrameClauseType
140
+ from .sql.expression import from_dml_column as from_dml_column
130
141
  from .sql.expression import FromClause as FromClause
131
142
  from .sql.expression import FromGrouping as FromGrouping
132
143
  from .sql.expression import func as func
@@ -171,6 +182,7 @@ from .sql.expression import nullsfirst as nullsfirst
171
182
  from .sql.expression import nullslast as nullslast
172
183
  from .sql.expression import Operators as Operators
173
184
  from .sql.expression import or_ as or_
185
+ from .sql.expression import OrderByList as OrderByList
174
186
  from .sql.expression import outerjoin as outerjoin
175
187
  from .sql.expression import outparam as outparam
176
188
  from .sql.expression import Over as Over
@@ -195,6 +207,7 @@ from .sql.expression import TableClause as TableClause
195
207
  from .sql.expression import TableSample as TableSample
196
208
  from .sql.expression import tablesample as tablesample
197
209
  from .sql.expression import TableValuedAlias as TableValuedAlias
210
+ from .sql.expression import TableValuedColumn as TableValuedColumn
198
211
  from .sql.expression import text as text
199
212
  from .sql.expression import TextAsFrom as TextAsFrom
200
213
  from .sql.expression import TextClause as TextClause
@@ -203,6 +216,8 @@ from .sql.expression import true as true
203
216
  from .sql.expression import True_ as True_
204
217
  from .sql.expression import try_cast as try_cast
205
218
  from .sql.expression import TryCast as TryCast
219
+ from .sql.expression import TString as TString
220
+ from .sql.expression import tstring as tstring
206
221
  from .sql.expression import Tuple as Tuple
207
222
  from .sql.expression import tuple_ as tuple_
208
223
  from .sql.expression import type_coerce as type_coerce
@@ -249,6 +264,7 @@ from .types import LargeBinary as LargeBinary
249
264
  from .types import NCHAR as NCHAR
250
265
  from .types import NUMERIC as NUMERIC
251
266
  from .types import Numeric as Numeric
267
+ from .types import NumericCommon as NumericCommon
252
268
  from .types import NVARCHAR as NVARCHAR
253
269
  from .types import PickleType as PickleType
254
270
  from .types import REAL as REAL
@@ -269,7 +285,7 @@ from .types import Uuid as Uuid
269
285
  from .types import VARBINARY as VARBINARY
270
286
  from .types import VARCHAR as VARCHAR
271
287
 
272
- __version__ = "2.0.53"
288
+ __version__ = "2.1.0"
273
289
 
274
290
 
275
291
  def __go(lcls: Any) -> None:
@@ -13,12 +13,9 @@ from typing import TYPE_CHECKING
13
13
  from .asyncio import AsyncAdapt_dbapi_connection
14
14
  from .asyncio import AsyncAdapt_dbapi_cursor
15
15
  from .asyncio import AsyncAdapt_dbapi_ss_cursor
16
- from .asyncio import AsyncAdaptFallback_dbapi_connection
17
16
  from .pyodbc import PyODBCConnector
18
- from .. import pool
19
- from .. import util
20
- from ..util.concurrency import await_fallback
21
- from ..util.concurrency import await_only
17
+ from ..connectors.asyncio import AsyncAdapt_dbapi_module
18
+ from ..util.concurrency import await_
22
19
 
23
20
  if TYPE_CHECKING:
24
21
  from ..engine.interfaces import ConnectArgsType
@@ -33,7 +30,7 @@ class AsyncAdapt_aioodbc_cursor(AsyncAdapt_dbapi_cursor):
33
30
  return self._cursor._impl.setinputsizes(*inputsizes)
34
31
 
35
32
  # how it's supposed to work
36
- # return self.await_(self._cursor.setinputsizes(*inputsizes))
33
+ # return await_(self._cursor.setinputsizes(*inputsizes))
37
34
 
38
35
  @property
39
36
  def fast_executemany(self):
@@ -67,7 +64,7 @@ class AsyncAdapt_aioodbc_connection(AsyncAdapt_dbapi_connection):
67
64
  self._connection._conn.autocommit = value
68
65
 
69
66
  def ping(self, reconnect):
70
- return self.await_(self._connection.ping(reconnect))
67
+ return await_(self._connection.ping(reconnect))
71
68
 
72
69
  def add_output_converter(self, *arg, **kw):
73
70
  self._connection.add_output_converter(*arg, **kw)
@@ -104,14 +101,9 @@ class AsyncAdapt_aioodbc_connection(AsyncAdapt_dbapi_connection):
104
101
  super().close()
105
102
 
106
103
 
107
- class AsyncAdaptFallback_aioodbc_connection(
108
- AsyncAdaptFallback_dbapi_connection, AsyncAdapt_aioodbc_connection
109
- ):
110
- __slots__ = ()
111
-
112
-
113
- class AsyncAdapt_aioodbc_dbapi:
104
+ class AsyncAdapt_aioodbc_dbapi(AsyncAdapt_dbapi_module):
114
105
  def __init__(self, aioodbc, pyodbc):
106
+ super().__init__(aioodbc, dbapi_module=pyodbc)
115
107
  self.aioodbc = aioodbc
116
108
  self.pyodbc = pyodbc
117
109
  self.paramstyle = pyodbc.paramstyle
@@ -146,19 +138,14 @@ class AsyncAdapt_aioodbc_dbapi:
146
138
  setattr(self, name, getattr(self.pyodbc, name))
147
139
 
148
140
  def connect(self, *arg, **kw):
149
- async_fallback = kw.pop("async_fallback", False)
150
141
  creator_fn = kw.pop("async_creator_fn", self.aioodbc.connect)
151
142
 
152
- if util.asbool(async_fallback):
153
- return AsyncAdaptFallback_aioodbc_connection(
154
- self,
155
- await_fallback(creator_fn(*arg, **kw)),
156
- )
157
- else:
158
- return AsyncAdapt_aioodbc_connection(
143
+ return await_(
144
+ AsyncAdapt_aioodbc_connection.create(
159
145
  self,
160
- await_only(creator_fn(*arg, **kw)),
146
+ creator_fn(*arg, **kw),
161
147
  )
148
+ )
162
149
 
163
150
 
164
151
  class aiodbcConnector(PyODBCConnector):
@@ -180,14 +167,5 @@ class aiodbcConnector(PyODBCConnector):
180
167
 
181
168
  return (), kw
182
169
 
183
- @classmethod
184
- def get_pool_class(cls, url):
185
- async_fallback = url.query.get("async_fallback", False)
186
-
187
- if util.asbool(async_fallback):
188
- return pool.FallbackAsyncAdaptedQueuePool
189
- else:
190
- return pool.AsyncAdaptedQueuePool
191
-
192
170
  def get_driver_connection(self, connection):
193
171
  return connection._connection
@@ -12,23 +12,25 @@ from __future__ import annotations
12
12
  import asyncio
13
13
  import collections
14
14
  import sys
15
+ import types
15
16
  from typing import Any
16
17
  from typing import AsyncIterator
18
+ from typing import Awaitable
17
19
  from typing import Deque
18
20
  from typing import Iterator
19
21
  from typing import NoReturn
20
22
  from typing import Optional
23
+ from typing import Protocol
21
24
  from typing import Sequence
22
25
  from typing import Tuple
23
26
  from typing import Type
24
27
  from typing import TYPE_CHECKING
25
28
 
26
29
  from ..engine import AdaptedConnection
30
+ from ..exc import EmulatedDBAPIException
27
31
  from ..util import EMPTY_DICT
28
- from ..util.concurrency import await_fallback
29
- from ..util.concurrency import await_only
32
+ from ..util.concurrency import await_
30
33
  from ..util.concurrency import in_greenlet
31
- from ..util.typing import Protocol
32
34
 
33
35
  if TYPE_CHECKING:
34
36
  from ..engine.interfaces import _DBAPICursorDescription
@@ -124,13 +126,38 @@ class AsyncAdapt_dbapi_module:
124
126
 
125
127
  def __getattr__(self, key: str) -> Any: ...
126
128
 
129
+ def __init__(
130
+ self,
131
+ driver: types.ModuleType,
132
+ *,
133
+ dbapi_module: types.ModuleType | None = None,
134
+ ):
135
+ self.driver = driver
136
+ self.dbapi_module = dbapi_module
137
+
138
+ @property
139
+ def exceptions_module(self) -> types.ModuleType:
140
+ """Return the module which we think will have the exception hierarchy.
141
+
142
+ For an asyncio driver that wraps a plain DBAPI like aiomysql,
143
+ aioodbc, aiosqlite, etc. these exceptions will be from the
144
+ dbapi_module. For a "pure" driver like asyncpg these will come
145
+ from the driver module.
146
+
147
+ .. versionadded:: 2.1
148
+
149
+ """
150
+ if self.dbapi_module is not None:
151
+ return self.dbapi_module
152
+ else:
153
+ return self.driver
154
+
127
155
 
128
156
  class AsyncAdapt_dbapi_cursor:
129
157
  server_side = False
130
158
  __slots__ = (
131
159
  "_adapt_connection",
132
160
  "_connection",
133
- "await_",
134
161
  "_cursor",
135
162
  "_rows",
136
163
  "_soft_closed_memoized",
@@ -147,8 +174,6 @@ class AsyncAdapt_dbapi_cursor:
147
174
  self._adapt_connection = adapt_connection
148
175
  self._connection = adapt_connection._connection
149
176
 
150
- self.await_ = adapt_connection.await_
151
-
152
177
  cursor = self._make_new_cursor(self._connection)
153
178
  self._cursor = self._aenter_cursor(cursor)
154
179
  self._soft_closed_memoized = EMPTY_DICT
@@ -156,7 +181,10 @@ class AsyncAdapt_dbapi_cursor:
156
181
  self._rows = collections.deque()
157
182
 
158
183
  def _aenter_cursor(self, cursor: AsyncIODBAPICursor) -> AsyncIODBAPICursor:
159
- return self.await_(cursor.__aenter__()) # type: ignore[no-any-return]
184
+ try:
185
+ return await_(cursor.__aenter__()) # type: ignore[no-any-return]
186
+ except Exception as error:
187
+ self._adapt_connection._handle_exception(error)
160
188
 
161
189
  def _make_new_cursor(
162
190
  self, connection: AsyncIODBAPIConnection
@@ -217,7 +245,7 @@ class AsyncAdapt_dbapi_cursor:
217
245
  if not self._awaitable_cursor_close:
218
246
  self._cursor.close() # type: ignore[unused-coroutine]
219
247
  elif in_greenlet():
220
- self.await_(self._cursor.close())
248
+ await_(self._cursor.close())
221
249
 
222
250
  def execute(
223
251
  self,
@@ -225,7 +253,7 @@ class AsyncAdapt_dbapi_cursor:
225
253
  parameters: Optional[_DBAPISingleExecuteParams] = None,
226
254
  ) -> Any:
227
255
  try:
228
- return self.await_(self._execute_async(operation, parameters))
256
+ return await_(self._execute_async(operation, parameters))
229
257
  except Exception as error:
230
258
  self._adapt_connection._handle_exception(error)
231
259
 
@@ -235,7 +263,7 @@ class AsyncAdapt_dbapi_cursor:
235
263
  seq_of_parameters: _DBAPIMultiExecuteParams,
236
264
  ) -> Any:
237
265
  try:
238
- return self.await_(
266
+ return await_(
239
267
  self._executemany_async(operation, seq_of_parameters)
240
268
  )
241
269
  except Exception as error:
@@ -263,18 +291,16 @@ class AsyncAdapt_dbapi_cursor:
263
291
  return await self._cursor.executemany(operation, seq_of_parameters)
264
292
 
265
293
  def nextset(self) -> None:
266
- self.await_(self._cursor.nextset())
294
+ await_(self._cursor.nextset())
267
295
  if self._cursor.description and not self.server_side:
268
- self._rows = collections.deque(
269
- self.await_(self._cursor.fetchall())
270
- )
296
+ self._rows = collections.deque(await_(self._cursor.fetchall()))
271
297
 
272
298
  def setinputsizes(self, *inputsizes: Any) -> None:
273
299
  # NOTE: this is overridden in aioodbc due to
274
300
  # see https://github.com/aio-libs/aioodbc/issues/451
275
301
  # right now
276
302
 
277
- return self.await_(self._cursor.setinputsizes(*inputsizes))
303
+ return await_(self._cursor.setinputsizes(*inputsizes))
278
304
 
279
305
  def __enter__(self) -> Self:
280
306
  return self
@@ -310,23 +336,23 @@ class AsyncAdapt_dbapi_ss_cursor(AsyncAdapt_dbapi_cursor):
310
336
 
311
337
  def close(self) -> None:
312
338
  if self._cursor is not None:
313
- self.await_(self._cursor.close())
314
- self._cursor = None # type: ignore
339
+ await_(self._cursor.close())
340
+ self._cursor = None # type: ignore[assignment]
315
341
 
316
342
  def fetchone(self) -> Optional[Any]:
317
- return self.await_(self._cursor.fetchone())
343
+ return await_(self._cursor.fetchone())
318
344
 
319
345
  def fetchmany(self, size: Optional[int] = None) -> Any:
320
- return self.await_(self._cursor.fetchmany(size=size))
346
+ return await_(self._cursor.fetchmany(size=size))
321
347
 
322
348
  def fetchall(self) -> Sequence[Any]:
323
- return self.await_(self._cursor.fetchall())
349
+ return await_(self._cursor.fetchall())
324
350
 
325
351
  def __iter__(self) -> Iterator[Any]:
326
352
  iterator = self._cursor.__aiter__()
327
353
  while True:
328
354
  try:
329
- yield self.await_(iterator.__anext__())
355
+ yield await_(iterator.__anext__())
330
356
  except StopAsyncIteration:
331
357
  break
332
358
 
@@ -335,12 +361,24 @@ class AsyncAdapt_dbapi_connection(AdaptedConnection):
335
361
  _cursor_cls = AsyncAdapt_dbapi_cursor
336
362
  _ss_cursor_cls = AsyncAdapt_dbapi_ss_cursor
337
363
 
338
- await_ = staticmethod(await_only)
339
-
340
364
  __slots__ = ("dbapi", "_execute_mutex")
341
365
 
342
366
  _connection: AsyncIODBAPIConnection
343
367
 
368
+ @classmethod
369
+ async def create(
370
+ cls,
371
+ dbapi: Any,
372
+ connection_awaitable: Awaitable[AsyncIODBAPIConnection],
373
+ **kw: Any,
374
+ ) -> Self:
375
+ try:
376
+ connection = await connection_awaitable
377
+ except Exception as error:
378
+ cls._handle_exception_no_connection(dbapi, error)
379
+ else:
380
+ return cls(dbapi, connection, **kw)
381
+
344
382
  def __init__(self, dbapi: Any, connection: AsyncIODBAPIConnection):
345
383
  self.dbapi = dbapi
346
384
  self._connection = connection
@@ -362,31 +400,31 @@ class AsyncAdapt_dbapi_connection(AdaptedConnection):
362
400
  cursor.execute(operation, parameters)
363
401
  return cursor
364
402
 
365
- def _handle_exception(self, error: Exception) -> NoReturn:
403
+ @classmethod
404
+ def _handle_exception_no_connection(
405
+ cls, dbapi: Any, error: Exception
406
+ ) -> NoReturn:
366
407
  exc_info = sys.exc_info()
367
408
 
368
409
  raise error.with_traceback(exc_info[2])
369
410
 
411
+ def _handle_exception(self, error: Exception) -> NoReturn:
412
+ self._handle_exception_no_connection(self.dbapi, error)
413
+
370
414
  def rollback(self) -> None:
371
415
  try:
372
- self.await_(self._connection.rollback())
416
+ await_(self._connection.rollback())
373
417
  except Exception as error:
374
418
  self._handle_exception(error)
375
419
 
376
420
  def commit(self) -> None:
377
421
  try:
378
- self.await_(self._connection.commit())
422
+ await_(self._connection.commit())
379
423
  except Exception as error:
380
424
  self._handle_exception(error)
381
425
 
382
426
  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)
427
+ await_(self._connection.close())
390
428
 
391
429
 
392
430
  class AsyncAdapt_terminate:
@@ -399,7 +437,7 @@ class AsyncAdapt_terminate:
399
437
  # in a greenlet; this is the connection was invalidated case.
400
438
  try:
401
439
  # try to gracefully close; see #10717
402
- self.await_(asyncio.shield(self._terminate_graceful_close())) # type: ignore[attr-defined] # noqa: E501
440
+ await_(asyncio.shield(self._terminate_graceful_close()))
403
441
  except self._terminate_handled_exceptions() as e:
404
442
  # in the case where we are recycling an old connection
405
443
  # that may have already been disconnected, close() will
@@ -427,3 +465,12 @@ class AsyncAdapt_terminate:
427
465
  def _terminate_force_close(self) -> None:
428
466
  """Terminate the connection"""
429
467
  raise NotImplementedError
468
+
469
+
470
+ class AsyncAdapt_Error(EmulatedDBAPIException):
471
+ """Provide for the base of DBAPI ``Error`` base class for dialects
472
+ that need to emulate the DBAPI exception hierarchy.
473
+
474
+ .. versionadded:: 2.1
475
+
476
+ """
@@ -15,7 +15,6 @@ from typing import List
15
15
  from typing import Optional
16
16
  from typing import Tuple
17
17
  from typing import Union
18
- from urllib.parse import unquote_plus
19
18
 
20
19
  from . import Connector
21
20
  from .. import ExecutionContext
@@ -73,7 +72,8 @@ class PyODBCConnector(Connector):
73
72
  connect_args[param] = util.asbool(keys.pop(param))
74
73
 
75
74
  if "odbc_connect" in keys:
76
- connectors = [unquote_plus(keys.pop("odbc_connect"))]
75
+ # (potential breaking change for issue #11250)
76
+ connectors = [keys.pop("odbc_connect")]
77
77
  else:
78
78
 
79
79
  def check_quote(token: str) -> str:
@@ -164,25 +164,14 @@ class PyODBCConnector(Connector):
164
164
  else:
165
165
  return False
166
166
 
167
- def _dbapi_version(self) -> interfaces.VersionInfoType:
168
- if not self.dbapi:
169
- return ()
170
- return self._parse_dbapi_version(self.dbapi.version)
171
-
172
- def _parse_dbapi_version(self, vers: str) -> interfaces.VersionInfoType:
173
- m = re.match(r"(?:py.*-)?([\d\.]+)(?:-(\w+))?", vers)
174
- if not m:
175
- return ()
176
- vers_tuple: interfaces.VersionInfoType = tuple(
177
- [int(x) for x in m.group(1).split(".")]
178
- )
179
- if m.group(2):
180
- vers_tuple += (m.group(2),)
181
- return vers_tuple
167
+ def retrieve_dbapi_version(
168
+ self, dbapi: interfaces.DBAPIModule
169
+ ) -> util.VersionInfo:
170
+ return util.parse_version_string(dbapi.version)
182
171
 
183
172
  def _get_server_version_info(
184
173
  self, connection: Connection
185
- ) -> interfaces.VersionInfoType:
174
+ ) -> interfaces.ServerVersionInfoType:
186
175
  # NOTE: this function is not reliable, particularly when
187
176
  # freetds is in use. Implement database-specific server version
188
177
  # queries.
@@ -43,7 +43,7 @@ def _auto_fn(name: str) -> Optional[Callable[[], Type[Dialect]]]:
43
43
  module: Any = __import__(
44
44
  "sqlalchemy.dialects.mysql.mariadb"
45
45
  ).dialects.mysql.mariadb
46
- return module.loader(driver) # type: ignore
46
+ return module.loader(driver) # type: ignore[no-any-return]
47
47
  else:
48
48
  module = __import__("sqlalchemy.dialects.%s" % (dialect,)).dialects
49
49
  module = getattr(module, dialect)
@@ -8,6 +8,7 @@
8
8
 
9
9
  from . import aioodbc # noqa
10
10
  from . import base # noqa
11
+ from . import mssqlpython # noqa
11
12
  from . import pymssql # noqa
12
13
  from . import pyodbc # noqa
13
14
  from .base import BIGINT
@@ -42,12 +42,12 @@ styles are otherwise equivalent to those documented in the pyodbc section::
42
42
 
43
43
  from __future__ import annotations
44
44
 
45
+ from .base import MSExecutionContext
45
46
  from .pyodbc import MSDialect_pyodbc
46
- from .pyodbc import MSExecutionContext_pyodbc
47
47
  from ...connectors.aioodbc import aiodbcConnector
48
48
 
49
49
 
50
- class MSExecutionContext_aioodbc(MSExecutionContext_pyodbc):
50
+ class MSExecutionContext_aioodbc(MSExecutionContext):
51
51
  def create_server_side_cursor(self):
52
52
  return self._dbapi_connection.cursor(server_side=True)
53
53