Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
events.py966 linesDownload Raw Back to engine
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
9from __future__ import annotations
10
11import typing
12from typing import Any
13from typing import Dict
14from typing import Optional
15from typing import Tuple
16from typing import Type
17from typing import Union
18
19from .base import Connection
20from .base import Engine
21from .interfaces import ConnectionEventsTarget
22from .interfaces import DBAPIConnection
23from .interfaces import DBAPICursor
24from .interfaces import Dialect
25from .. import event
26from .. import exc
27from ..util.typing import Literal
28
29if 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
44class 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
620class 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
966 
codekingpro/portable-devtools · Team Ai