SQLAlchemy 2.0.47__cp313-cp313t-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 (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-win32.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win32.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win32.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win32.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win32.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,965 @@
1
+ # engine/events.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
+
9
+ from __future__ import annotations
10
+
11
+ import typing
12
+ from typing import Any
13
+ from typing import Dict
14
+ from typing import Optional
15
+ from typing import Tuple
16
+ from typing import Type
17
+ from typing import Union
18
+
19
+ from .base import Connection
20
+ from .base import Engine
21
+ from .interfaces import ConnectionEventsTarget
22
+ from .interfaces import DBAPIConnection
23
+ from .interfaces import DBAPICursor
24
+ from .interfaces import Dialect
25
+ from .. import event
26
+ from .. import exc
27
+ from ..util.typing import Literal
28
+
29
+ if typing.TYPE_CHECKING:
30
+ from .interfaces import _CoreMultiExecuteParams
31
+ from .interfaces import _CoreSingleExecuteParams
32
+ from .interfaces import _DBAPIAnyExecuteParams
33
+ from .interfaces import _DBAPIMultiExecuteParams
34
+ from .interfaces import _DBAPISingleExecuteParams
35
+ from .interfaces import _ExecuteOptions
36
+ from .interfaces import ExceptionContext
37
+ from .interfaces import ExecutionContext
38
+ from .result import Result
39
+ from ..pool import ConnectionPoolEntry
40
+ from ..sql import Executable
41
+ from ..sql.elements import BindParameter
42
+
43
+
44
+ class ConnectionEvents(event.Events[ConnectionEventsTarget]):
45
+ """Available events for
46
+ :class:`_engine.Connection` and :class:`_engine.Engine`.
47
+
48
+ The methods here define the name of an event as well as the names of
49
+ members that are passed to listener functions.
50
+
51
+ An event listener can be associated with any
52
+ :class:`_engine.Connection` or :class:`_engine.Engine`
53
+ class or instance, such as an :class:`_engine.Engine`, e.g.::
54
+
55
+ from sqlalchemy import event, create_engine
56
+
57
+
58
+ def before_cursor_execute(
59
+ conn, cursor, statement, parameters, context, executemany
60
+ ):
61
+ log.info("Received statement: %s", statement)
62
+
63
+
64
+ engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
65
+ event.listen(engine, "before_cursor_execute", before_cursor_execute)
66
+
67
+ or with a specific :class:`_engine.Connection`::
68
+
69
+ with engine.begin() as conn:
70
+
71
+ @event.listens_for(conn, "before_cursor_execute")
72
+ def before_cursor_execute(
73
+ conn, cursor, statement, parameters, context, executemany
74
+ ):
75
+ log.info("Received statement: %s", statement)
76
+
77
+ When the methods are called with a `statement` parameter, such as in
78
+ :meth:`.after_cursor_execute` or :meth:`.before_cursor_execute`,
79
+ the statement is the exact SQL string that was prepared for transmission
80
+ to the DBAPI ``cursor`` in the connection's :class:`.Dialect`.
81
+
82
+ The :meth:`.before_execute` and :meth:`.before_cursor_execute`
83
+ events can also be established with the ``retval=True`` flag, which
84
+ allows modification of the statement and parameters to be sent
85
+ to the database. The :meth:`.before_cursor_execute` event is
86
+ particularly useful here to add ad-hoc string transformations, such
87
+ as comments, to all executions::
88
+
89
+ from sqlalchemy.engine import Engine
90
+ from sqlalchemy import event
91
+
92
+
93
+ @event.listens_for(Engine, "before_cursor_execute", retval=True)
94
+ def comment_sql_calls(
95
+ conn, cursor, statement, parameters, context, executemany
96
+ ):
97
+ statement = statement + " -- some comment"
98
+ return statement, parameters
99
+
100
+ .. note:: :class:`_events.ConnectionEvents` can be established on any
101
+ combination of :class:`_engine.Engine`, :class:`_engine.Connection`,
102
+ as well
103
+ as instances of each of those classes. Events across all
104
+ four scopes will fire off for a given instance of
105
+ :class:`_engine.Connection`. However, for performance reasons, the
106
+ :class:`_engine.Connection` object determines at instantiation time
107
+ whether or not its parent :class:`_engine.Engine` has event listeners
108
+ established. Event listeners added to the :class:`_engine.Engine`
109
+ class or to an instance of :class:`_engine.Engine`
110
+ *after* the instantiation
111
+ of a dependent :class:`_engine.Connection` instance will usually
112
+ *not* be available on that :class:`_engine.Connection` instance.
113
+ The newly
114
+ added listeners will instead take effect for
115
+ :class:`_engine.Connection`
116
+ instances created subsequent to those event listeners being
117
+ established on the parent :class:`_engine.Engine` class or instance.
118
+
119
+ :param retval=False: Applies to the :meth:`.before_execute` and
120
+ :meth:`.before_cursor_execute` events only. When True, the
121
+ user-defined event function must have a return value, which
122
+ is a tuple of parameters that replace the given statement
123
+ and parameters. See those methods for a description of
124
+ specific return arguments.
125
+
126
+ """ # noqa
127
+
128
+ _target_class_doc = "SomeEngine"
129
+ _dispatch_target = ConnectionEventsTarget
130
+
131
+ @classmethod
132
+ def _accept_with(
133
+ cls,
134
+ target: Union[ConnectionEventsTarget, Type[ConnectionEventsTarget]],
135
+ identifier: str,
136
+ ) -> Optional[Union[ConnectionEventsTarget, Type[ConnectionEventsTarget]]]:
137
+ default_dispatch = super()._accept_with(target, identifier)
138
+ if default_dispatch is None and hasattr(
139
+ target, "_no_async_engine_events"
140
+ ):
141
+ target._no_async_engine_events()
142
+
143
+ return default_dispatch
144
+
145
+ @classmethod
146
+ def _listen(
147
+ cls,
148
+ event_key: event._EventKey[ConnectionEventsTarget],
149
+ *,
150
+ retval: bool = False,
151
+ **kw: Any,
152
+ ) -> None:
153
+ target, identifier, fn = (
154
+ event_key.dispatch_target,
155
+ event_key.identifier,
156
+ event_key._listen_fn,
157
+ )
158
+ target._has_events = True
159
+
160
+ if not retval:
161
+ if identifier == "before_execute":
162
+ orig_fn = fn
163
+
164
+ def wrap_before_execute( # type: ignore
165
+ conn, clauseelement, multiparams, params, execution_options
166
+ ):
167
+ orig_fn(
168
+ conn,
169
+ clauseelement,
170
+ multiparams,
171
+ params,
172
+ execution_options,
173
+ )
174
+ return clauseelement, multiparams, params
175
+
176
+ fn = wrap_before_execute
177
+ elif identifier == "before_cursor_execute":
178
+ orig_fn = fn
179
+
180
+ def wrap_before_cursor_execute( # type: ignore
181
+ conn, cursor, statement, parameters, context, executemany
182
+ ):
183
+ orig_fn(
184
+ conn,
185
+ cursor,
186
+ statement,
187
+ parameters,
188
+ context,
189
+ executemany,
190
+ )
191
+ return statement, parameters
192
+
193
+ fn = wrap_before_cursor_execute
194
+ elif retval and identifier not in (
195
+ "before_execute",
196
+ "before_cursor_execute",
197
+ ):
198
+ raise exc.ArgumentError(
199
+ "Only the 'before_execute', "
200
+ "'before_cursor_execute' and 'handle_error' engine "
201
+ "event listeners accept the 'retval=True' "
202
+ "argument."
203
+ )
204
+ event_key.with_wrapper(fn).base_listen()
205
+
206
+ @event._legacy_signature(
207
+ "1.4",
208
+ ["conn", "clauseelement", "multiparams", "params"],
209
+ lambda conn, clauseelement, multiparams, params, execution_options: (
210
+ conn,
211
+ clauseelement,
212
+ multiparams,
213
+ params,
214
+ ),
215
+ )
216
+ def before_execute(
217
+ self,
218
+ conn: Connection,
219
+ clauseelement: Executable,
220
+ multiparams: _CoreMultiExecuteParams,
221
+ params: _CoreSingleExecuteParams,
222
+ execution_options: _ExecuteOptions,
223
+ ) -> Optional[
224
+ Tuple[Executable, _CoreMultiExecuteParams, _CoreSingleExecuteParams]
225
+ ]:
226
+ """Intercept high level execute() events, receiving uncompiled
227
+ SQL constructs and other objects prior to rendering into SQL.
228
+
229
+ This event is good for debugging SQL compilation issues as well
230
+ as early manipulation of the parameters being sent to the database,
231
+ as the parameter lists will be in a consistent format here.
232
+
233
+ This event can be optionally established with the ``retval=True``
234
+ flag. The ``clauseelement``, ``multiparams``, and ``params``
235
+ arguments should be returned as a three-tuple in this case::
236
+
237
+ @event.listens_for(Engine, "before_execute", retval=True)
238
+ def before_execute(conn, clauseelement, multiparams, params):
239
+ # do something with clauseelement, multiparams, params
240
+ return clauseelement, multiparams, params
241
+
242
+ :param conn: :class:`_engine.Connection` object
243
+ :param clauseelement: SQL expression construct, :class:`.Compiled`
244
+ instance, or string statement passed to
245
+ :meth:`_engine.Connection.execute`.
246
+ :param multiparams: Multiple parameter sets, a list of dictionaries.
247
+ :param params: Single parameter set, a single dictionary.
248
+ :param execution_options: dictionary of execution
249
+ options passed along with the statement, if any. This is a merge
250
+ of all options that will be used, including those of the statement,
251
+ the connection, and those passed in to the method itself for
252
+ the 2.0 style of execution.
253
+
254
+ .. versionadded: 1.4
255
+
256
+ .. seealso::
257
+
258
+ :meth:`.before_cursor_execute`
259
+
260
+ """
261
+
262
+ @event._legacy_signature(
263
+ "1.4",
264
+ ["conn", "clauseelement", "multiparams", "params", "result"],
265
+ lambda conn, clauseelement, multiparams, params, execution_options, result: ( # noqa
266
+ conn,
267
+ clauseelement,
268
+ multiparams,
269
+ params,
270
+ result,
271
+ ),
272
+ )
273
+ def after_execute(
274
+ self,
275
+ conn: Connection,
276
+ clauseelement: Executable,
277
+ multiparams: _CoreMultiExecuteParams,
278
+ params: _CoreSingleExecuteParams,
279
+ execution_options: _ExecuteOptions,
280
+ result: Result[Any],
281
+ ) -> None:
282
+ """Intercept high level execute() events after execute.
283
+
284
+
285
+ :param conn: :class:`_engine.Connection` object
286
+ :param clauseelement: SQL expression construct, :class:`.Compiled`
287
+ instance, or string statement passed to
288
+ :meth:`_engine.Connection.execute`.
289
+ :param multiparams: Multiple parameter sets, a list of dictionaries.
290
+ :param params: Single parameter set, a single dictionary.
291
+ :param execution_options: dictionary of execution
292
+ options passed along with the statement, if any. This is a merge
293
+ of all options that will be used, including those of the statement,
294
+ the connection, and those passed in to the method itself for
295
+ the 2.0 style of execution.
296
+
297
+ .. versionadded: 1.4
298
+
299
+ :param result: :class:`_engine.CursorResult` generated by the
300
+ execution.
301
+
302
+ """
303
+
304
+ def before_cursor_execute(
305
+ self,
306
+ conn: Connection,
307
+ cursor: DBAPICursor,
308
+ statement: str,
309
+ parameters: _DBAPIAnyExecuteParams,
310
+ context: Optional[ExecutionContext],
311
+ executemany: bool,
312
+ ) -> Optional[Tuple[str, _DBAPIAnyExecuteParams]]:
313
+ """Intercept low-level cursor execute() events before execution,
314
+ receiving the string SQL statement and DBAPI-specific parameter list to
315
+ be invoked against a cursor.
316
+
317
+ This event is a good choice for logging as well as late modifications
318
+ to the SQL string. It's less ideal for parameter modifications except
319
+ for those which are specific to a target backend.
320
+
321
+ This event can be optionally established with the ``retval=True``
322
+ flag. The ``statement`` and ``parameters`` arguments should be
323
+ returned as a two-tuple in this case::
324
+
325
+ @event.listens_for(Engine, "before_cursor_execute", retval=True)
326
+ def before_cursor_execute(
327
+ conn, cursor, statement, parameters, context, executemany
328
+ ):
329
+ # do something with statement, parameters
330
+ return statement, parameters
331
+
332
+ See the example at :class:`_events.ConnectionEvents`.
333
+
334
+ :param conn: :class:`_engine.Connection` object
335
+ :param cursor: DBAPI cursor object
336
+ :param statement: string SQL statement, as to be passed to the DBAPI
337
+ :param parameters: Dictionary, tuple, or list of parameters being
338
+ passed to the ``execute()`` or ``executemany()`` method of the
339
+ DBAPI ``cursor``. In some cases may be ``None``.
340
+ :param context: :class:`.ExecutionContext` object in use. May
341
+ be ``None``.
342
+ :param executemany: boolean, if ``True``, this is an ``executemany()``
343
+ call, if ``False``, this is an ``execute()`` call.
344
+
345
+ .. seealso::
346
+
347
+ :meth:`.before_execute`
348
+
349
+ :meth:`.after_cursor_execute`
350
+
351
+ """
352
+
353
+ def after_cursor_execute(
354
+ self,
355
+ conn: Connection,
356
+ cursor: DBAPICursor,
357
+ statement: str,
358
+ parameters: _DBAPIAnyExecuteParams,
359
+ context: Optional[ExecutionContext],
360
+ executemany: bool,
361
+ ) -> None:
362
+ """Intercept low-level cursor execute() events after execution.
363
+
364
+ :param conn: :class:`_engine.Connection` object
365
+ :param cursor: DBAPI cursor object. Will have results pending
366
+ if the statement was a SELECT, but these should not be consumed
367
+ as they will be needed by the :class:`_engine.CursorResult`.
368
+ :param statement: string SQL statement, as passed to the DBAPI
369
+ :param parameters: Dictionary, tuple, or list of parameters being
370
+ passed to the ``execute()`` or ``executemany()`` method of the
371
+ DBAPI ``cursor``. In some cases may be ``None``.
372
+ :param context: :class:`.ExecutionContext` object in use. May
373
+ be ``None``.
374
+ :param executemany: boolean, if ``True``, this is an ``executemany()``
375
+ call, if ``False``, this is an ``execute()`` call.
376
+
377
+ """
378
+
379
+ @event._legacy_signature(
380
+ "2.0", ["conn", "branch"], converter=lambda conn: (conn, False)
381
+ )
382
+ def engine_connect(self, conn: Connection) -> None:
383
+ """Intercept the creation of a new :class:`_engine.Connection`.
384
+
385
+ This event is called typically as the direct result of calling
386
+ the :meth:`_engine.Engine.connect` method.
387
+
388
+ It differs from the :meth:`_events.PoolEvents.connect` method, which
389
+ refers to the actual connection to a database at the DBAPI level;
390
+ a DBAPI connection may be pooled and reused for many operations.
391
+ In contrast, this event refers only to the production of a higher level
392
+ :class:`_engine.Connection` wrapper around such a DBAPI connection.
393
+
394
+ It also differs from the :meth:`_events.PoolEvents.checkout` event
395
+ in that it is specific to the :class:`_engine.Connection` object,
396
+ not the
397
+ DBAPI connection that :meth:`_events.PoolEvents.checkout` deals with,
398
+ although
399
+ this DBAPI connection is available here via the
400
+ :attr:`_engine.Connection.connection` attribute.
401
+ But note there can in fact
402
+ be multiple :meth:`_events.PoolEvents.checkout`
403
+ events within the lifespan
404
+ of a single :class:`_engine.Connection` object, if that
405
+ :class:`_engine.Connection`
406
+ is invalidated and re-established.
407
+
408
+ :param conn: :class:`_engine.Connection` object.
409
+
410
+ .. seealso::
411
+
412
+ :meth:`_events.PoolEvents.checkout`
413
+ the lower-level pool checkout event
414
+ for an individual DBAPI connection
415
+
416
+ """
417
+
418
+ def set_connection_execution_options(
419
+ self, conn: Connection, opts: Dict[str, Any]
420
+ ) -> None:
421
+ """Intercept when the :meth:`_engine.Connection.execution_options`
422
+ method is called.
423
+
424
+ This method is called after the new :class:`_engine.Connection`
425
+ has been
426
+ produced, with the newly updated execution options collection, but
427
+ before the :class:`.Dialect` has acted upon any of those new options.
428
+
429
+ Note that this method is not called when a new
430
+ :class:`_engine.Connection`
431
+ is produced which is inheriting execution options from its parent
432
+ :class:`_engine.Engine`; to intercept this condition, use the
433
+ :meth:`_events.ConnectionEvents.engine_connect` event.
434
+
435
+ :param conn: The newly copied :class:`_engine.Connection` object
436
+
437
+ :param opts: dictionary of options that were passed to the
438
+ :meth:`_engine.Connection.execution_options` method.
439
+ This dictionary may be modified in place to affect the ultimate
440
+ options which take effect.
441
+
442
+ .. versionadded:: 2.0 the ``opts`` dictionary may be modified
443
+ in place.
444
+
445
+
446
+ .. seealso::
447
+
448
+ :meth:`_events.ConnectionEvents.set_engine_execution_options`
449
+ - event
450
+ which is called when :meth:`_engine.Engine.execution_options`
451
+ is called.
452
+
453
+
454
+ """
455
+
456
+ def set_engine_execution_options(
457
+ self, engine: Engine, opts: Dict[str, Any]
458
+ ) -> None:
459
+ """Intercept when the :meth:`_engine.Engine.execution_options`
460
+ method is called.
461
+
462
+ The :meth:`_engine.Engine.execution_options` method produces a shallow
463
+ copy of the :class:`_engine.Engine` which stores the new options.
464
+ That new
465
+ :class:`_engine.Engine` is passed here.
466
+ A particular application of this
467
+ method is to add a :meth:`_events.ConnectionEvents.engine_connect`
468
+ event
469
+ handler to the given :class:`_engine.Engine`
470
+ which will perform some per-
471
+ :class:`_engine.Connection` task specific to these execution options.
472
+
473
+ :param conn: The newly copied :class:`_engine.Engine` object
474
+
475
+ :param opts: dictionary of options that were passed to the
476
+ :meth:`_engine.Connection.execution_options` method.
477
+ This dictionary may be modified in place to affect the ultimate
478
+ options which take effect.
479
+
480
+ .. versionadded:: 2.0 the ``opts`` dictionary may be modified
481
+ in place.
482
+
483
+ .. seealso::
484
+
485
+ :meth:`_events.ConnectionEvents.set_connection_execution_options`
486
+ - event
487
+ which is called when :meth:`_engine.Connection.execution_options`
488
+ is
489
+ called.
490
+
491
+ """
492
+
493
+ def engine_disposed(self, engine: Engine) -> None:
494
+ """Intercept when the :meth:`_engine.Engine.dispose` method is called.
495
+
496
+ The :meth:`_engine.Engine.dispose` method instructs the engine to
497
+ "dispose" of it's connection pool (e.g. :class:`_pool.Pool`), and
498
+ replaces it with a new one. Disposing of the old pool has the
499
+ effect that existing checked-in connections are closed. The new
500
+ pool does not establish any new connections until it is first used.
501
+
502
+ This event can be used to indicate that resources related to the
503
+ :class:`_engine.Engine` should also be cleaned up,
504
+ keeping in mind that the
505
+ :class:`_engine.Engine`
506
+ can still be used for new requests in which case
507
+ it re-acquires connection resources.
508
+
509
+ """
510
+
511
+ def begin(self, conn: Connection) -> None:
512
+ """Intercept begin() events.
513
+
514
+ :param conn: :class:`_engine.Connection` object
515
+
516
+ """
517
+
518
+ def rollback(self, conn: Connection) -> None:
519
+ """Intercept rollback() events, as initiated by a
520
+ :class:`.Transaction`.
521
+
522
+ Note that the :class:`_pool.Pool` also "auto-rolls back"
523
+ a DBAPI connection upon checkin, if the ``reset_on_return``
524
+ flag is set to its default value of ``'rollback'``.
525
+ To intercept this
526
+ rollback, use the :meth:`_events.PoolEvents.reset` hook.
527
+
528
+ :param conn: :class:`_engine.Connection` object
529
+
530
+ .. seealso::
531
+
532
+ :meth:`_events.PoolEvents.reset`
533
+
534
+ """
535
+
536
+ def commit(self, conn: Connection) -> None:
537
+ """Intercept commit() events, as initiated by a
538
+ :class:`.Transaction`.
539
+
540
+ Note that the :class:`_pool.Pool` may also "auto-commit"
541
+ a DBAPI connection upon checkin, if the ``reset_on_return``
542
+ flag is set to the value ``'commit'``. To intercept this
543
+ commit, use the :meth:`_events.PoolEvents.reset` hook.
544
+
545
+ :param conn: :class:`_engine.Connection` object
546
+ """
547
+
548
+ def savepoint(self, conn: Connection, name: str) -> None:
549
+ """Intercept savepoint() events.
550
+
551
+ :param conn: :class:`_engine.Connection` object
552
+ :param name: specified name used for the savepoint.
553
+
554
+ """
555
+
556
+ def rollback_savepoint(
557
+ self, conn: Connection, name: str, context: None
558
+ ) -> None:
559
+ """Intercept rollback_savepoint() events.
560
+
561
+ :param conn: :class:`_engine.Connection` object
562
+ :param name: specified name used for the savepoint.
563
+ :param context: not used
564
+
565
+ """
566
+ # TODO: deprecate "context"
567
+
568
+ def release_savepoint(
569
+ self, conn: Connection, name: str, context: None
570
+ ) -> None:
571
+ """Intercept release_savepoint() events.
572
+
573
+ :param conn: :class:`_engine.Connection` object
574
+ :param name: specified name used for the savepoint.
575
+ :param context: not used
576
+
577
+ """
578
+ # TODO: deprecate "context"
579
+
580
+ def begin_twophase(self, conn: Connection, xid: Any) -> None:
581
+ """Intercept begin_twophase() events.
582
+
583
+ :param conn: :class:`_engine.Connection` object
584
+ :param xid: two-phase XID identifier
585
+
586
+ """
587
+
588
+ def prepare_twophase(self, conn: Connection, xid: Any) -> None:
589
+ """Intercept prepare_twophase() events.
590
+
591
+ :param conn: :class:`_engine.Connection` object
592
+ :param xid: two-phase XID identifier
593
+ """
594
+
595
+ def rollback_twophase(
596
+ self, conn: Connection, xid: Any, is_prepared: bool
597
+ ) -> None:
598
+ """Intercept rollback_twophase() events.
599
+
600
+ :param conn: :class:`_engine.Connection` object
601
+ :param xid: two-phase XID identifier
602
+ :param is_prepared: boolean, indicates if
603
+ :meth:`.TwoPhaseTransaction.prepare` was called.
604
+
605
+ """
606
+
607
+ def commit_twophase(
608
+ self, conn: Connection, xid: Any, is_prepared: bool
609
+ ) -> None:
610
+ """Intercept commit_twophase() events.
611
+
612
+ :param conn: :class:`_engine.Connection` object
613
+ :param xid: two-phase XID identifier
614
+ :param is_prepared: boolean, indicates if
615
+ :meth:`.TwoPhaseTransaction.prepare` was called.
616
+
617
+ """
618
+
619
+
620
+ class DialectEvents(event.Events[Dialect]):
621
+ """event interface for execution-replacement functions.
622
+
623
+ These events allow direct instrumentation and replacement
624
+ of key dialect functions which interact with the DBAPI.
625
+
626
+ .. note::
627
+
628
+ :class:`.DialectEvents` hooks should be considered **semi-public**
629
+ and experimental.
630
+ These hooks are not for general use and are only for those situations
631
+ where intricate re-statement of DBAPI mechanics must be injected onto
632
+ an existing dialect. For general-use statement-interception events,
633
+ please use the :class:`_events.ConnectionEvents` interface.
634
+
635
+ .. seealso::
636
+
637
+ :meth:`_events.ConnectionEvents.before_cursor_execute`
638
+
639
+ :meth:`_events.ConnectionEvents.before_execute`
640
+
641
+ :meth:`_events.ConnectionEvents.after_cursor_execute`
642
+
643
+ :meth:`_events.ConnectionEvents.after_execute`
644
+
645
+ """
646
+
647
+ _target_class_doc = "SomeEngine"
648
+ _dispatch_target = Dialect
649
+
650
+ @classmethod
651
+ def _listen(
652
+ cls,
653
+ event_key: event._EventKey[Dialect],
654
+ *,
655
+ retval: bool = False,
656
+ **kw: Any,
657
+ ) -> None:
658
+ target = event_key.dispatch_target
659
+
660
+ target._has_events = True
661
+ event_key.base_listen()
662
+
663
+ @classmethod
664
+ def _accept_with(
665
+ cls,
666
+ target: Union[Engine, Type[Engine], Dialect, Type[Dialect]],
667
+ identifier: str,
668
+ ) -> Optional[Union[Dialect, Type[Dialect]]]:
669
+ if isinstance(target, type):
670
+ if issubclass(target, Engine):
671
+ return Dialect
672
+ elif issubclass(target, Dialect):
673
+ return target
674
+ elif isinstance(target, Engine):
675
+ return target.dialect
676
+ elif isinstance(target, Dialect):
677
+ return target
678
+ elif isinstance(target, Connection) and identifier == "handle_error":
679
+ raise exc.InvalidRequestError(
680
+ "The handle_error() event hook as of SQLAlchemy 2.0 is "
681
+ "established on the Dialect, and may only be applied to the "
682
+ "Engine as a whole or to a specific Dialect as a whole, "
683
+ "not on a per-Connection basis."
684
+ )
685
+ elif hasattr(target, "_no_async_engine_events"):
686
+ target._no_async_engine_events()
687
+ else:
688
+ return None
689
+
690
+ def handle_error(
691
+ self, exception_context: ExceptionContext
692
+ ) -> Optional[BaseException]:
693
+ r"""Intercept all exceptions processed by the
694
+ :class:`_engine.Dialect`, typically but not limited to those
695
+ emitted within the scope of a :class:`_engine.Connection`.
696
+
697
+ .. versionchanged:: 2.0 the :meth:`.DialectEvents.handle_error` event
698
+ is moved to the :class:`.DialectEvents` class, moved from the
699
+ :class:`.ConnectionEvents` class, so that it may also participate in
700
+ the "pre ping" operation configured with the
701
+ :paramref:`_sa.create_engine.pool_pre_ping` parameter. The event
702
+ remains registered by using the :class:`_engine.Engine` as the event
703
+ target, however note that using the :class:`_engine.Connection` as
704
+ an event target for :meth:`.DialectEvents.handle_error` is no longer
705
+ supported.
706
+
707
+ This includes all exceptions emitted by the DBAPI as well as
708
+ within SQLAlchemy's statement invocation process, including
709
+ encoding errors and other statement validation errors. Other areas
710
+ in which the event is invoked include transaction begin and end,
711
+ result row fetching, cursor creation.
712
+
713
+ Note that :meth:`.handle_error` may support new kinds of exceptions
714
+ and new calling scenarios at *any time*. Code which uses this
715
+ event must expect new calling patterns to be present in minor
716
+ releases.
717
+
718
+ To support the wide variety of members that correspond to an exception,
719
+ as well as to allow extensibility of the event without backwards
720
+ incompatibility, the sole argument received is an instance of
721
+ :class:`.ExceptionContext`. This object contains data members
722
+ representing detail about the exception.
723
+
724
+ Use cases supported by this hook include:
725
+
726
+ * read-only, low-level exception handling for logging and
727
+ debugging purposes
728
+ * Establishing whether a DBAPI connection error message indicates
729
+ that the database connection needs to be reconnected, including
730
+ for the "pre_ping" handler used by **some** dialects
731
+ * Establishing or disabling whether a connection or the owning
732
+ connection pool is invalidated or expired in response to a
733
+ specific exception
734
+ * exception re-writing
735
+
736
+ The hook is called while the cursor from the failed operation
737
+ (if any) is still open and accessible. Special cleanup operations
738
+ can be called on this cursor; SQLAlchemy will attempt to close
739
+ this cursor subsequent to this hook being invoked.
740
+
741
+ As of SQLAlchemy 2.0, the "pre_ping" handler enabled using the
742
+ :paramref:`_sa.create_engine.pool_pre_ping` parameter will also
743
+ participate in the :meth:`.handle_error` process, **for those dialects
744
+ that rely upon disconnect codes to detect database liveness**. Note
745
+ that some dialects such as psycopg, psycopg2, and most MySQL dialects
746
+ make use of a native ``ping()`` method supplied by the DBAPI which does
747
+ not make use of disconnect codes.
748
+
749
+ .. versionchanged:: 2.0.0 The :meth:`.DialectEvents.handle_error`
750
+ event hook participates in connection pool "pre-ping" operations.
751
+ Within this usage, the :attr:`.ExceptionContext.engine` attribute
752
+ will be ``None``, however the :class:`.Dialect` in use is always
753
+ available via the :attr:`.ExceptionContext.dialect` attribute.
754
+
755
+ .. versionchanged:: 2.0.5 Added :attr:`.ExceptionContext.is_pre_ping`
756
+ attribute which will be set to ``True`` when the
757
+ :meth:`.DialectEvents.handle_error` event hook is triggered within
758
+ a connection pool pre-ping operation.
759
+
760
+ .. versionchanged:: 2.0.5 An issue was repaired that allows for the
761
+ PostgreSQL ``psycopg`` and ``psycopg2`` drivers, as well as all
762
+ MySQL drivers, to properly participate in the
763
+ :meth:`.DialectEvents.handle_error` event hook during
764
+ connection pool "pre-ping" operations; previously, the
765
+ implementation was non-working for these drivers.
766
+
767
+
768
+ A handler function has two options for replacing
769
+ the SQLAlchemy-constructed exception into one that is user
770
+ defined. It can either raise this new exception directly, in
771
+ which case all further event listeners are bypassed and the
772
+ exception will be raised, after appropriate cleanup as taken
773
+ place::
774
+
775
+ @event.listens_for(Engine, "handle_error")
776
+ def handle_exception(context):
777
+ if isinstance(
778
+ context.original_exception, psycopg2.OperationalError
779
+ ) and "failed" in str(context.original_exception):
780
+ raise MySpecialException("failed operation")
781
+
782
+ .. warning:: Because the
783
+ :meth:`_events.DialectEvents.handle_error`
784
+ event specifically provides for exceptions to be re-thrown as
785
+ the ultimate exception raised by the failed statement,
786
+ **stack traces will be misleading** if the user-defined event
787
+ handler itself fails and throws an unexpected exception;
788
+ the stack trace may not illustrate the actual code line that
789
+ failed! It is advised to code carefully here and use
790
+ logging and/or inline debugging if unexpected exceptions are
791
+ occurring.
792
+
793
+ Alternatively, a "chained" style of event handling can be
794
+ used, by configuring the handler with the ``retval=True``
795
+ modifier and returning the new exception instance from the
796
+ function. In this case, event handling will continue onto the
797
+ next handler. The "chained" exception is available using
798
+ :attr:`.ExceptionContext.chained_exception`::
799
+
800
+ @event.listens_for(Engine, "handle_error", retval=True)
801
+ def handle_exception(context):
802
+ if (
803
+ context.chained_exception is not None
804
+ and "special" in context.chained_exception.message
805
+ ):
806
+ return MySpecialException(
807
+ "failed", cause=context.chained_exception
808
+ )
809
+
810
+ Handlers that return ``None`` may be used within the chain; when
811
+ a handler returns ``None``, the previous exception instance,
812
+ if any, is maintained as the current exception that is passed onto the
813
+ next handler.
814
+
815
+ When a custom exception is raised or returned, SQLAlchemy raises
816
+ this new exception as-is, it is not wrapped by any SQLAlchemy
817
+ object. If the exception is not a subclass of
818
+ :class:`sqlalchemy.exc.StatementError`,
819
+ certain features may not be available; currently this includes
820
+ the ORM's feature of adding a detail hint about "autoflush" to
821
+ exceptions raised within the autoflush process.
822
+
823
+ :param context: an :class:`.ExceptionContext` object. See this
824
+ class for details on all available members.
825
+
826
+
827
+ .. seealso::
828
+
829
+ :ref:`pool_new_disconnect_codes`
830
+
831
+ """
832
+
833
+ def do_connect(
834
+ self,
835
+ dialect: Dialect,
836
+ conn_rec: ConnectionPoolEntry,
837
+ cargs: Tuple[Any, ...],
838
+ cparams: Dict[str, Any],
839
+ ) -> Optional[DBAPIConnection]:
840
+ """Receive connection arguments before a connection is made.
841
+
842
+ This event is useful in that it allows the handler to manipulate the
843
+ cargs and/or cparams collections that control how the DBAPI
844
+ ``connect()`` function will be called. ``cargs`` will always be a
845
+ Python list that can be mutated in-place, and ``cparams`` a Python
846
+ dictionary that may also be mutated::
847
+
848
+ e = create_engine("postgresql+psycopg2://user@host/dbname")
849
+
850
+
851
+ @event.listens_for(e, "do_connect")
852
+ def receive_do_connect(dialect, conn_rec, cargs, cparams):
853
+ cparams["password"] = "some_password"
854
+
855
+ The event hook may also be used to override the call to ``connect()``
856
+ entirely, by returning a non-``None`` DBAPI connection object::
857
+
858
+ e = create_engine("postgresql+psycopg2://user@host/dbname")
859
+
860
+
861
+ @event.listens_for(e, "do_connect")
862
+ def receive_do_connect(dialect, conn_rec, cargs, cparams):
863
+ return psycopg2.connect(*cargs, **cparams)
864
+
865
+ .. seealso::
866
+
867
+ :ref:`custom_dbapi_args`
868
+
869
+ """
870
+
871
+ def do_executemany(
872
+ self,
873
+ cursor: DBAPICursor,
874
+ statement: str,
875
+ parameters: _DBAPIMultiExecuteParams,
876
+ context: ExecutionContext,
877
+ ) -> Optional[Literal[True]]:
878
+ """Receive a cursor to have executemany() called.
879
+
880
+ Return the value True to halt further events from invoking,
881
+ and to indicate that the cursor execution has already taken
882
+ place within the event handler.
883
+
884
+ """
885
+
886
+ def do_execute_no_params(
887
+ self, cursor: DBAPICursor, statement: str, context: ExecutionContext
888
+ ) -> Optional[Literal[True]]:
889
+ """Receive a cursor to have execute() with no parameters called.
890
+
891
+ Return the value True to halt further events from invoking,
892
+ and to indicate that the cursor execution has already taken
893
+ place within the event handler.
894
+
895
+ """
896
+
897
+ def do_execute(
898
+ self,
899
+ cursor: DBAPICursor,
900
+ statement: str,
901
+ parameters: _DBAPISingleExecuteParams,
902
+ context: ExecutionContext,
903
+ ) -> Optional[Literal[True]]:
904
+ """Receive a cursor to have execute() called.
905
+
906
+ Return the value True to halt further events from invoking,
907
+ and to indicate that the cursor execution has already taken
908
+ place within the event handler.
909
+
910
+ """
911
+
912
+ def do_setinputsizes(
913
+ self,
914
+ inputsizes: Dict[BindParameter[Any], Any],
915
+ cursor: DBAPICursor,
916
+ statement: str,
917
+ parameters: _DBAPIAnyExecuteParams,
918
+ context: ExecutionContext,
919
+ ) -> None:
920
+ """Receive the setinputsizes dictionary for possible modification.
921
+
922
+ This event is emitted in the case where the dialect makes use of the
923
+ DBAPI ``cursor.setinputsizes()`` method which passes information about
924
+ parameter binding for a particular statement. The given
925
+ ``inputsizes`` dictionary will contain :class:`.BindParameter` objects
926
+ as keys, linked to DBAPI-specific type objects as values; for
927
+ parameters that are not bound, they are added to the dictionary with
928
+ ``None`` as the value, which means the parameter will not be included
929
+ in the ultimate setinputsizes call. The event may be used to inspect
930
+ and/or log the datatypes that are being bound, as well as to modify the
931
+ dictionary in place. Parameters can be added, modified, or removed
932
+ from this dictionary. Callers will typically want to inspect the
933
+ :attr:`.BindParameter.type` attribute of the given bind objects in
934
+ order to make decisions about the DBAPI object.
935
+
936
+ After the event, the ``inputsizes`` dictionary is converted into
937
+ an appropriate datastructure to be passed to ``cursor.setinputsizes``;
938
+ either a list for a positional bound parameter execution style,
939
+ or a dictionary of string parameter keys to DBAPI type objects for
940
+ a named bound parameter execution style.
941
+
942
+ The setinputsizes hook overall is only used for dialects which include
943
+ the flag ``use_setinputsizes=True``. Dialects which use this
944
+ include python-oracledb, cx_Oracle, pg8000, asyncpg, and pyodbc
945
+ dialects.
946
+
947
+ .. note::
948
+
949
+ For use with pyodbc, the ``use_setinputsizes`` flag
950
+ must be passed to the dialect, e.g.::
951
+
952
+ create_engine("mssql+pyodbc://...", use_setinputsizes=True)
953
+
954
+ .. seealso::
955
+
956
+ :ref:`mssql_pyodbc_setinputsizes`
957
+
958
+ .. versionadded:: 1.2.9
959
+
960
+ .. seealso::
961
+
962
+ :ref:`cx_oracle_setinputsizes`
963
+
964
+ """
965
+ pass