SQLAlchemy 2.1.0rc1__cp315-cp315-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 (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win_amd64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_amd64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win_amd64.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win_amd64.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win_amd64.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win_amd64.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,575 @@
1
+ # testing/util.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
+ from __future__ import annotations
11
+
12
+ from collections import deque
13
+ from collections import namedtuple
14
+ import contextlib
15
+ import decimal
16
+ import gc
17
+ from itertools import chain
18
+ import os
19
+ import pickle
20
+ import random
21
+ import subprocess
22
+ import sys
23
+ from sys import getsizeof
24
+ from tempfile import mkstemp
25
+ import time
26
+ import types
27
+ from typing import Any
28
+
29
+ from . import config
30
+ from . import mock
31
+ from .. import inspect
32
+ from ..engine import Connection
33
+ from ..schema import Column
34
+ from ..schema import DropConstraint
35
+ from ..schema import DropTable
36
+ from ..schema import ForeignKeyConstraint
37
+ from ..schema import MetaData
38
+ from ..schema import Table
39
+ from ..sql import schema
40
+ from ..sql.sqltypes import Integer
41
+ from ..util import decorator
42
+ from ..util import defaultdict
43
+ from ..util import has_refcount_gc
44
+ from ..util import inspect_getfullargspec
45
+
46
+ if not has_refcount_gc:
47
+
48
+ def non_refcount_gc_collect(*args):
49
+ gc.collect()
50
+ gc.collect()
51
+
52
+ gc_collect = lazy_gc = non_refcount_gc_collect
53
+ else:
54
+ # assume CPython - straight gc.collect, lazy_gc() is a pass
55
+ gc_collect = gc.collect
56
+
57
+ def lazy_gc():
58
+ pass
59
+
60
+
61
+ def picklers():
62
+ nt = namedtuple("picklers", ["loads", "dumps"])
63
+
64
+ for protocol in range(-2, pickle.HIGHEST_PROTOCOL + 1):
65
+ yield nt(pickle.loads, lambda d: pickle.dumps(d, protocol))
66
+
67
+
68
+ def unpickle_in_subprocess(obj, code):
69
+ """pickle ``obj`` to a file, then unpickle it in a new interpreter.
70
+
71
+ ``code`` is Python source run by that interpreter, which receives the
72
+ name of the pickle file as ``sys.argv[1]``. The new interpreter has
73
+ the current ``sys.path``, so that the SQLAlchemy under test, as well
74
+ as the ``test`` package, are importable.
75
+
76
+ Returns the stripped stdout of the subprocess; a non-zero exit status
77
+ fails the test, reporting its stderr.
78
+
79
+ """
80
+
81
+ fd, filename = mkstemp("pkl")
82
+ try:
83
+ with os.fdopen(fd, "wb") as file_:
84
+ pickle.dump(obj, file_)
85
+
86
+ parts = list(sys.path)
87
+ if os.environ.get("PYTHONPATH"):
88
+ parts.append(os.environ["PYTHONPATH"])
89
+
90
+ proc = subprocess.run(
91
+ [sys.executable, "-c", code, filename.replace(os.sep, "/")],
92
+ stdout=subprocess.PIPE,
93
+ stderr=subprocess.PIPE,
94
+ env={**os.environ, "PYTHONPATH": os.pathsep.join(parts)},
95
+ )
96
+ finally:
97
+ os.unlink(filename)
98
+
99
+ if proc.returncode != 0:
100
+ raise AssertionError(
101
+ "subprocess failed: %s" % proc.stderr.decode(errors="replace")
102
+ )
103
+ return proc.stdout.strip()
104
+
105
+
106
+ def random_choices(population, k=1):
107
+ return random.choices(population, k=k)
108
+
109
+
110
+ def round_decimal(value, prec):
111
+ if isinstance(value, float):
112
+ return round(value, prec)
113
+
114
+ # can also use shift() here but that is 2.6 only
115
+ return (value * decimal.Decimal("1" + "0" * prec)).to_integral(
116
+ decimal.ROUND_FLOOR
117
+ ) / pow(10, prec)
118
+
119
+
120
+ class RandomSet(set):
121
+ def __iter__(self):
122
+ l = list(set.__iter__(self))
123
+ random.shuffle(l)
124
+ return iter(l)
125
+
126
+ def pop(self):
127
+ index = random.randint(0, len(self) - 1)
128
+ item = list(set.__iter__(self))[index]
129
+ self.remove(item)
130
+ return item
131
+
132
+ def union(self, other):
133
+ return RandomSet(set.union(self, other))
134
+
135
+ def difference(self, other):
136
+ return RandomSet(set.difference(self, other))
137
+
138
+ def intersection(self, other):
139
+ return RandomSet(set.intersection(self, other))
140
+
141
+ def copy(self):
142
+ return RandomSet(self)
143
+
144
+
145
+ def conforms_partial_ordering(tuples, sorted_elements):
146
+ """True if the given sorting conforms to the given partial ordering."""
147
+
148
+ deps = defaultdict(set)
149
+ for parent, child in tuples:
150
+ deps[parent].add(child)
151
+ for i, node in enumerate(sorted_elements):
152
+ for n in sorted_elements[i:]:
153
+ if node in deps[n]:
154
+ return False
155
+ else:
156
+ return True
157
+
158
+
159
+ def all_partial_orderings(tuples, elements):
160
+ edges = defaultdict(set)
161
+ for parent, child in tuples:
162
+ edges[child].add(parent)
163
+
164
+ def _all_orderings(elements):
165
+ if len(elements) == 1:
166
+ yield list(elements)
167
+ else:
168
+ for elem in elements:
169
+ subset = set(elements).difference([elem])
170
+ if not subset.intersection(edges[elem]):
171
+ for sub_ordering in _all_orderings(subset):
172
+ yield [elem] + sub_ordering
173
+
174
+ return iter(_all_orderings(elements))
175
+
176
+
177
+ def function_named(fn, name):
178
+ """Return a function with a given __name__.
179
+
180
+ Will assign to __name__ and return the original function if possible on
181
+ the Python implementation, otherwise a new function will be constructed.
182
+
183
+ This function should be phased out as much as possible
184
+ in favor of @decorator. Tests that "generate" many named tests
185
+ should be modernized.
186
+
187
+ """
188
+ try:
189
+ fn.__name__ = name
190
+ except TypeError:
191
+ fn = types.FunctionType(
192
+ fn.__code__, fn.__globals__, name, fn.__defaults__, fn.__closure__
193
+ )
194
+ return fn
195
+
196
+
197
+ def run_as_contextmanager(ctx, fn, *arg, **kw):
198
+ """Run the given function under the given contextmanager,
199
+ simulating the behavior of 'with' to support older
200
+ Python versions.
201
+
202
+ This is not necessary anymore as we have placed 2.6
203
+ as minimum Python version, however some tests are still using
204
+ this structure.
205
+
206
+ """
207
+
208
+ obj = ctx.__enter__()
209
+ try:
210
+ result = fn(obj, *arg, **kw)
211
+ ctx.__exit__(None, None, None)
212
+ return result
213
+ except:
214
+ exc_info = sys.exc_info()
215
+ raise_ = ctx.__exit__(*exc_info)
216
+ if not raise_:
217
+ raise
218
+ else:
219
+ return raise_
220
+
221
+
222
+ def rowset(results):
223
+ """Converts the results of sql execution into a plain set of column tuples.
224
+
225
+ Useful for asserting the results of an unordered query.
226
+ """
227
+
228
+ return {tuple(row) for row in results}
229
+
230
+
231
+ def fail(msg):
232
+ assert False, msg
233
+
234
+
235
+ @decorator
236
+ def provide_metadata(fn, *args, **kw):
237
+ """Provide bound MetaData for a single test, dropping afterwards.
238
+
239
+ Legacy; use the "metadata" pytest fixture.
240
+
241
+ """
242
+
243
+ from . import fixtures
244
+
245
+ metadata = schema.MetaData()
246
+ self = args[0]
247
+ prev_meta = getattr(self, "metadata", None)
248
+ self.metadata = metadata
249
+ try:
250
+ return fn(*args, **kw)
251
+ finally:
252
+ # close out some things that get in the way of dropping tables.
253
+ # when using the "metadata" fixture, there is a set ordering
254
+ # of things that makes sure things are cleaned up in order, however
255
+ # the simple "decorator" nature of this legacy function means
256
+ # we have to hardcode some of that cleanup ahead of time.
257
+
258
+ # close ORM sessions
259
+ fixtures.close_all_sessions()
260
+
261
+ # integrate with the "connection" fixture as there are many
262
+ # tests where it is used along with provide_metadata
263
+ cfc = fixtures.base._connection_fixture_connection
264
+ if cfc:
265
+ # TODO: this warning can be used to find all the places
266
+ # this is used with connection fixture
267
+ # warn("mixing legacy provide metadata with connection fixture")
268
+ drop_all_tables_from_metadata(metadata, cfc)
269
+ # as the provide_metadata fixture is often used with "testing.db",
270
+ # when we do the drop we have to commit the transaction so that
271
+ # the DB is actually updated as the CREATE would have been
272
+ # committed
273
+ cfc.get_transaction().commit()
274
+ else:
275
+ drop_all_tables_from_metadata(metadata, config.db)
276
+ self.metadata = prev_meta
277
+
278
+
279
+ def flag_combinations(*combinations):
280
+ """A facade around @testing.combinations() oriented towards boolean
281
+ keyword-based arguments.
282
+
283
+ Basically generates a nice looking identifier based on the keywords
284
+ and also sets up the argument names.
285
+
286
+ E.g.::
287
+
288
+ @testing.flag_combinations(
289
+ dict(lazy=False, passive=False),
290
+ dict(lazy=True, passive=False),
291
+ dict(lazy=False, passive=True),
292
+ dict(lazy=False, passive=True, raiseload=True),
293
+ )
294
+ def test_fn(lazy, passive, raiseload): ...
295
+
296
+ would result in::
297
+
298
+ @testing.combinations(
299
+ ("", False, False, False),
300
+ ("lazy", True, False, False),
301
+ ("lazy_passive", True, True, False),
302
+ ("lazy_passive", True, True, True),
303
+ id_="iaaa",
304
+ argnames="lazy,passive,raiseload",
305
+ )
306
+ def test_fn(lazy, passive, raiseload): ...
307
+
308
+ """
309
+
310
+ keys = set()
311
+
312
+ for d in combinations:
313
+ keys.update(d)
314
+
315
+ keys = sorted(keys)
316
+
317
+ return config.combinations(
318
+ *[
319
+ ("_".join(k for k in keys if d.get(k, False)),)
320
+ + tuple(d.get(k, False) for k in keys)
321
+ for d in combinations
322
+ ],
323
+ id_="i" + ("a" * len(keys)),
324
+ argnames=",".join(keys),
325
+ )
326
+
327
+
328
+ def lambda_combinations(lambda_arg_sets, **kw):
329
+ args = inspect_getfullargspec(lambda_arg_sets)
330
+
331
+ arg_sets = lambda_arg_sets(*[mock.Mock() for arg in args[0]])
332
+
333
+ def create_fixture(pos):
334
+ def fixture(**kw):
335
+ return lambda_arg_sets(**kw)[pos]
336
+
337
+ fixture.__name__ = "fixture_%3.3d" % pos
338
+ return fixture
339
+
340
+ return config.combinations(
341
+ *[(create_fixture(i),) for i in range(len(arg_sets))], **kw
342
+ )
343
+
344
+
345
+ def resolve_lambda(__fn, **kw):
346
+ """Given a no-arg lambda and a namespace, return a new lambda that
347
+ has all the values filled in.
348
+
349
+ This is used so that we can have module-level fixtures that
350
+ refer to instance-level variables using lambdas.
351
+
352
+ """
353
+
354
+ pos_args = inspect_getfullargspec(__fn)[0]
355
+ pass_pos_args = {arg: kw.pop(arg) for arg in pos_args}
356
+ glb = dict(__fn.__globals__)
357
+ glb.update(kw)
358
+ new_fn = types.FunctionType(__fn.__code__, glb)
359
+ return new_fn(**pass_pos_args)
360
+
361
+
362
+ def metadata_fixture(ddl="function"):
363
+ """Provide MetaData for a pytest fixture."""
364
+
365
+ def decorate(fn):
366
+ def run_ddl(self):
367
+ metadata = self.metadata = schema.MetaData()
368
+ try:
369
+ result = fn(self, metadata)
370
+ metadata.create_all(config.db)
371
+ # TODO:
372
+ # somehow get a per-function dml erase fixture here
373
+ yield result
374
+ finally:
375
+ metadata.drop_all(config.db)
376
+
377
+ return config.fixture(scope=ddl)(run_ddl)
378
+
379
+ return decorate
380
+
381
+
382
+ def force_drop_names(*names):
383
+ """Force the given table names to be dropped after test complete,
384
+ isolating for foreign key cycles
385
+
386
+ """
387
+
388
+ @decorator
389
+ def go(fn, *args, **kw):
390
+ try:
391
+ return fn(*args, **kw)
392
+ finally:
393
+ drop_all_tables(config.db, inspect(config.db), include_names=names)
394
+
395
+ return go
396
+
397
+
398
+ class adict(dict):
399
+ """Dict keys available as attributes. Shadows."""
400
+
401
+ def __getattribute__(self, key):
402
+ try:
403
+ return self[key]
404
+ except KeyError:
405
+ return dict.__getattribute__(self, key)
406
+
407
+ def __call__(self, *keys):
408
+ return tuple([self[key] for key in keys])
409
+
410
+ get_all = __call__
411
+
412
+
413
+ def drop_all_tables_from_metadata(metadata, engine_or_connection):
414
+ from . import engines
415
+
416
+ def go(connection):
417
+ engines.testing_reaper.prepare_for_drop_tables(connection)
418
+
419
+ if not connection.dialect.supports_alter:
420
+ from . import assertions
421
+
422
+ with assertions.expect_warnings(
423
+ "Can't sort tables", assert_=False
424
+ ):
425
+ metadata.drop_all(connection)
426
+ else:
427
+ metadata.drop_all(connection)
428
+
429
+ if not isinstance(engine_or_connection, Connection):
430
+ with engine_or_connection.begin() as connection:
431
+ go(connection)
432
+ else:
433
+ go(engine_or_connection)
434
+
435
+
436
+ def drop_all_tables(
437
+ engine,
438
+ inspector,
439
+ schema=None,
440
+ consider_schemas=(None,),
441
+ include_names=None,
442
+ ):
443
+ if include_names is not None:
444
+ include_names = set(include_names)
445
+
446
+ if schema is not None:
447
+ assert consider_schemas == (
448
+ None,
449
+ ), "consider_schemas and schema are mutually exclusive"
450
+ consider_schemas = (schema,)
451
+
452
+ with engine.begin() as conn:
453
+ for table_key, fkcs in reversed(
454
+ inspector.sort_tables_on_foreign_key_dependency(
455
+ consider_schemas=consider_schemas
456
+ )
457
+ ):
458
+ if table_key:
459
+ if (
460
+ include_names is not None
461
+ and table_key[1] not in include_names
462
+ ):
463
+ continue
464
+ conn.execute(
465
+ DropTable(
466
+ Table(table_key[1], MetaData(), schema=table_key[0])
467
+ )
468
+ )
469
+ elif fkcs:
470
+ if not engine.dialect.supports_alter:
471
+ continue
472
+ for t_key, fkc in fkcs:
473
+ if (
474
+ include_names is not None
475
+ and t_key[1] not in include_names
476
+ ):
477
+ continue
478
+ tb = Table(
479
+ t_key[1],
480
+ MetaData(),
481
+ Column("x", Integer),
482
+ Column("y", Integer),
483
+ schema=t_key[0],
484
+ )
485
+ conn.execute(
486
+ DropConstraint(
487
+ ForeignKeyConstraint([tb.c.x], [tb.c.y], name=fkc)
488
+ ),
489
+ )
490
+
491
+
492
+ def teardown_events(event_cls):
493
+ @decorator
494
+ def decorate(fn, *arg, **kw):
495
+ try:
496
+ return fn(*arg, **kw)
497
+ finally:
498
+ event_cls._clear()
499
+
500
+ return decorate
501
+
502
+
503
+ def total_size(o):
504
+ """Returns the approximate memory footprint an object and all of its
505
+ contents.
506
+
507
+ source: https://code.activestate.com/recipes/577504/
508
+
509
+
510
+ """
511
+
512
+ def dict_handler(d):
513
+ return chain.from_iterable(d.items())
514
+
515
+ all_handlers = {
516
+ tuple: iter,
517
+ list: iter,
518
+ deque: iter,
519
+ dict: dict_handler,
520
+ set: iter,
521
+ frozenset: iter,
522
+ }
523
+ seen = set() # track which object id's have already been seen
524
+ default_size = getsizeof(0) # estimate sizeof object without __sizeof__
525
+
526
+ def sizeof(o):
527
+ if id(o) in seen: # do not double count the same object
528
+ return 0
529
+ seen.add(id(o))
530
+ s = getsizeof(o, default_size)
531
+
532
+ for typ, handler in all_handlers.items():
533
+ if isinstance(o, typ):
534
+ s += sum(map(sizeof, handler(o)))
535
+ break
536
+ return s
537
+
538
+ return sizeof(o)
539
+
540
+
541
+ def count_cache_key_tuples(tup):
542
+ """given a cache key tuple, counts how many instances of actual
543
+ tuples are found.
544
+
545
+ used to alert large jumps in cache key complexity.
546
+
547
+ """
548
+ stack = [tup]
549
+
550
+ sentinel = object()
551
+ num_elements = 0
552
+
553
+ while stack:
554
+ elem = stack.pop(0)
555
+ if elem is sentinel:
556
+ num_elements += 1
557
+ elif isinstance(elem, tuple):
558
+ if elem:
559
+ stack = list(elem) + [sentinel] + stack
560
+ return num_elements
561
+
562
+
563
+ @contextlib.contextmanager
564
+ def skip_if_timeout(seconds: float, cleanup: Any = None):
565
+
566
+ now = time.time()
567
+ yield
568
+ sec = time.time() - now
569
+ if sec > seconds:
570
+ try:
571
+ cleanup()
572
+ finally:
573
+ config.skip_test(
574
+ f"test took too long ({sec:.4f} seconds > {seconds})"
575
+ )
@@ -0,0 +1,52 @@
1
+ # testing/warnings.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
+ from __future__ import annotations
10
+
11
+ import warnings
12
+
13
+ from . import assertions
14
+ from .. import exc
15
+ from .. import exc as sa_exc
16
+ from ..exc import SATestSuiteWarning
17
+ from ..util.langhelpers import _warnings_warn
18
+
19
+
20
+ def warn_test_suite(message):
21
+ _warnings_warn(message, category=SATestSuiteWarning)
22
+
23
+
24
+ def setup_filters():
25
+ """hook for setting up warnings filters.
26
+
27
+ SQLAlchemy-specific classes must only be here and not in pytest config,
28
+ as we need to delay importing SQLAlchemy until conftest.py has been
29
+ processed.
30
+
31
+ NOTE: filters on subclasses of DeprecationWarning or
32
+ PendingDeprecationWarning have no effect if added here, since pytest
33
+ will add at each test the following filters
34
+ ``always::PendingDeprecationWarning`` and ``always::DeprecationWarning``
35
+ that will take precedence over any added here.
36
+
37
+ """
38
+ warnings.filterwarnings("error", category=exc.SAWarning)
39
+ warnings.filterwarnings("always", category=exc.SATestSuiteWarning)
40
+
41
+
42
+ def assert_warnings(fn, warning_msgs, regex=False):
43
+ """Assert that each of the given warnings are emitted by fn.
44
+
45
+ Deprecated. Please use assertions.expect_warnings().
46
+
47
+ """
48
+
49
+ with assertions._expect_warnings(
50
+ sa_exc.SAWarning, warning_msgs, regex=regex
51
+ ):
52
+ return fn()
sqlalchemy/types.py ADDED
@@ -0,0 +1,75 @@
1
+ # types.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
+ """Compatibility namespace for sqlalchemy.sql.types."""
9
+
10
+ from __future__ import annotations
11
+
12
+ from .sql.operators import OperatorClass as OperatorClass
13
+ from .sql.sqltypes import _Binary as _Binary
14
+ from .sql.sqltypes import ARRAY as ARRAY
15
+ from .sql.sqltypes import BIGINT as BIGINT
16
+ from .sql.sqltypes import BigInteger as BigInteger
17
+ from .sql.sqltypes import BINARY as BINARY
18
+ from .sql.sqltypes import BLOB as BLOB
19
+ from .sql.sqltypes import BOOLEAN as BOOLEAN
20
+ from .sql.sqltypes import Boolean as Boolean
21
+ from .sql.sqltypes import CHAR as CHAR
22
+ from .sql.sqltypes import CLOB as CLOB
23
+ from .sql.sqltypes import Concatenable as Concatenable
24
+ from .sql.sqltypes import DATE as DATE
25
+ from .sql.sqltypes import Date as Date
26
+ from .sql.sqltypes import DATETIME as DATETIME
27
+ from .sql.sqltypes import DateTime as DateTime
28
+ from .sql.sqltypes import DECIMAL as DECIMAL
29
+ from .sql.sqltypes import DOUBLE as DOUBLE
30
+ from .sql.sqltypes import Double as Double
31
+ from .sql.sqltypes import DOUBLE_PRECISION as DOUBLE_PRECISION
32
+ from .sql.sqltypes import Enum as Enum
33
+ from .sql.sqltypes import FLOAT as FLOAT
34
+ from .sql.sqltypes import Float as Float
35
+ from .sql.sqltypes import Indexable as Indexable
36
+ from .sql.sqltypes import INT as INT
37
+ from .sql.sqltypes import INTEGER as INTEGER
38
+ from .sql.sqltypes import Integer as Integer
39
+ from .sql.sqltypes import Interval as Interval
40
+ from .sql.sqltypes import JSON as JSON
41
+ from .sql.sqltypes import LargeBinary as LargeBinary
42
+ from .sql.sqltypes import MatchType as MatchType
43
+ from .sql.sqltypes import NCHAR as NCHAR
44
+ from .sql.sqltypes import NULLTYPE as NULLTYPE
45
+ from .sql.sqltypes import NullType as NullType
46
+ from .sql.sqltypes import NUMERIC as NUMERIC
47
+ from .sql.sqltypes import Numeric as Numeric
48
+ from .sql.sqltypes import NumericCommon as NumericCommon
49
+ from .sql.sqltypes import NVARCHAR as NVARCHAR
50
+ from .sql.sqltypes import PickleType as PickleType
51
+ from .sql.sqltypes import REAL as REAL
52
+ from .sql.sqltypes import SchemaType as SchemaType
53
+ from .sql.sqltypes import SMALLINT as SMALLINT
54
+ from .sql.sqltypes import SmallInteger as SmallInteger
55
+ from .sql.sqltypes import String as String
56
+ from .sql.sqltypes import STRINGTYPE as STRINGTYPE
57
+ from .sql.sqltypes import TEXT as TEXT
58
+ from .sql.sqltypes import Text as Text
59
+ from .sql.sqltypes import TIME as TIME
60
+ from .sql.sqltypes import Time as Time
61
+ from .sql.sqltypes import TIMESTAMP as TIMESTAMP
62
+ from .sql.sqltypes import TupleType as TupleType
63
+ from .sql.sqltypes import Unicode as Unicode
64
+ from .sql.sqltypes import UnicodeText as UnicodeText
65
+ from .sql.sqltypes import UUID as UUID
66
+ from .sql.sqltypes import Uuid as Uuid
67
+ from .sql.sqltypes import VARBINARY as VARBINARY
68
+ from .sql.sqltypes import VARCHAR as VARCHAR
69
+ from .sql.type_api import adapt_type as adapt_type
70
+ from .sql.type_api import ExternalType as ExternalType
71
+ from .sql.type_api import to_instance as to_instance
72
+ from .sql.type_api import TypeDecorator as TypeDecorator
73
+ from .sql.type_api import TypeEngine as TypeEngine
74
+ from .sql.type_api import UserDefinedType as UserDefinedType
75
+ from .sql.type_api import Variant as Variant