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,167 @@
1
+ # engine/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
+
8
+ from __future__ import annotations
9
+
10
+ import typing
11
+ from typing import Any
12
+ from typing import Callable
13
+ from typing import Optional
14
+ from typing import TypeVar
15
+
16
+ from .. import exc
17
+ from .. import util
18
+ from ..util._has_cy import HAS_CYEXTENSION
19
+ from ..util.typing import Protocol
20
+ from ..util.typing import Self
21
+
22
+ if typing.TYPE_CHECKING or not HAS_CYEXTENSION:
23
+ from ._py_util import _distill_params_20 as _distill_params_20
24
+ from ._py_util import _distill_raw_params as _distill_raw_params
25
+ else:
26
+ from sqlalchemy.cyextension.util import ( # noqa: F401
27
+ _distill_params_20 as _distill_params_20,
28
+ )
29
+ from sqlalchemy.cyextension.util import ( # noqa: F401
30
+ _distill_raw_params as _distill_raw_params,
31
+ )
32
+
33
+ _C = TypeVar("_C", bound=Callable[[], Any])
34
+
35
+
36
+ def connection_memoize(key: str) -> Callable[[_C], _C]:
37
+ """Decorator, memoize a function in a connection.info stash.
38
+
39
+ Only applicable to functions which take no arguments other than a
40
+ connection. The memo will be stored in ``connection.info[key]``.
41
+ """
42
+
43
+ @util.decorator
44
+ def decorated(fn, self, connection): # type: ignore
45
+ connection = connection.connect()
46
+ try:
47
+ return connection.info[key]
48
+ except KeyError:
49
+ connection.info[key] = val = fn(self, connection)
50
+ return val
51
+
52
+ return decorated
53
+
54
+
55
+ class _TConsSubject(Protocol):
56
+ _trans_context_manager: Optional[TransactionalContext]
57
+
58
+
59
+ class TransactionalContext:
60
+ """Apply Python context manager behavior to transaction objects.
61
+
62
+ Performs validation to ensure the subject of the transaction is not
63
+ used if the transaction were ended prematurely.
64
+
65
+ """
66
+
67
+ __slots__ = ("_outer_trans_ctx", "_trans_subject", "__weakref__")
68
+
69
+ _trans_subject: Optional[_TConsSubject]
70
+
71
+ def _transaction_is_active(self) -> bool:
72
+ raise NotImplementedError()
73
+
74
+ def _transaction_is_closed(self) -> bool:
75
+ raise NotImplementedError()
76
+
77
+ def _rollback_can_be_called(self) -> bool:
78
+ """indicates the object is in a state that is known to be acceptable
79
+ for rollback() to be called.
80
+
81
+ This does not necessarily mean rollback() will succeed or not raise
82
+ an error, just that there is currently no state detected that indicates
83
+ rollback() would fail or emit warnings.
84
+
85
+ It also does not mean that there's a transaction in progress, as
86
+ it is usually safe to call rollback() even if no transaction is
87
+ present.
88
+
89
+ .. versionadded:: 1.4.28
90
+
91
+ """
92
+ raise NotImplementedError()
93
+
94
+ def _get_subject(self) -> _TConsSubject:
95
+ raise NotImplementedError()
96
+
97
+ def commit(self) -> None:
98
+ raise NotImplementedError()
99
+
100
+ def rollback(self) -> None:
101
+ raise NotImplementedError()
102
+
103
+ def close(self) -> None:
104
+ raise NotImplementedError()
105
+
106
+ @classmethod
107
+ def _trans_ctx_check(cls, subject: _TConsSubject) -> None:
108
+ trans_context = subject._trans_context_manager
109
+ if trans_context:
110
+ if not trans_context._transaction_is_active():
111
+ raise exc.InvalidRequestError(
112
+ "Can't operate on closed transaction inside context "
113
+ "manager. Please complete the context manager "
114
+ "before emitting further commands."
115
+ )
116
+
117
+ def __enter__(self) -> Self:
118
+ subject = self._get_subject()
119
+
120
+ # none for outer transaction, may be non-None for nested
121
+ # savepoint, legacy nesting cases
122
+ trans_context = subject._trans_context_manager
123
+ self._outer_trans_ctx = trans_context
124
+
125
+ self._trans_subject = subject
126
+ subject._trans_context_manager = self
127
+ return self
128
+
129
+ def __exit__(self, type_: Any, value: Any, traceback: Any) -> None:
130
+ subject = getattr(self, "_trans_subject", None)
131
+
132
+ # simplistically we could assume that
133
+ # "subject._trans_context_manager is self". However, any calling
134
+ # code that is manipulating __exit__ directly would break this
135
+ # assumption. alembic context manager
136
+ # is an example of partial use that just calls __exit__ and
137
+ # not __enter__ at the moment. it's safe to assume this is being done
138
+ # in the wild also
139
+ out_of_band_exit = (
140
+ subject is None or subject._trans_context_manager is not self
141
+ )
142
+
143
+ if type_ is None and self._transaction_is_active():
144
+ try:
145
+ self.commit()
146
+ except:
147
+ with util.safe_reraise():
148
+ if self._rollback_can_be_called():
149
+ self.rollback()
150
+ finally:
151
+ if not out_of_band_exit:
152
+ assert subject is not None
153
+ subject._trans_context_manager = self._outer_trans_ctx
154
+ self._trans_subject = self._outer_trans_ctx = None
155
+ else:
156
+ try:
157
+ if not self._transaction_is_active():
158
+ if not self._transaction_is_closed():
159
+ self.close()
160
+ else:
161
+ if self._rollback_can_be_called():
162
+ self.rollback()
163
+ finally:
164
+ if not out_of_band_exit:
165
+ assert subject is not None
166
+ subject._trans_context_manager = self._outer_trans_ctx
167
+ self._trans_subject = self._outer_trans_ctx = None
@@ -0,0 +1,26 @@
1
+ # event/__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
7
+
8
+ from __future__ import annotations
9
+
10
+ from .api import CANCEL as CANCEL
11
+ from .api import contains as contains
12
+ from .api import listen as listen
13
+ from .api import listens_for as listens_for
14
+ from .api import NO_RETVAL as NO_RETVAL
15
+ from .api import remove as remove
16
+ from .attr import _InstanceLevelDispatch as _InstanceLevelDispatch
17
+ from .attr import RefCollection as RefCollection
18
+ from .base import _Dispatch as _Dispatch
19
+ from .base import _DispatchCommon as _DispatchCommon
20
+ from .base import dispatcher as dispatcher
21
+ from .base import Events as Events
22
+ from .legacy import _legacy_signature as _legacy_signature
23
+ from .legacy import _omit_standard_example as _omit_standard_example
24
+ from .registry import _EventKey as _EventKey
25
+ from .registry import _ListenerFnType as _ListenerFnType
26
+ from .registry import EventTarget as EventTarget
@@ -0,0 +1,220 @@
1
+ # event/api.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
+ """Public API functions for the event system."""
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+ from typing import Callable
13
+
14
+ from .base import _registrars
15
+ from .registry import _ET
16
+ from .registry import _EventKey
17
+ from .registry import _ListenerFnType
18
+ from .. import exc
19
+ from .. import util
20
+
21
+
22
+ CANCEL = util.symbol("CANCEL")
23
+ NO_RETVAL = util.symbol("NO_RETVAL")
24
+
25
+
26
+ def _event_key(
27
+ target: _ET, identifier: str, fn: _ListenerFnType
28
+ ) -> _EventKey[_ET]:
29
+ for evt_cls in _registrars[identifier]:
30
+ tgt = evt_cls._accept_with(target, identifier)
31
+ if tgt is not None:
32
+ return _EventKey(target, identifier, fn, tgt)
33
+ else:
34
+ raise exc.InvalidRequestError(
35
+ "No such event '%s' for target '%s'" % (identifier, target)
36
+ )
37
+
38
+
39
+ def listen(
40
+ target: Any, identifier: str, fn: Callable[..., Any], *args: Any, **kw: Any
41
+ ) -> None:
42
+ """Register a listener function for the given target.
43
+
44
+ The :func:`.listen` function is part of the primary interface for the
45
+ SQLAlchemy event system, documented at :ref:`event_toplevel`.
46
+
47
+ e.g.::
48
+
49
+ from sqlalchemy import event
50
+ from sqlalchemy.schema import UniqueConstraint
51
+
52
+
53
+ def unique_constraint_name(const, table):
54
+ const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)
55
+
56
+
57
+ event.listen(
58
+ UniqueConstraint, "after_parent_attach", unique_constraint_name
59
+ )
60
+
61
+ :param bool insert: The default behavior for event handlers is to append
62
+ the decorated user defined function to an internal list of registered
63
+ event listeners upon discovery. If a user registers a function with
64
+ ``insert=True``, SQLAlchemy will insert (prepend) the function to the
65
+ internal list upon discovery. This feature is not typically used or
66
+ recommended by the SQLAlchemy maintainers, but is provided to ensure
67
+ certain user defined functions can run before others, such as when
68
+ :ref:`Changing the sql_mode in MySQL <mysql_sql_mode>`.
69
+
70
+ :param bool named: When using named argument passing, the names listed in
71
+ the function argument specification will be used as keys in the
72
+ dictionary.
73
+ See :ref:`event_named_argument_styles`.
74
+
75
+ :param bool once: Private/Internal API usage. Deprecated. This parameter
76
+ would provide that an event function would run only once per given
77
+ target. It does not however imply automatic de-registration of the
78
+ listener function; associating an arbitrarily high number of listeners
79
+ without explicitly removing them will cause memory to grow unbounded even
80
+ if ``once=True`` is specified.
81
+
82
+ :param bool propagate: The ``propagate`` kwarg is available when working
83
+ with ORM instrumentation and mapping events.
84
+ See :class:`_ormevent.MapperEvents` and
85
+ :meth:`_ormevent.MapperEvents.before_mapper_configured` for examples.
86
+
87
+ :param bool retval: This flag applies only to specific event listeners,
88
+ each of which includes documentation explaining when it should be used.
89
+ By default, no listener ever requires a return value.
90
+ However, some listeners do support special behaviors for return values,
91
+ and include in their documentation that the ``retval=True`` flag is
92
+ necessary for a return value to be processed.
93
+
94
+ Event listener suites that make use of :paramref:`_event.listen.retval`
95
+ include :class:`_events.ConnectionEvents` and
96
+ :class:`_ormevent.AttributeEvents`.
97
+
98
+ .. note::
99
+
100
+ The :func:`.listen` function cannot be called at the same time
101
+ that the target event is being run. This has implications
102
+ for thread safety, and also means an event cannot be added
103
+ from inside the listener function for itself. The list of
104
+ events to be run are present inside of a mutable collection
105
+ that can't be changed during iteration.
106
+
107
+ Event registration and removal is not intended to be a "high
108
+ velocity" operation; it is a configurational operation. For
109
+ systems that need to quickly associate and deassociate with
110
+ events at high scale, use a mutable structure that is handled
111
+ from inside of a single listener.
112
+
113
+ .. seealso::
114
+
115
+ :func:`.listens_for`
116
+
117
+ :func:`.remove`
118
+
119
+ """
120
+
121
+ _event_key(target, identifier, fn).listen(*args, **kw)
122
+
123
+
124
+ def listens_for(
125
+ target: Any, identifier: str, *args: Any, **kw: Any
126
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
127
+ """Decorate a function as a listener for the given target + identifier.
128
+
129
+ The :func:`.listens_for` decorator is part of the primary interface for the
130
+ SQLAlchemy event system, documented at :ref:`event_toplevel`.
131
+
132
+ This function generally shares the same kwargs as :func:`.listen`.
133
+
134
+ e.g.::
135
+
136
+ from sqlalchemy import event
137
+ from sqlalchemy.schema import UniqueConstraint
138
+
139
+
140
+ @event.listens_for(UniqueConstraint, "after_parent_attach")
141
+ def unique_constraint_name(const, table):
142
+ const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)
143
+
144
+ A given function can also be invoked for only the first invocation
145
+ of the event using the ``once`` argument::
146
+
147
+ @event.listens_for(Mapper, "before_configure", once=True)
148
+ def on_config():
149
+ do_config()
150
+
151
+ .. warning:: The ``once`` argument does not imply automatic de-registration
152
+ of the listener function after it has been invoked a first time; a
153
+ listener entry will remain associated with the target object.
154
+ Associating an arbitrarily high number of listeners without explicitly
155
+ removing them will cause memory to grow unbounded even if ``once=True``
156
+ is specified.
157
+
158
+ .. seealso::
159
+
160
+ :func:`.listen` - general description of event listening
161
+
162
+ """
163
+
164
+ def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
165
+ listen(target, identifier, fn, *args, **kw)
166
+ return fn
167
+
168
+ return decorate
169
+
170
+
171
+ def remove(target: Any, identifier: str, fn: Callable[..., Any]) -> None:
172
+ """Remove an event listener.
173
+
174
+ The arguments here should match exactly those which were sent to
175
+ :func:`.listen`; all the event registration which proceeded as a result
176
+ of this call will be reverted by calling :func:`.remove` with the same
177
+ arguments.
178
+
179
+ e.g.::
180
+
181
+ # if a function was registered like this...
182
+ @event.listens_for(SomeMappedClass, "before_insert", propagate=True)
183
+ def my_listener_function(*arg):
184
+ pass
185
+
186
+
187
+ # ... it's removed like this
188
+ event.remove(SomeMappedClass, "before_insert", my_listener_function)
189
+
190
+ Above, the listener function associated with ``SomeMappedClass`` was also
191
+ propagated to subclasses of ``SomeMappedClass``; the :func:`.remove`
192
+ function will revert all of these operations.
193
+
194
+ .. note::
195
+
196
+ The :func:`.remove` function cannot be called at the same time
197
+ that the target event is being run. This has implications
198
+ for thread safety, and also means an event cannot be removed
199
+ from inside the listener function for itself. The list of
200
+ events to be run are present inside of a mutable collection
201
+ that can't be changed during iteration.
202
+
203
+ Event registration and removal is not intended to be a "high
204
+ velocity" operation; it is a configurational operation. For
205
+ systems that need to quickly associate and deassociate with
206
+ events at high scale, use a mutable structure that is handled
207
+ from inside of a single listener.
208
+
209
+ .. seealso::
210
+
211
+ :func:`.listen`
212
+
213
+ """
214
+ _event_key(target, identifier, fn).remove()
215
+
216
+
217
+ def contains(target: Any, identifier: str, fn: Callable[..., Any]) -> bool:
218
+ """Return True if the given target/ident/fn is set up to listen."""
219
+
220
+ return _event_key(target, identifier, fn).contains()