SQLAlchemy 2.0.47__cp313-cp313t-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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-win_amd64.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win_amd64.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,482 @@
1
+ # testing/fixtures/sql.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: ignore-errors
8
+ from __future__ import annotations
9
+
10
+ import itertools
11
+ import random
12
+ import re
13
+
14
+ import sqlalchemy as sa
15
+ from .base import TestBase
16
+ from .. import config
17
+ from .. import mock
18
+ from .. import provision
19
+ from ..assertions import eq_
20
+ from ..assertions import ne_
21
+ from ..util import adict
22
+ from ..util import drop_all_tables_from_metadata
23
+ from ... import event
24
+ from ... import util
25
+ from ...schema import sort_tables_and_constraints
26
+ from ...sql import visitors
27
+ from ...sql.elements import ClauseElement
28
+
29
+
30
+ class TablesTest(TestBase):
31
+ # 'once', None
32
+ run_setup_bind = "once"
33
+
34
+ # 'once', 'each', None
35
+ run_define_tables = "once"
36
+
37
+ # 'once', 'each', None
38
+ run_create_tables = "once"
39
+
40
+ # 'once', 'each', None
41
+ run_inserts = "each"
42
+
43
+ # 'each', None
44
+ run_deletes = "each"
45
+
46
+ # 'once', None
47
+ run_dispose_bind = None
48
+
49
+ bind = None
50
+ _tables_metadata = None
51
+ tables = None
52
+ other = None
53
+ sequences = None
54
+
55
+ @config.fixture(autouse=True, scope="class")
56
+ def _setup_tables_test_class(self):
57
+ cls = self.__class__
58
+ cls._init_class()
59
+
60
+ cls._setup_once_tables()
61
+
62
+ cls._setup_once_inserts()
63
+
64
+ yield
65
+
66
+ cls._teardown_once_metadata_bind()
67
+
68
+ @config.fixture(autouse=True, scope="function")
69
+ def _setup_tables_test_instance(self):
70
+ self._setup_each_tables()
71
+ self._setup_each_inserts()
72
+
73
+ yield
74
+
75
+ self._teardown_each_tables()
76
+
77
+ @property
78
+ def tables_test_metadata(self):
79
+ return self._tables_metadata
80
+
81
+ @classmethod
82
+ def _init_class(cls):
83
+ if cls.run_define_tables == "each":
84
+ if cls.run_create_tables == "once":
85
+ cls.run_create_tables = "each"
86
+ assert cls.run_inserts in ("each", None)
87
+
88
+ cls.other = adict()
89
+ cls.tables = adict()
90
+ cls.sequences = adict()
91
+
92
+ cls.bind = cls.setup_bind()
93
+ cls._tables_metadata = sa.MetaData()
94
+
95
+ @classmethod
96
+ def _setup_once_inserts(cls):
97
+ if cls.run_inserts == "once":
98
+ cls._load_fixtures()
99
+ with cls.bind.begin() as conn:
100
+ cls.insert_data(conn)
101
+
102
+ @classmethod
103
+ def _setup_once_tables(cls):
104
+ if cls.run_define_tables == "once":
105
+ cls.define_tables(cls._tables_metadata)
106
+ if cls.run_create_tables == "once":
107
+ cls._tables_metadata.create_all(cls.bind)
108
+ cls.tables.update(cls._tables_metadata.tables)
109
+ cls.sequences.update(cls._tables_metadata._sequences)
110
+
111
+ def _setup_each_tables(self):
112
+ if self.run_define_tables == "each":
113
+ self.define_tables(self._tables_metadata)
114
+ if self.run_create_tables == "each":
115
+ self._tables_metadata.create_all(self.bind)
116
+ self.tables.update(self._tables_metadata.tables)
117
+ self.sequences.update(self._tables_metadata._sequences)
118
+ elif self.run_create_tables == "each":
119
+ self._tables_metadata.create_all(self.bind)
120
+
121
+ def _setup_each_inserts(self):
122
+ if self.run_inserts == "each":
123
+ self._load_fixtures()
124
+ with self.bind.begin() as conn:
125
+ self.insert_data(conn)
126
+
127
+ def _teardown_each_tables(self):
128
+ if self.run_define_tables == "each":
129
+ self.tables.clear()
130
+ if self.run_create_tables == "each":
131
+ drop_all_tables_from_metadata(self._tables_metadata, self.bind)
132
+ self._tables_metadata.clear()
133
+ elif self.run_create_tables == "each":
134
+ drop_all_tables_from_metadata(self._tables_metadata, self.bind)
135
+
136
+ # no need to run deletes if tables are recreated on setup
137
+ if (
138
+ self.run_define_tables != "each"
139
+ and self.run_create_tables == "once"
140
+ and self.run_deletes == "each"
141
+ ):
142
+ with self.bind.begin() as conn:
143
+ provision.delete_from_all_tables(
144
+ conn, config, self._tables_metadata
145
+ )
146
+
147
+ @classmethod
148
+ def _teardown_once_metadata_bind(cls):
149
+ if cls.run_create_tables:
150
+ drop_all_tables_from_metadata(cls._tables_metadata, cls.bind)
151
+
152
+ if cls.run_dispose_bind == "once":
153
+ cls.dispose_bind(cls.bind)
154
+
155
+ cls._tables_metadata.bind = None
156
+
157
+ if cls.run_setup_bind is not None:
158
+ cls.bind = None
159
+
160
+ @classmethod
161
+ def setup_bind(cls):
162
+ return config.db
163
+
164
+ @classmethod
165
+ def dispose_bind(cls, bind):
166
+ if hasattr(bind, "dispose"):
167
+ bind.dispose()
168
+ elif hasattr(bind, "close"):
169
+ bind.close()
170
+
171
+ @classmethod
172
+ def define_tables(cls, metadata):
173
+ pass
174
+
175
+ @classmethod
176
+ def fixtures(cls):
177
+ return {}
178
+
179
+ @classmethod
180
+ def insert_data(cls, connection):
181
+ pass
182
+
183
+ def sql_count_(self, count, fn):
184
+ self.assert_sql_count(self.bind, fn, count)
185
+
186
+ def sql_eq_(self, callable_, statements):
187
+ self.assert_sql(self.bind, callable_, statements)
188
+
189
+ @classmethod
190
+ def _load_fixtures(cls):
191
+ """Insert rows as represented by the fixtures() method."""
192
+ headers, rows = {}, {}
193
+ for table, data in cls.fixtures().items():
194
+ if len(data) < 2:
195
+ continue
196
+ if isinstance(table, str):
197
+ table = cls.tables[table]
198
+ headers[table] = data[0]
199
+ rows[table] = data[1:]
200
+ for table, fks in sort_tables_and_constraints(
201
+ cls._tables_metadata.tables.values()
202
+ ):
203
+ if table is None:
204
+ continue
205
+ if table not in headers:
206
+ continue
207
+ with cls.bind.begin() as conn:
208
+ conn.execute(
209
+ table.insert(),
210
+ [
211
+ dict(zip(headers[table], column_values))
212
+ for column_values in rows[table]
213
+ ],
214
+ )
215
+
216
+
217
+ class NoCache:
218
+ @config.fixture(autouse=True, scope="function")
219
+ def _disable_cache(self):
220
+ _cache = config.db._compiled_cache
221
+ config.db._compiled_cache = None
222
+ yield
223
+ config.db._compiled_cache = _cache
224
+
225
+
226
+ class RemovesEvents:
227
+ @util.memoized_property
228
+ def _event_fns(self):
229
+ return set()
230
+
231
+ def event_listen(self, target, name, fn, **kw):
232
+ self._event_fns.add((target, name, fn))
233
+ event.listen(target, name, fn, **kw)
234
+
235
+ @config.fixture(autouse=True, scope="function")
236
+ def _remove_events(self):
237
+ yield
238
+ for key in self._event_fns:
239
+ event.remove(*key)
240
+
241
+
242
+ class ComputedReflectionFixtureTest(TablesTest):
243
+ run_inserts = run_deletes = None
244
+
245
+ __backend__ = True
246
+ __requires__ = ("computed_columns", "table_reflection")
247
+
248
+ regexp = re.compile(r"[\[\]\(\)\s`'\"]*")
249
+
250
+ def normalize(self, text):
251
+ return self.regexp.sub("", text).lower()
252
+
253
+ @classmethod
254
+ def define_tables(cls, metadata):
255
+ from ... import Integer
256
+ from ... import testing
257
+ from ...schema import Column
258
+ from ...schema import Computed
259
+ from ...schema import Table
260
+
261
+ Table(
262
+ "computed_default_table",
263
+ metadata,
264
+ Column("id", Integer, primary_key=True),
265
+ Column("normal", Integer),
266
+ Column("computed_col", Integer, Computed("normal + 42")),
267
+ Column("with_default", Integer, server_default="42"),
268
+ )
269
+
270
+ t = Table(
271
+ "computed_column_table",
272
+ metadata,
273
+ Column("id", Integer, primary_key=True),
274
+ Column("normal", Integer),
275
+ Column("computed_no_flag", Integer, Computed("normal + 42")),
276
+ )
277
+
278
+ if testing.requires.schemas.enabled:
279
+ t2 = Table(
280
+ "computed_column_table",
281
+ metadata,
282
+ Column("id", Integer, primary_key=True),
283
+ Column("normal", Integer),
284
+ Column("computed_no_flag", Integer, Computed("normal / 42")),
285
+ schema=config.test_schema,
286
+ )
287
+
288
+ if testing.requires.computed_columns_virtual.enabled:
289
+ t.append_column(
290
+ Column(
291
+ "computed_virtual",
292
+ Integer,
293
+ Computed("normal + 2", persisted=False),
294
+ )
295
+ )
296
+ if testing.requires.schemas.enabled:
297
+ t2.append_column(
298
+ Column(
299
+ "computed_virtual",
300
+ Integer,
301
+ Computed("normal / 2", persisted=False),
302
+ )
303
+ )
304
+ if testing.requires.computed_columns_stored.enabled:
305
+ t.append_column(
306
+ Column(
307
+ "computed_stored",
308
+ Integer,
309
+ Computed("normal - 42", persisted=True),
310
+ )
311
+ )
312
+ if testing.requires.schemas.enabled:
313
+ t2.append_column(
314
+ Column(
315
+ "computed_stored",
316
+ Integer,
317
+ Computed("normal * 42", persisted=True),
318
+ )
319
+ )
320
+
321
+
322
+ class CacheKeyFixture:
323
+ def _compare_equal(self, a, b, compare_values):
324
+ a_key = a._generate_cache_key()
325
+ b_key = b._generate_cache_key()
326
+
327
+ if a_key is None:
328
+ assert a._annotations.get("nocache")
329
+
330
+ assert b_key is None
331
+ else:
332
+ eq_(a_key.key, b_key.key)
333
+ eq_(hash(a_key.key), hash(b_key.key))
334
+
335
+ for a_param, b_param in zip(a_key.bindparams, b_key.bindparams):
336
+ assert a_param.compare(b_param, compare_values=compare_values)
337
+ return a_key, b_key
338
+
339
+ def _run_cache_key_fixture(self, fixture, compare_values):
340
+ case_a = fixture()
341
+ case_b = fixture()
342
+
343
+ for a, b in itertools.combinations_with_replacement(
344
+ range(len(case_a)), 2
345
+ ):
346
+ if a == b:
347
+ a_key, b_key = self._compare_equal(
348
+ case_a[a], case_b[b], compare_values
349
+ )
350
+ if a_key is None:
351
+ continue
352
+ else:
353
+ a_key = case_a[a]._generate_cache_key()
354
+ b_key = case_b[b]._generate_cache_key()
355
+
356
+ if a_key is None or b_key is None:
357
+ if a_key is None:
358
+ assert case_a[a]._annotations.get("nocache")
359
+ if b_key is None:
360
+ assert case_b[b]._annotations.get("nocache")
361
+ continue
362
+
363
+ if a_key.key == b_key.key:
364
+ for a_param, b_param in zip(
365
+ a_key.bindparams, b_key.bindparams
366
+ ):
367
+ if not a_param.compare(
368
+ b_param, compare_values=compare_values
369
+ ):
370
+ break
371
+ else:
372
+ # this fails unconditionally since we could not
373
+ # find bound parameter values that differed.
374
+ # Usually we intended to get two distinct keys here
375
+ # so the failure will be more descriptive using the
376
+ # ne_() assertion.
377
+ ne_(a_key.key, b_key.key)
378
+ else:
379
+ ne_(a_key.key, b_key.key)
380
+
381
+ # ClauseElement-specific test to ensure the cache key
382
+ # collected all the bound parameters that aren't marked
383
+ # as "literal execute"
384
+ if isinstance(case_a[a], ClauseElement) and isinstance(
385
+ case_b[b], ClauseElement
386
+ ):
387
+ assert_a_params = []
388
+ assert_b_params = []
389
+
390
+ for elem in visitors.iterate(case_a[a]):
391
+ if elem.__visit_name__ == "bindparam":
392
+ assert_a_params.append(elem)
393
+
394
+ for elem in visitors.iterate(case_b[b]):
395
+ if elem.__visit_name__ == "bindparam":
396
+ assert_b_params.append(elem)
397
+
398
+ # note we're asserting the order of the params as well as
399
+ # if there are dupes or not. ordering has to be
400
+ # deterministic and matches what a traversal would provide.
401
+ eq_(
402
+ sorted(a_key.bindparams, key=lambda b: b.key),
403
+ sorted(
404
+ util.unique_list(assert_a_params), key=lambda b: b.key
405
+ ),
406
+ )
407
+ eq_(
408
+ sorted(b_key.bindparams, key=lambda b: b.key),
409
+ sorted(
410
+ util.unique_list(assert_b_params), key=lambda b: b.key
411
+ ),
412
+ )
413
+
414
+ def _run_cache_key_equal_fixture(self, fixture, compare_values):
415
+ case_a = fixture()
416
+ case_b = fixture()
417
+
418
+ for a, b in itertools.combinations_with_replacement(
419
+ range(len(case_a)), 2
420
+ ):
421
+ self._compare_equal(case_a[a], case_b[b], compare_values)
422
+
423
+
424
+ def insertmanyvalues_fixture(
425
+ connection, randomize_rows=False, warn_on_downgraded=False
426
+ ):
427
+ dialect = connection.dialect
428
+ orig_dialect = dialect._deliver_insertmanyvalues_batches
429
+ orig_conn = connection._exec_insertmany_context
430
+
431
+ class RandomCursor:
432
+ __slots__ = ("cursor",)
433
+
434
+ def __init__(self, cursor):
435
+ self.cursor = cursor
436
+
437
+ # only this method is called by the deliver method.
438
+ # by not having the other methods we assert that those aren't being
439
+ # used
440
+
441
+ @property
442
+ def description(self):
443
+ return self.cursor.description
444
+
445
+ def fetchall(self):
446
+ rows = self.cursor.fetchall()
447
+ rows = list(rows)
448
+ random.shuffle(rows)
449
+ return rows
450
+
451
+ def _deliver_insertmanyvalues_batches(
452
+ connection,
453
+ cursor,
454
+ statement,
455
+ parameters,
456
+ generic_setinputsizes,
457
+ context,
458
+ ):
459
+ if randomize_rows:
460
+ cursor = RandomCursor(cursor)
461
+ for batch in orig_dialect(
462
+ connection,
463
+ cursor,
464
+ statement,
465
+ parameters,
466
+ generic_setinputsizes,
467
+ context,
468
+ ):
469
+ if warn_on_downgraded and batch.is_downgraded:
470
+ util.warn("Batches were downgraded for sorted INSERT")
471
+
472
+ yield batch
473
+
474
+ def _exec_insertmany_context(dialect, context):
475
+ with mock.patch.object(
476
+ dialect,
477
+ "_deliver_insertmanyvalues_batches",
478
+ new=_deliver_insertmanyvalues_batches,
479
+ ):
480
+ return orig_conn(dialect, context)
481
+
482
+ connection._exec_insertmany_context = _exec_insertmany_context
@@ -0,0 +1,155 @@
1
+ # testing/pickleable.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: ignore-errors
8
+
9
+
10
+ """Classes used in pickling tests, need to be at the module level for
11
+ unpickling.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from .entities import ComparableEntity
17
+ from ..schema import Column
18
+ from ..types import String
19
+
20
+
21
+ class User(ComparableEntity):
22
+ pass
23
+
24
+
25
+ class Order(ComparableEntity):
26
+ pass
27
+
28
+
29
+ class Dingaling(ComparableEntity):
30
+ pass
31
+
32
+
33
+ class EmailUser(User):
34
+ pass
35
+
36
+
37
+ class Address(ComparableEntity):
38
+ pass
39
+
40
+
41
+ # TODO: these are kind of arbitrary....
42
+ class Child1(ComparableEntity):
43
+ pass
44
+
45
+
46
+ class Child2(ComparableEntity):
47
+ pass
48
+
49
+
50
+ class Parent(ComparableEntity):
51
+ pass
52
+
53
+
54
+ class Screen:
55
+ def __init__(self, obj, parent=None):
56
+ self.obj = obj
57
+ self.parent = parent
58
+
59
+
60
+ class Mixin:
61
+ email_address = Column(String)
62
+
63
+
64
+ class AddressWMixin(Mixin, ComparableEntity):
65
+ pass
66
+
67
+
68
+ class Foo:
69
+ def __init__(self, moredata, stuff="im stuff"):
70
+ self.data = "im data"
71
+ self.stuff = stuff
72
+ self.moredata = moredata
73
+
74
+ __hash__ = object.__hash__
75
+
76
+ def __eq__(self, other):
77
+ return (
78
+ other.data == self.data
79
+ and other.stuff == self.stuff
80
+ and other.moredata == self.moredata
81
+ )
82
+
83
+
84
+ class Bar:
85
+ def __init__(self, x, y):
86
+ self.x = x
87
+ self.y = y
88
+
89
+ __hash__ = object.__hash__
90
+
91
+ def __eq__(self, other):
92
+ return (
93
+ other.__class__ is self.__class__
94
+ and other.x == self.x
95
+ and other.y == self.y
96
+ )
97
+
98
+ def __str__(self):
99
+ return "Bar(%d, %d)" % (self.x, self.y)
100
+
101
+
102
+ class OldSchool:
103
+ def __init__(self, x, y):
104
+ self.x = x
105
+ self.y = y
106
+
107
+ def __eq__(self, other):
108
+ return (
109
+ other.__class__ is self.__class__
110
+ and other.x == self.x
111
+ and other.y == self.y
112
+ )
113
+
114
+
115
+ class OldSchoolWithoutCompare:
116
+ def __init__(self, x, y):
117
+ self.x = x
118
+ self.y = y
119
+
120
+
121
+ class BarWithoutCompare:
122
+ def __init__(self, x, y):
123
+ self.x = x
124
+ self.y = y
125
+
126
+ def __str__(self):
127
+ return "Bar(%d, %d)" % (self.x, self.y)
128
+
129
+
130
+ class NotComparable:
131
+ def __init__(self, data):
132
+ self.data = data
133
+
134
+ def __hash__(self):
135
+ return id(self)
136
+
137
+ def __eq__(self, other):
138
+ return NotImplemented
139
+
140
+ def __ne__(self, other):
141
+ return NotImplemented
142
+
143
+
144
+ class BrokenComparable:
145
+ def __init__(self, data):
146
+ self.data = data
147
+
148
+ def __hash__(self):
149
+ return id(self)
150
+
151
+ def __eq__(self, other):
152
+ raise NotImplementedError
153
+
154
+ def __ne__(self, other):
155
+ raise NotImplementedError
@@ -0,0 +1,6 @@
1
+ # testing/plugin/__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
@@ -0,0 +1,51 @@
1
+ # testing/plugin/bootstrap.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: ignore-errors
8
+
9
+ """
10
+ Bootstrapper for test framework plugins.
11
+
12
+ The entire rationale for this system is to get the modules in plugin/
13
+ imported without importing all of the supporting library, so that we can
14
+ set up things for testing before coverage starts.
15
+
16
+ The rationale for all of plugin/ being *in* the supporting library in the
17
+ first place is so that the testing and plugin suite is available to other
18
+ libraries, mainly external SQLAlchemy and Alembic dialects, to make use
19
+ of the same test environment and standard suites available to
20
+ SQLAlchemy/Alembic themselves without the need to ship/install a separate
21
+ package outside of SQLAlchemy.
22
+
23
+
24
+ """
25
+
26
+ import importlib.util
27
+ import os
28
+ import sys
29
+
30
+
31
+ bootstrap_file = locals()["bootstrap_file"]
32
+ to_bootstrap = locals()["to_bootstrap"]
33
+
34
+
35
+ def load_file_as_module(name):
36
+ path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name)
37
+
38
+ spec = importlib.util.spec_from_file_location(name, path)
39
+ assert spec is not None
40
+ assert spec.loader is not None
41
+ mod = importlib.util.module_from_spec(spec)
42
+ spec.loader.exec_module(mod)
43
+ return mod
44
+
45
+
46
+ if to_bootstrap == "pytest":
47
+ sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base")
48
+ sys.modules["sqla_plugin_base"].bootstrapped_as_sqlalchemy = True
49
+ sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin")
50
+ else:
51
+ raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa