SQLAlchemy 2.0.36__cp313-cp313-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. SQLAlchemy-2.0.36.dist-info/LICENSE +19 -0
  2. SQLAlchemy-2.0.36.dist-info/METADATA +243 -0
  3. SQLAlchemy-2.0.36.dist-info/RECORD +273 -0
  4. SQLAlchemy-2.0.36.dist-info/WHEEL +5 -0
  5. SQLAlchemy-2.0.36.dist-info/top_level.txt +1 -0
  6. sqlalchemy/__init__.py +294 -0
  7. sqlalchemy/connectors/__init__.py +18 -0
  8. sqlalchemy/connectors/aioodbc.py +174 -0
  9. sqlalchemy/connectors/asyncio.py +213 -0
  10. sqlalchemy/connectors/pyodbc.py +249 -0
  11. sqlalchemy/cyextension/__init__.py +6 -0
  12. sqlalchemy/cyextension/collections.cp313-win32.pyd +0 -0
  13. sqlalchemy/cyextension/collections.pyx +409 -0
  14. sqlalchemy/cyextension/immutabledict.cp313-win32.pyd +0 -0
  15. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  16. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  17. sqlalchemy/cyextension/processors.cp313-win32.pyd +0 -0
  18. sqlalchemy/cyextension/processors.pyx +68 -0
  19. sqlalchemy/cyextension/resultproxy.cp313-win32.pyd +0 -0
  20. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  21. sqlalchemy/cyextension/util.cp313-win32.pyd +0 -0
  22. sqlalchemy/cyextension/util.pyx +91 -0
  23. sqlalchemy/dialects/__init__.py +61 -0
  24. sqlalchemy/dialects/_typing.py +25 -0
  25. sqlalchemy/dialects/mssql/__init__.py +88 -0
  26. sqlalchemy/dialects/mssql/aioodbc.py +64 -0
  27. sqlalchemy/dialects/mssql/base.py +4010 -0
  28. sqlalchemy/dialects/mssql/information_schema.py +254 -0
  29. sqlalchemy/dialects/mssql/json.py +133 -0
  30. sqlalchemy/dialects/mssql/provision.py +162 -0
  31. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  32. sqlalchemy/dialects/mssql/pyodbc.py +745 -0
  33. sqlalchemy/dialects/mysql/__init__.py +101 -0
  34. sqlalchemy/dialects/mysql/aiomysql.py +333 -0
  35. sqlalchemy/dialects/mysql/asyncmy.py +337 -0
  36. sqlalchemy/dialects/mysql/base.py +3494 -0
  37. sqlalchemy/dialects/mysql/cymysql.py +84 -0
  38. sqlalchemy/dialects/mysql/dml.py +219 -0
  39. sqlalchemy/dialects/mysql/enumerated.py +244 -0
  40. sqlalchemy/dialects/mysql/expression.py +141 -0
  41. sqlalchemy/dialects/mysql/json.py +81 -0
  42. sqlalchemy/dialects/mysql/mariadb.py +32 -0
  43. sqlalchemy/dialects/mysql/mariadbconnector.py +277 -0
  44. sqlalchemy/dialects/mysql/mysqlconnector.py +180 -0
  45. sqlalchemy/dialects/mysql/mysqldb.py +303 -0
  46. sqlalchemy/dialects/mysql/provision.py +110 -0
  47. sqlalchemy/dialects/mysql/pymysql.py +137 -0
  48. sqlalchemy/dialects/mysql/pyodbc.py +138 -0
  49. sqlalchemy/dialects/mysql/reflection.py +677 -0
  50. sqlalchemy/dialects/mysql/reserved_words.py +571 -0
  51. sqlalchemy/dialects/mysql/types.py +774 -0
  52. sqlalchemy/dialects/oracle/__init__.py +67 -0
  53. sqlalchemy/dialects/oracle/base.py +3271 -0
  54. sqlalchemy/dialects/oracle/cx_oracle.py +1483 -0
  55. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  56. sqlalchemy/dialects/oracle/oracledb.py +431 -0
  57. sqlalchemy/dialects/oracle/provision.py +220 -0
  58. sqlalchemy/dialects/oracle/types.py +287 -0
  59. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  60. sqlalchemy/dialects/postgresql/_psycopg_common.py +187 -0
  61. sqlalchemy/dialects/postgresql/array.py +425 -0
  62. sqlalchemy/dialects/postgresql/asyncpg.py +1274 -0
  63. sqlalchemy/dialects/postgresql/base.py +5008 -0
  64. sqlalchemy/dialects/postgresql/dml.py +310 -0
  65. sqlalchemy/dialects/postgresql/ext.py +496 -0
  66. sqlalchemy/dialects/postgresql/hstore.py +397 -0
  67. sqlalchemy/dialects/postgresql/json.py +333 -0
  68. sqlalchemy/dialects/postgresql/named_types.py +509 -0
  69. sqlalchemy/dialects/postgresql/operators.py +129 -0
  70. sqlalchemy/dialects/postgresql/pg8000.py +662 -0
  71. sqlalchemy/dialects/postgresql/pg_catalog.py +300 -0
  72. sqlalchemy/dialects/postgresql/provision.py +175 -0
  73. sqlalchemy/dialects/postgresql/psycopg.py +772 -0
  74. sqlalchemy/dialects/postgresql/psycopg2.py +886 -0
  75. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  76. sqlalchemy/dialects/postgresql/ranges.py +1029 -0
  77. sqlalchemy/dialects/postgresql/types.py +303 -0
  78. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  79. sqlalchemy/dialects/sqlite/aiosqlite.py +396 -0
  80. sqlalchemy/dialects/sqlite/base.py +2805 -0
  81. sqlalchemy/dialects/sqlite/dml.py +240 -0
  82. sqlalchemy/dialects/sqlite/json.py +92 -0
  83. sqlalchemy/dialects/sqlite/provision.py +198 -0
  84. sqlalchemy/dialects/sqlite/pysqlcipher.py +155 -0
  85. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  86. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  87. sqlalchemy/engine/__init__.py +62 -0
  88. sqlalchemy/engine/_py_processors.py +136 -0
  89. sqlalchemy/engine/_py_row.py +128 -0
  90. sqlalchemy/engine/_py_util.py +74 -0
  91. sqlalchemy/engine/base.py +3375 -0
  92. sqlalchemy/engine/characteristics.py +155 -0
  93. sqlalchemy/engine/create.py +875 -0
  94. sqlalchemy/engine/cursor.py +2181 -0
  95. sqlalchemy/engine/default.py +2365 -0
  96. sqlalchemy/engine/events.py +951 -0
  97. sqlalchemy/engine/interfaces.py +3403 -0
  98. sqlalchemy/engine/mock.py +131 -0
  99. sqlalchemy/engine/processors.py +61 -0
  100. sqlalchemy/engine/reflection.py +2098 -0
  101. sqlalchemy/engine/result.py +2382 -0
  102. sqlalchemy/engine/row.py +401 -0
  103. sqlalchemy/engine/strategies.py +19 -0
  104. sqlalchemy/engine/url.py +910 -0
  105. sqlalchemy/engine/util.py +167 -0
  106. sqlalchemy/event/__init__.py +25 -0
  107. sqlalchemy/event/api.py +225 -0
  108. sqlalchemy/event/attr.py +655 -0
  109. sqlalchemy/event/base.py +470 -0
  110. sqlalchemy/event/legacy.py +246 -0
  111. sqlalchemy/event/registry.py +386 -0
  112. sqlalchemy/events.py +17 -0
  113. sqlalchemy/exc.py +830 -0
  114. sqlalchemy/ext/__init__.py +11 -0
  115. sqlalchemy/ext/associationproxy.py +2013 -0
  116. sqlalchemy/ext/asyncio/__init__.py +25 -0
  117. sqlalchemy/ext/asyncio/base.py +279 -0
  118. sqlalchemy/ext/asyncio/engine.py +1466 -0
  119. sqlalchemy/ext/asyncio/exc.py +21 -0
  120. sqlalchemy/ext/asyncio/result.py +961 -0
  121. sqlalchemy/ext/asyncio/scoping.py +1614 -0
  122. sqlalchemy/ext/asyncio/session.py +1936 -0
  123. sqlalchemy/ext/automap.py +1691 -0
  124. sqlalchemy/ext/baked.py +574 -0
  125. sqlalchemy/ext/compiler.py +570 -0
  126. sqlalchemy/ext/declarative/__init__.py +65 -0
  127. sqlalchemy/ext/declarative/extensions.py +548 -0
  128. sqlalchemy/ext/horizontal_shard.py +481 -0
  129. sqlalchemy/ext/hybrid.py +1514 -0
  130. sqlalchemy/ext/indexable.py +341 -0
  131. sqlalchemy/ext/instrumentation.py +450 -0
  132. sqlalchemy/ext/mutable.py +1073 -0
  133. sqlalchemy/ext/mypy/__init__.py +6 -0
  134. sqlalchemy/ext/mypy/apply.py +320 -0
  135. sqlalchemy/ext/mypy/decl_class.py +515 -0
  136. sqlalchemy/ext/mypy/infer.py +590 -0
  137. sqlalchemy/ext/mypy/names.py +335 -0
  138. sqlalchemy/ext/mypy/plugin.py +303 -0
  139. sqlalchemy/ext/mypy/util.py +357 -0
  140. sqlalchemy/ext/orderinglist.py +416 -0
  141. sqlalchemy/ext/serializer.py +181 -0
  142. sqlalchemy/future/__init__.py +16 -0
  143. sqlalchemy/future/engine.py +15 -0
  144. sqlalchemy/inspection.py +174 -0
  145. sqlalchemy/log.py +288 -0
  146. sqlalchemy/orm/__init__.py +170 -0
  147. sqlalchemy/orm/_orm_constructors.py +2571 -0
  148. sqlalchemy/orm/_typing.py +179 -0
  149. sqlalchemy/orm/attributes.py +2835 -0
  150. sqlalchemy/orm/base.py +973 -0
  151. sqlalchemy/orm/bulk_persistence.py +2123 -0
  152. sqlalchemy/orm/clsregistry.py +571 -0
  153. sqlalchemy/orm/collections.py +1620 -0
  154. sqlalchemy/orm/context.py +3268 -0
  155. sqlalchemy/orm/decl_api.py +1883 -0
  156. sqlalchemy/orm/decl_base.py +2190 -0
  157. sqlalchemy/orm/dependency.py +1304 -0
  158. sqlalchemy/orm/descriptor_props.py +1076 -0
  159. sqlalchemy/orm/dynamic.py +300 -0
  160. sqlalchemy/orm/evaluator.py +379 -0
  161. sqlalchemy/orm/events.py +3261 -0
  162. sqlalchemy/orm/exc.py +228 -0
  163. sqlalchemy/orm/identity.py +302 -0
  164. sqlalchemy/orm/instrumentation.py +754 -0
  165. sqlalchemy/orm/interfaces.py +1474 -0
  166. sqlalchemy/orm/loading.py +1682 -0
  167. sqlalchemy/orm/mapped_collection.py +557 -0
  168. sqlalchemy/orm/mapper.py +4432 -0
  169. sqlalchemy/orm/path_registry.py +811 -0
  170. sqlalchemy/orm/persistence.py +1782 -0
  171. sqlalchemy/orm/properties.py +886 -0
  172. sqlalchemy/orm/query.py +3396 -0
  173. sqlalchemy/orm/relationships.py +3500 -0
  174. sqlalchemy/orm/scoping.py +2165 -0
  175. sqlalchemy/orm/session.py +5301 -0
  176. sqlalchemy/orm/state.py +1143 -0
  177. sqlalchemy/orm/state_changes.py +198 -0
  178. sqlalchemy/orm/strategies.py +3473 -0
  179. sqlalchemy/orm/strategy_options.py +2569 -0
  180. sqlalchemy/orm/sync.py +164 -0
  181. sqlalchemy/orm/unitofwork.py +796 -0
  182. sqlalchemy/orm/util.py +2424 -0
  183. sqlalchemy/orm/writeonly.py +678 -0
  184. sqlalchemy/pool/__init__.py +44 -0
  185. sqlalchemy/pool/base.py +1515 -0
  186. sqlalchemy/pool/events.py +370 -0
  187. sqlalchemy/pool/impl.py +581 -0
  188. sqlalchemy/py.typed +0 -0
  189. sqlalchemy/schema.py +70 -0
  190. sqlalchemy/sql/__init__.py +145 -0
  191. sqlalchemy/sql/_dml_constructors.py +140 -0
  192. sqlalchemy/sql/_elements_constructors.py +1850 -0
  193. sqlalchemy/sql/_orm_types.py +20 -0
  194. sqlalchemy/sql/_py_util.py +75 -0
  195. sqlalchemy/sql/_selectable_constructors.py +635 -0
  196. sqlalchemy/sql/_typing.py +460 -0
  197. sqlalchemy/sql/annotation.py +585 -0
  198. sqlalchemy/sql/base.py +2185 -0
  199. sqlalchemy/sql/cache_key.py +1057 -0
  200. sqlalchemy/sql/coercions.py +1405 -0
  201. sqlalchemy/sql/compiler.py +7818 -0
  202. sqlalchemy/sql/crud.py +1669 -0
  203. sqlalchemy/sql/ddl.py +1378 -0
  204. sqlalchemy/sql/default_comparator.py +552 -0
  205. sqlalchemy/sql/dml.py +1817 -0
  206. sqlalchemy/sql/elements.py +5499 -0
  207. sqlalchemy/sql/events.py +455 -0
  208. sqlalchemy/sql/expression.py +162 -0
  209. sqlalchemy/sql/functions.py +2055 -0
  210. sqlalchemy/sql/lambdas.py +1449 -0
  211. sqlalchemy/sql/naming.py +212 -0
  212. sqlalchemy/sql/operators.py +2579 -0
  213. sqlalchemy/sql/roles.py +323 -0
  214. sqlalchemy/sql/schema.py +6158 -0
  215. sqlalchemy/sql/selectable.py +7004 -0
  216. sqlalchemy/sql/sqltypes.py +3827 -0
  217. sqlalchemy/sql/traversals.py +1024 -0
  218. sqlalchemy/sql/type_api.py +2339 -0
  219. sqlalchemy/sql/util.py +1486 -0
  220. sqlalchemy/sql/visitors.py +1165 -0
  221. sqlalchemy/testing/__init__.py +96 -0
  222. sqlalchemy/testing/assertions.py +989 -0
  223. sqlalchemy/testing/assertsql.py +516 -0
  224. sqlalchemy/testing/asyncio.py +135 -0
  225. sqlalchemy/testing/config.py +427 -0
  226. sqlalchemy/testing/engines.py +472 -0
  227. sqlalchemy/testing/entities.py +117 -0
  228. sqlalchemy/testing/exclusions.py +435 -0
  229. sqlalchemy/testing/fixtures/__init__.py +28 -0
  230. sqlalchemy/testing/fixtures/base.py +366 -0
  231. sqlalchemy/testing/fixtures/mypy.py +312 -0
  232. sqlalchemy/testing/fixtures/orm.py +227 -0
  233. sqlalchemy/testing/fixtures/sql.py +503 -0
  234. sqlalchemy/testing/pickleable.py +155 -0
  235. sqlalchemy/testing/plugin/__init__.py +6 -0
  236. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  237. sqlalchemy/testing/plugin/plugin_base.py +779 -0
  238. sqlalchemy/testing/plugin/pytestplugin.py +868 -0
  239. sqlalchemy/testing/profiling.py +324 -0
  240. sqlalchemy/testing/provision.py +496 -0
  241. sqlalchemy/testing/requirements.py +1818 -0
  242. sqlalchemy/testing/schema.py +224 -0
  243. sqlalchemy/testing/suite/__init__.py +19 -0
  244. sqlalchemy/testing/suite/test_cte.py +211 -0
  245. sqlalchemy/testing/suite/test_ddl.py +389 -0
  246. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  247. sqlalchemy/testing/suite/test_dialect.py +740 -0
  248. sqlalchemy/testing/suite/test_insert.py +630 -0
  249. sqlalchemy/testing/suite/test_reflection.py +3225 -0
  250. sqlalchemy/testing/suite/test_results.py +502 -0
  251. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  252. sqlalchemy/testing/suite/test_select.py +1999 -0
  253. sqlalchemy/testing/suite/test_sequence.py +317 -0
  254. sqlalchemy/testing/suite/test_types.py +2141 -0
  255. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  256. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  257. sqlalchemy/testing/util.py +537 -0
  258. sqlalchemy/testing/warnings.py +52 -0
  259. sqlalchemy/types.py +76 -0
  260. sqlalchemy/util/__init__.py +160 -0
  261. sqlalchemy/util/_collections.py +715 -0
  262. sqlalchemy/util/_concurrency_py3k.py +288 -0
  263. sqlalchemy/util/_has_cy.py +40 -0
  264. sqlalchemy/util/_py_collections.py +541 -0
  265. sqlalchemy/util/compat.py +301 -0
  266. sqlalchemy/util/concurrency.py +108 -0
  267. sqlalchemy/util/deprecations.py +401 -0
  268. sqlalchemy/util/langhelpers.py +2218 -0
  269. sqlalchemy/util/preloaded.py +150 -0
  270. sqlalchemy/util/queue.py +322 -0
  271. sqlalchemy/util/tool_support.py +201 -0
  272. sqlalchemy/util/topological.py +120 -0
  273. sqlalchemy/util/typing.py +629 -0
@@ -0,0 +1,167 @@
1
+ # engine/util.py
2
+ # Copyright (C) 2005-2024 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,25 @@
1
+ # event/__init__.py
2
+ # Copyright (C) 2005-2024 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 .registry import _EventKey as _EventKey
24
+ from .registry import _ListenerFnType as _ListenerFnType
25
+ from .registry import EventTarget as EventTarget
@@ -0,0 +1,225 @@
1
+ # event/api.py
2
+ # Copyright (C) 2005-2024 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
+
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+ from typing import Callable
15
+
16
+ from .base import _registrars
17
+ from .registry import _ET
18
+ from .registry import _EventKey
19
+ from .registry import _ListenerFnType
20
+ from .. import exc
21
+ from .. import util
22
+
23
+
24
+ CANCEL = util.symbol("CANCEL")
25
+ NO_RETVAL = util.symbol("NO_RETVAL")
26
+
27
+
28
+ def _event_key(
29
+ target: _ET, identifier: str, fn: _ListenerFnType
30
+ ) -> _EventKey[_ET]:
31
+ for evt_cls in _registrars[identifier]:
32
+ tgt = evt_cls._accept_with(target, identifier)
33
+ if tgt is not None:
34
+ return _EventKey(target, identifier, fn, tgt)
35
+ else:
36
+ raise exc.InvalidRequestError(
37
+ "No such event '%s' for target '%s'" % (identifier, target)
38
+ )
39
+
40
+
41
+ def listen(
42
+ target: Any, identifier: str, fn: Callable[..., Any], *args: Any, **kw: Any
43
+ ) -> None:
44
+ """Register a listener function for the given target.
45
+
46
+ The :func:`.listen` function is part of the primary interface for the
47
+ SQLAlchemy event system, documented at :ref:`event_toplevel`.
48
+
49
+ e.g.::
50
+
51
+ from sqlalchemy import event
52
+ from sqlalchemy.schema import UniqueConstraint
53
+
54
+ def unique_constraint_name(const, table):
55
+ const.name = "uq_%s_%s" % (
56
+ table.name,
57
+ list(const.columns)[0].name
58
+ )
59
+ event.listen(
60
+ UniqueConstraint,
61
+ "after_parent_attach",
62
+ unique_constraint_name)
63
+
64
+ :param bool insert: The default behavior for event handlers is to append
65
+ the decorated user defined function to an internal list of registered
66
+ event listeners upon discovery. If a user registers a function with
67
+ ``insert=True``, SQLAlchemy will insert (prepend) the function to the
68
+ internal list upon discovery. This feature is not typically used or
69
+ recommended by the SQLAlchemy maintainers, but is provided to ensure
70
+ certain user defined functions can run before others, such as when
71
+ :ref:`Changing the sql_mode in MySQL <mysql_sql_mode>`.
72
+
73
+ :param bool named: When using named argument passing, the names listed in
74
+ the function argument specification will be used as keys in the
75
+ dictionary.
76
+ See :ref:`event_named_argument_styles`.
77
+
78
+ :param bool once: Private/Internal API usage. Deprecated. This parameter
79
+ would provide that an event function would run only once per given
80
+ target. It does not however imply automatic de-registration of the
81
+ listener function; associating an arbitrarily high number of listeners
82
+ without explicitly removing them will cause memory to grow unbounded even
83
+ if ``once=True`` is specified.
84
+
85
+ :param bool propagate: The ``propagate`` kwarg is available when working
86
+ with ORM instrumentation and mapping events.
87
+ See :class:`_ormevent.MapperEvents` and
88
+ :meth:`_ormevent.MapperEvents.before_mapper_configured` for examples.
89
+
90
+ :param bool retval: This flag applies only to specific event listeners,
91
+ each of which includes documentation explaining when it should be used.
92
+ By default, no listener ever requires a return value.
93
+ However, some listeners do support special behaviors for return values,
94
+ and include in their documentation that the ``retval=True`` flag is
95
+ necessary for a return value to be processed.
96
+
97
+ Event listener suites that make use of :paramref:`_event.listen.retval`
98
+ include :class:`_events.ConnectionEvents` and
99
+ :class:`_ormevent.AttributeEvents`.
100
+
101
+ .. note::
102
+
103
+ The :func:`.listen` function cannot be called at the same time
104
+ that the target event is being run. This has implications
105
+ for thread safety, and also means an event cannot be added
106
+ from inside the listener function for itself. The list of
107
+ events to be run are present inside of a mutable collection
108
+ that can't be changed during iteration.
109
+
110
+ Event registration and removal is not intended to be a "high
111
+ velocity" operation; it is a configurational operation. For
112
+ systems that need to quickly associate and deassociate with
113
+ events at high scale, use a mutable structure that is handled
114
+ from inside of a single listener.
115
+
116
+ .. seealso::
117
+
118
+ :func:`.listens_for`
119
+
120
+ :func:`.remove`
121
+
122
+ """
123
+
124
+ _event_key(target, identifier, fn).listen(*args, **kw)
125
+
126
+
127
+ def listens_for(
128
+ target: Any, identifier: str, *args: Any, **kw: Any
129
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
130
+ """Decorate a function as a listener for the given target + identifier.
131
+
132
+ The :func:`.listens_for` decorator is part of the primary interface for the
133
+ SQLAlchemy event system, documented at :ref:`event_toplevel`.
134
+
135
+ This function generally shares the same kwargs as :func:`.listen`.
136
+
137
+ e.g.::
138
+
139
+ from sqlalchemy import event
140
+ from sqlalchemy.schema import UniqueConstraint
141
+
142
+ @event.listens_for(UniqueConstraint, "after_parent_attach")
143
+ def unique_constraint_name(const, table):
144
+ const.name = "uq_%s_%s" % (
145
+ table.name,
146
+ list(const.columns)[0].name
147
+ )
148
+
149
+ A given function can also be invoked for only the first invocation
150
+ of the event using the ``once`` argument::
151
+
152
+ @event.listens_for(Mapper, "before_configure", once=True)
153
+ def on_config():
154
+ do_config()
155
+
156
+
157
+ .. warning:: The ``once`` argument does not imply automatic de-registration
158
+ of the listener function after it has been invoked a first time; a
159
+ listener entry will remain associated with the target object.
160
+ Associating an arbitrarily high number of listeners without explicitly
161
+ removing them will cause memory to grow unbounded even if ``once=True``
162
+ is specified.
163
+
164
+ .. seealso::
165
+
166
+ :func:`.listen` - general description of event listening
167
+
168
+ """
169
+
170
+ def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
171
+ listen(target, identifier, fn, *args, **kw)
172
+ return fn
173
+
174
+ return decorate
175
+
176
+
177
+ def remove(target: Any, identifier: str, fn: Callable[..., Any]) -> None:
178
+ """Remove an event listener.
179
+
180
+ The arguments here should match exactly those which were sent to
181
+ :func:`.listen`; all the event registration which proceeded as a result
182
+ of this call will be reverted by calling :func:`.remove` with the same
183
+ arguments.
184
+
185
+ e.g.::
186
+
187
+ # if a function was registered like this...
188
+ @event.listens_for(SomeMappedClass, "before_insert", propagate=True)
189
+ def my_listener_function(*arg):
190
+ pass
191
+
192
+ # ... it's removed like this
193
+ event.remove(SomeMappedClass, "before_insert", my_listener_function)
194
+
195
+ Above, the listener function associated with ``SomeMappedClass`` was also
196
+ propagated to subclasses of ``SomeMappedClass``; the :func:`.remove`
197
+ function will revert all of these operations.
198
+
199
+ .. note::
200
+
201
+ The :func:`.remove` function cannot be called at the same time
202
+ that the target event is being run. This has implications
203
+ for thread safety, and also means an event cannot be removed
204
+ from inside the listener function for itself. The list of
205
+ events to be run are present inside of a mutable collection
206
+ that can't be changed during iteration.
207
+
208
+ Event registration and removal is not intended to be a "high
209
+ velocity" operation; it is a configurational operation. For
210
+ systems that need to quickly associate and deassociate with
211
+ events at high scale, use a mutable structure that is handled
212
+ from inside of a single listener.
213
+
214
+ .. seealso::
215
+
216
+ :func:`.listen`
217
+
218
+ """
219
+ _event_key(target, identifier, fn).remove()
220
+
221
+
222
+ def contains(target: Any, identifier: str, fn: Callable[..., Any]) -> bool:
223
+ """Return True if the given target/ident/fn is set up to listen."""
224
+
225
+ return _event_key(target, identifier, fn).contains()