]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Reorganize core event modules to avoid import cycles
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Jan 2020 23:41:48 +0000 (18:41 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Jan 2020 01:09:39 +0000 (20:09 -0500)
sqlalchemy.sql.naming was causing a full import of
engine due to the DDLEvents dependency.  Break out pool,
DDL and engine events into new modules specific to those
packages; resolve some other import cycles in Core also.

Change-Id: Ife8d217e58a26ab3605dd80ee70837968f957eaf

lib/sqlalchemy/__init__.py
lib/sqlalchemy/engine/__init__.py
lib/sqlalchemy/engine/events.py [new file with mode: 0644]
lib/sqlalchemy/events.py
lib/sqlalchemy/pool/__init__.py
lib/sqlalchemy/pool/events.py [new file with mode: 0644]
lib/sqlalchemy/sql/__init__.py
lib/sqlalchemy/sql/events.py [new file with mode: 0644]
lib/sqlalchemy/sql/naming.py
lib/sqlalchemy/sql/traversals.py

index bff833d8791090a91c50b804710cbf086fc422e9..2d67db24cf3ff71107b1a6187bc0b6cbb9455d9e 100644 (file)
@@ -6,6 +6,9 @@
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 from . import util as _util  # noqa
+from .engine import create_engine  # noqa
+from .engine import create_mock_engine  # noqa
+from .engine import engine_from_config  # noqa
 from .inspection import inspect  # noqa
 from .schema import BLANK_SCHEMA  # noqa
 from .schema import CheckConstraint  # noqa
@@ -118,14 +121,6 @@ from .types import UnicodeText  # noqa
 from .types import VARBINARY  # noqa
 from .types import VARCHAR  # noqa
 
-from .engine import create_engine  # noqa nosort
-from .engine import engine_from_config  # noqa nosort
-from .engine import create_mock_engine  # noqa nosort
-
-# these are placed last because there are
-# cross dependencies between sqlalchemy.sql and
-# sqlalchemy.engine that cause import cycles
-
 
 __version__ = "1.4.0b1"
 
index 0c5c6882c090fd36a140002e0972f262c06eb590..a129069a402e5aca2636943b6e97156c6a87cf82 100644 (file)
@@ -15,6 +15,7 @@ constructor ``create_engine()``.
 
 """
 
+from . import events  # noqa
 from . import util  # noqa
 from .base import Connection  # noqa
 from .base import Engine  # noqa
diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py
new file mode 100644 (file)
index 0000000..638048e
--- /dev/null
@@ -0,0 +1,789 @@
+# sqlalchemy/engine/events.py
+# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
+# <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+from .base import Engine
+from .interfaces import Connectable
+from .interfaces import Dialect
+from .. import event
+from .. import exc
+from .. import util
+
+
+class ConnectionEvents(event.Events):
+    """Available events for :class:`.Connectable`, which includes
+    :class:`.Connection` and :class:`.Engine`.
+
+    The methods here define the name of an event as well as the names of
+    members that are passed to listener functions.
+
+    An event listener can be associated with any :class:`.Connectable`
+    class or instance, such as an :class:`.Engine`, e.g.::
+
+        from sqlalchemy import event, create_engine
+
+        def before_cursor_execute(conn, cursor, statement, parameters, context,
+                                                        executemany):
+            log.info("Received statement: %s", statement)
+
+        engine = create_engine('postgresql://scott:tiger@localhost/test')
+        event.listen(engine, "before_cursor_execute", before_cursor_execute)
+
+    or with a specific :class:`.Connection`::
+
+        with engine.begin() as conn:
+            @event.listens_for(conn, 'before_cursor_execute')
+            def before_cursor_execute(conn, cursor, statement, parameters,
+                                            context, executemany):
+                log.info("Received statement: %s", statement)
+
+    When the methods are called with a `statement` parameter, such as in
+    :meth:`.after_cursor_execute`, :meth:`.before_cursor_execute` and
+    :meth:`.dbapi_error`, the statement is the exact SQL string that was
+    prepared for transmission to the DBAPI ``cursor`` in the connection's
+    :class:`.Dialect`.
+
+    The :meth:`.before_execute` and :meth:`.before_cursor_execute`
+    events can also be established with the ``retval=True`` flag, which
+    allows modification of the statement and parameters to be sent
+    to the database.  The :meth:`.before_cursor_execute` event is
+    particularly useful here to add ad-hoc string transformations, such
+    as comments, to all executions::
+
+        from sqlalchemy.engine import Engine
+        from sqlalchemy import event
+
+        @event.listens_for(Engine, "before_cursor_execute", retval=True)
+        def comment_sql_calls(conn, cursor, statement, parameters,
+                                            context, executemany):
+            statement = statement + " -- some comment"
+            return statement, parameters
+
+    .. note:: :class:`.ConnectionEvents` can be established on any
+       combination of :class:`.Engine`, :class:`.Connection`, as well
+       as instances of each of those classes.  Events across all
+       four scopes will fire off for a given instance of
+       :class:`.Connection`.  However, for performance reasons, the
+       :class:`.Connection` object determines at instantiation time
+       whether or not its parent :class:`.Engine` has event listeners
+       established.   Event listeners added to the :class:`.Engine`
+       class or to an instance of :class:`.Engine` *after* the instantiation
+       of a dependent :class:`.Connection` instance will usually
+       *not* be available on that :class:`.Connection` instance.  The newly
+       added listeners will instead take effect for :class:`.Connection`
+       instances created subsequent to those event listeners being
+       established on the parent :class:`.Engine` class or instance.
+
+    :param retval=False: Applies to the :meth:`.before_execute` and
+      :meth:`.before_cursor_execute` events only.  When True, the
+      user-defined event function must have a return value, which
+      is a tuple of parameters that replace the given statement
+      and parameters.  See those methods for a description of
+      specific return arguments.
+
+    """
+
+    _target_class_doc = "SomeEngine"
+    _dispatch_target = Connectable
+
+    @classmethod
+    def _listen(cls, event_key, retval=False):
+        target, identifier, fn = (
+            event_key.dispatch_target,
+            event_key.identifier,
+            event_key._listen_fn,
+        )
+
+        target._has_events = True
+
+        if not retval:
+            if identifier == "before_execute":
+                orig_fn = fn
+
+                def wrap_before_execute(
+                    conn, clauseelement, multiparams, params
+                ):
+                    orig_fn(conn, clauseelement, multiparams, params)
+                    return clauseelement, multiparams, params
+
+                fn = wrap_before_execute
+            elif identifier == "before_cursor_execute":
+                orig_fn = fn
+
+                def wrap_before_cursor_execute(
+                    conn, cursor, statement, parameters, context, executemany
+                ):
+                    orig_fn(
+                        conn,
+                        cursor,
+                        statement,
+                        parameters,
+                        context,
+                        executemany,
+                    )
+                    return statement, parameters
+
+                fn = wrap_before_cursor_execute
+        elif retval and identifier not in (
+            "before_execute",
+            "before_cursor_execute",
+            "handle_error",
+        ):
+            raise exc.ArgumentError(
+                "Only the 'before_execute', "
+                "'before_cursor_execute' and 'handle_error' engine "
+                "event listeners accept the 'retval=True' "
+                "argument."
+            )
+        event_key.with_wrapper(fn).base_listen()
+
+    def before_execute(self, conn, clauseelement, multiparams, params):
+        """Intercept high level execute() events, receiving uncompiled
+        SQL constructs and other objects prior to rendering into SQL.
+
+        This event is good for debugging SQL compilation issues as well
+        as early manipulation of the parameters being sent to the database,
+        as the parameter lists will be in a consistent format here.
+
+        This event can be optionally established with the ``retval=True``
+        flag.  The ``clauseelement``, ``multiparams``, and ``params``
+        arguments should be returned as a three-tuple in this case::
+
+            @event.listens_for(Engine, "before_execute", retval=True)
+            def before_execute(conn, clauseelement, multiparams, params):
+                # do something with clauseelement, multiparams, params
+                return clauseelement, multiparams, params
+
+        :param conn: :class:`.Connection` object
+        :param clauseelement: SQL expression construct, :class:`.Compiled`
+         instance, or string statement passed to :meth:`.Connection.execute`.
+        :param multiparams: Multiple parameter sets, a list of dictionaries.
+        :param params: Single parameter set, a single dictionary.
+
+        .. seealso::
+
+            :meth:`.before_cursor_execute`
+
+        """
+
+    def after_execute(self, conn, clauseelement, multiparams, params, result):
+        """Intercept high level execute() events after execute.
+
+
+        :param conn: :class:`.Connection` object
+        :param clauseelement: SQL expression construct, :class:`.Compiled`
+         instance, or string statement passed to :meth:`.Connection.execute`.
+        :param multiparams: Multiple parameter sets, a list of dictionaries.
+        :param params: Single parameter set, a single dictionary.
+        :param result: :class:`.ResultProxy` generated by the execution.
+
+        """
+
+    def before_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        """Intercept low-level cursor execute() events before execution,
+        receiving the string SQL statement and DBAPI-specific parameter list to
+        be invoked against a cursor.
+
+        This event is a good choice for logging as well as late modifications
+        to the SQL string.  It's less ideal for parameter modifications except
+        for those which are specific to a target backend.
+
+        This event can be optionally established with the ``retval=True``
+        flag.  The ``statement`` and ``parameters`` arguments should be
+        returned as a two-tuple in this case::
+
+            @event.listens_for(Engine, "before_cursor_execute", retval=True)
+            def before_cursor_execute(conn, cursor, statement,
+                            parameters, context, executemany):
+                # do something with statement, parameters
+                return statement, parameters
+
+        See the example at :class:`.ConnectionEvents`.
+
+        :param conn: :class:`.Connection` object
+        :param cursor: DBAPI cursor object
+        :param statement: string SQL statement, as to be passed to the DBAPI
+        :param parameters: Dictionary, tuple, or list of parameters being
+         passed to the ``execute()`` or ``executemany()`` method of the
+         DBAPI ``cursor``.  In some cases may be ``None``.
+        :param context: :class:`.ExecutionContext` object in use.  May
+         be ``None``.
+        :param executemany: boolean, if ``True``, this is an ``executemany()``
+         call, if ``False``, this is an ``execute()`` call.
+
+        .. seealso::
+
+            :meth:`.before_execute`
+
+            :meth:`.after_cursor_execute`
+
+        """
+
+    def after_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        """Intercept low-level cursor execute() events after execution.
+
+        :param conn: :class:`.Connection` object
+        :param cursor: DBAPI cursor object.  Will have results pending
+         if the statement was a SELECT, but these should not be consumed
+         as they will be needed by the :class:`.ResultProxy`.
+        :param statement: string SQL statement, as passed to the DBAPI
+        :param parameters: Dictionary, tuple, or list of parameters being
+         passed to the ``execute()`` or ``executemany()`` method of the
+         DBAPI ``cursor``.  In some cases may be ``None``.
+        :param context: :class:`.ExecutionContext` object in use.  May
+         be ``None``.
+        :param executemany: boolean, if ``True``, this is an ``executemany()``
+         call, if ``False``, this is an ``execute()`` call.
+
+        """
+
+    @util.deprecated(
+        "0.9",
+        "The :meth:`.ConnectionEvents.dbapi_error` "
+        "event is deprecated and will be removed in a future release. "
+        "Please refer to the :meth:`.ConnectionEvents.handle_error` "
+        "event.",
+    )
+    def dbapi_error(
+        self, conn, cursor, statement, parameters, context, exception
+    ):
+        """Intercept a raw DBAPI error.
+
+        This event is called with the DBAPI exception instance
+        received from the DBAPI itself, *before* SQLAlchemy wraps the
+        exception with it's own exception wrappers, and before any
+        other operations are performed on the DBAPI cursor; the
+        existing transaction remains in effect as well as any state
+        on the cursor.
+
+        The use case here is to inject low-level exception handling
+        into an :class:`.Engine`, typically for logging and
+        debugging purposes.
+
+        .. warning::
+
+            Code should **not** modify
+            any state or throw any exceptions here as this will
+            interfere with SQLAlchemy's cleanup and error handling
+            routines.  For exception modification, please refer to the
+            new :meth:`.ConnectionEvents.handle_error` event.
+
+        Subsequent to this hook, SQLAlchemy may attempt any
+        number of operations on the connection/cursor, including
+        closing the cursor, rolling back of the transaction in the
+        case of connectionless execution, and disposing of the entire
+        connection pool if a "disconnect" was detected.   The
+        exception is then wrapped in a SQLAlchemy DBAPI exception
+        wrapper and re-thrown.
+
+        :param conn: :class:`.Connection` object
+        :param cursor: DBAPI cursor object
+        :param statement: string SQL statement, as passed to the DBAPI
+        :param parameters: Dictionary, tuple, or list of parameters being
+         passed to the ``execute()`` or ``executemany()`` method of the
+         DBAPI ``cursor``.  In some cases may be ``None``.
+        :param context: :class:`.ExecutionContext` object in use.  May
+         be ``None``.
+        :param exception: The **unwrapped** exception emitted directly from the
+         DBAPI.  The class here is specific to the DBAPI module in use.
+
+        """
+
+    def handle_error(self, exception_context):
+        r"""Intercept all exceptions processed by the :class:`.Connection`.
+
+        This includes all exceptions emitted by the DBAPI as well as
+        within SQLAlchemy's statement invocation process, including
+        encoding errors and other statement validation errors.  Other areas
+        in which the event is invoked include transaction begin and end,
+        result row fetching, cursor creation.
+
+        Note that :meth:`.handle_error` may support new kinds of exceptions
+        and new calling scenarios at *any time*.  Code which uses this
+        event must expect new calling patterns to be present in minor
+        releases.
+
+        To support the wide variety of members that correspond to an exception,
+        as well as to allow extensibility of the event without backwards
+        incompatibility, the sole argument received is an instance of
+        :class:`.ExceptionContext`.   This object contains data members
+        representing detail about the exception.
+
+        Use cases supported by this hook include:
+
+        * read-only, low-level exception handling for logging and
+          debugging purposes
+        * exception re-writing
+        * Establishing or disabling whether a connection or the owning
+          connection pool is invalidated or expired in response to a
+          specific exception.
+
+        The hook is called while the cursor from the failed operation
+        (if any) is still open and accessible.   Special cleanup operations
+        can be called on this cursor; SQLAlchemy will attempt to close
+        this cursor subsequent to this hook being invoked.  If the connection
+        is in "autocommit" mode, the transaction also remains open within
+        the scope of this hook; the rollback of the per-statement transaction
+        also occurs after the hook is called.
+
+        For the common case of detecting a "disconnect" situation which
+        is not currently handled by the SQLAlchemy dialect, the
+        :attr:`.ExceptionContext.is_disconnect` flag can be set to True which
+        will cause the exception to be considered as a disconnect situation,
+        which typically results in the connection pool being invalidated::
+
+            @event.listens_for(Engine, "handle_error")
+            def handle_exception(context):
+                if isinstance(context.original_exception, pyodbc.Error):
+                    for code in (
+                        '08S01', '01002', '08003',
+                        '08007', '08S02', '08001', 'HYT00', 'HY010'):
+
+                        if code in str(context.original_exception):
+                            context.is_disconnect = True
+
+        A handler function has two options for replacing
+        the SQLAlchemy-constructed exception into one that is user
+        defined.   It can either raise this new exception directly, in
+        which case all further event listeners are bypassed and the
+        exception will be raised, after appropriate cleanup as taken
+        place::
+
+            @event.listens_for(Engine, "handle_error")
+            def handle_exception(context):
+                if isinstance(context.original_exception,
+                    psycopg2.OperationalError) and \
+                    "failed" in str(context.original_exception):
+                    raise MySpecialException("failed operation")
+
+        .. warning::  Because the :meth:`.ConnectionEvents.handle_error`
+           event specifically provides for exceptions to be re-thrown as
+           the ultimate exception raised by the failed statement,
+           **stack traces will be misleading** if the user-defined event
+           handler itself fails and throws an unexpected exception;
+           the stack trace may not illustrate the actual code line that
+           failed!  It is advised to code carefully here and use
+           logging and/or inline debugging if unexpected exceptions are
+           occurring.
+
+        Alternatively, a "chained" style of event handling can be
+        used, by configuring the handler with the ``retval=True``
+        modifier and returning the new exception instance from the
+        function.  In this case, event handling will continue onto the
+        next handler.   The "chained" exception is available using
+        :attr:`.ExceptionContext.chained_exception`::
+
+            @event.listens_for(Engine, "handle_error", retval=True)
+            def handle_exception(context):
+                if context.chained_exception is not None and \
+                    "special" in context.chained_exception.message:
+                    return MySpecialException("failed",
+                        cause=context.chained_exception)
+
+        Handlers that return ``None`` may be used within the chain; when
+        a handler returns ``None``, the previous exception instance,
+        if any, is maintained as the current exception that is passed onto the
+        next handler.
+
+        When a custom exception is raised or returned, SQLAlchemy raises
+        this new exception as-is, it is not wrapped by any SQLAlchemy
+        object.  If the exception is not a subclass of
+        :class:`sqlalchemy.exc.StatementError`,
+        certain features may not be available; currently this includes
+        the ORM's feature of adding a detail hint about "autoflush" to
+        exceptions raised within the autoflush process.
+
+        :param context: an :class:`.ExceptionContext` object.  See this
+         class for details on all available members.
+
+        .. versionadded:: 0.9.7 Added the
+            :meth:`.ConnectionEvents.handle_error` hook.
+
+        .. versionchanged:: 1.1 The :meth:`.handle_error` event will now
+           receive all exceptions that inherit from ``BaseException``,
+           including ``SystemExit`` and ``KeyboardInterrupt``.  The setting for
+           :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case and
+           the default for
+           :attr:`.ExceptionContext.invalidate_pool_on_disconnect` is
+           ``False``.
+
+        .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now
+           invoked when an :class:`.Engine` fails during the initial
+           call to :meth:`.Engine.connect`, as well as when a
+           :class:`.Connection` object encounters an error during a
+           reconnect operation.
+
+        .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is
+           not fired off when a dialect makes use of the
+           ``skip_user_error_events`` execution option.   This is used
+           by dialects which intend to catch SQLAlchemy-specific exceptions
+           within specific operations, such as when the MySQL dialect detects
+           a table not present within the ``has_table()`` dialect method.
+           Prior to 1.0.0, code which implements :meth:`.handle_error` needs
+           to ensure that exceptions thrown in these scenarios are re-raised
+           without modification.
+
+        """
+
+    def engine_connect(self, conn, branch):
+        """Intercept the creation of a new :class:`.Connection`.
+
+        This event is called typically as the direct result of calling
+        the :meth:`.Engine.connect` method.
+
+        It differs from the :meth:`.PoolEvents.connect` method, which
+        refers to the actual connection to a database at the DBAPI level;
+        a DBAPI connection may be pooled and reused for many operations.
+        In contrast, this event refers only to the production of a higher level
+        :class:`.Connection` wrapper around such a DBAPI connection.
+
+        It also differs from the :meth:`.PoolEvents.checkout` event
+        in that it is specific to the :class:`.Connection` object, not the
+        DBAPI connection that :meth:`.PoolEvents.checkout` deals with, although
+        this DBAPI connection is available here via the
+        :attr:`.Connection.connection` attribute.  But note there can in fact
+        be multiple :meth:`.PoolEvents.checkout` events within the lifespan
+        of a single :class:`.Connection` object, if that :class:`.Connection`
+        is invalidated and re-established.  There can also be multiple
+        :class:`.Connection` objects generated for the same already-checked-out
+        DBAPI connection, in the case that a "branch" of a :class:`.Connection`
+        is produced.
+
+        :param conn: :class:`.Connection` object.
+        :param branch: if True, this is a "branch" of an existing
+         :class:`.Connection`.  A branch is generated within the course
+         of a statement execution to invoke supplemental statements, most
+         typically to pre-execute a SELECT of a default value for the purposes
+         of an INSERT statement.
+
+        .. versionadded:: 0.9.0
+
+        .. seealso::
+
+            :ref:`pool_disconnects_pessimistic` - illustrates how to use
+            :meth:`.ConnectionEvents.engine_connect`
+            to transparently ensure pooled connections are connected to the
+            database.
+
+            :meth:`.PoolEvents.checkout` the lower-level pool checkout event
+            for an individual DBAPI connection
+
+            :meth:`.ConnectionEvents.set_connection_execution_options` - a copy
+            of a :class:`.Connection` is also made when the
+            :meth:`.Connection.execution_options` method is called.
+
+        """
+
+    def set_connection_execution_options(self, conn, opts):
+        """Intercept when the :meth:`.Connection.execution_options`
+        method is called.
+
+        This method is called after the new :class:`.Connection` has been
+        produced, with the newly updated execution options collection, but
+        before the :class:`.Dialect` has acted upon any of those new options.
+
+        Note that this method is not called when a new :class:`.Connection`
+        is produced which is inheriting execution options from its parent
+        :class:`.Engine`; to intercept this condition, use the
+        :meth:`.ConnectionEvents.engine_connect` event.
+
+        :param conn: The newly copied :class:`.Connection` object
+
+        :param opts: dictionary of options that were passed to the
+         :meth:`.Connection.execution_options` method.
+
+        .. versionadded:: 0.9.0
+
+        .. seealso::
+
+            :meth:`.ConnectionEvents.set_engine_execution_options` - event
+            which is called when :meth:`.Engine.execution_options` is called.
+
+
+        """
+
+    def set_engine_execution_options(self, engine, opts):
+        """Intercept when the :meth:`.Engine.execution_options`
+        method is called.
+
+        The :meth:`.Engine.execution_options` method produces a shallow
+        copy of the :class:`.Engine` which stores the new options.  That new
+        :class:`.Engine` is passed here.   A particular application of this
+        method is to add a :meth:`.ConnectionEvents.engine_connect` event
+        handler to the given :class:`.Engine` which will perform some per-
+        :class:`.Connection` task specific to these execution options.
+
+        :param conn: The newly copied :class:`.Engine` object
+
+        :param opts: dictionary of options that were passed to the
+         :meth:`.Connection.execution_options` method.
+
+        .. versionadded:: 0.9.0
+
+        .. seealso::
+
+            :meth:`.ConnectionEvents.set_connection_execution_options` - event
+            which is called when :meth:`.Connection.execution_options` is
+            called.
+
+        """
+
+    def engine_disposed(self, engine):
+        """Intercept when the :meth:`.Engine.dispose` method is called.
+
+        The :meth:`.Engine.dispose` method instructs the engine to
+        "dispose" of it's connection pool (e.g. :class:`.Pool`), and
+        replaces it with a new one.  Disposing of the old pool has the
+        effect that existing checked-in connections are closed.  The new
+        pool does not establish any new connections until it is first used.
+
+        This event can be used to indicate that resources related to the
+        :class:`.Engine` should also be cleaned up, keeping in mind that the
+        :class:`.Engine` can still be used for new requests in which case
+        it re-acquires connection resources.
+
+        .. versionadded:: 1.0.5
+
+        """
+
+    def begin(self, conn):
+        """Intercept begin() events.
+
+        :param conn: :class:`.Connection` object
+
+        """
+
+    def rollback(self, conn):
+        """Intercept rollback() events, as initiated by a
+        :class:`.Transaction`.
+
+        Note that the :class:`.Pool` also "auto-rolls back"
+        a DBAPI connection upon checkin, if the ``reset_on_return``
+        flag is set to its default value of ``'rollback'``.
+        To intercept this
+        rollback, use the :meth:`.PoolEvents.reset` hook.
+
+        :param conn: :class:`.Connection` object
+
+        .. seealso::
+
+            :meth:`.PoolEvents.reset`
+
+        """
+
+    def commit(self, conn):
+        """Intercept commit() events, as initiated by a
+        :class:`.Transaction`.
+
+        Note that the :class:`.Pool` may also "auto-commit"
+        a DBAPI connection upon checkin, if the ``reset_on_return``
+        flag is set to the value ``'commit'``.  To intercept this
+        commit, use the :meth:`.PoolEvents.reset` hook.
+
+        :param conn: :class:`.Connection` object
+        """
+
+    def savepoint(self, conn, name):
+        """Intercept savepoint() events.
+
+        :param conn: :class:`.Connection` object
+        :param name: specified name used for the savepoint.
+
+        """
+
+    def rollback_savepoint(self, conn, name, context):
+        """Intercept rollback_savepoint() events.
+
+        :param conn: :class:`.Connection` object
+        :param name: specified name used for the savepoint.
+        :param context: :class:`.ExecutionContext` in use.  May be ``None``.
+
+        """
+
+    def release_savepoint(self, conn, name, context):
+        """Intercept release_savepoint() events.
+
+        :param conn: :class:`.Connection` object
+        :param name: specified name used for the savepoint.
+        :param context: :class:`.ExecutionContext` in use.  May be ``None``.
+
+        """
+
+    def begin_twophase(self, conn, xid):
+        """Intercept begin_twophase() events.
+
+        :param conn: :class:`.Connection` object
+        :param xid: two-phase XID identifier
+
+        """
+
+    def prepare_twophase(self, conn, xid):
+        """Intercept prepare_twophase() events.
+
+        :param conn: :class:`.Connection` object
+        :param xid: two-phase XID identifier
+        """
+
+    def rollback_twophase(self, conn, xid, is_prepared):
+        """Intercept rollback_twophase() events.
+
+        :param conn: :class:`.Connection` object
+        :param xid: two-phase XID identifier
+        :param is_prepared: boolean, indicates if
+         :meth:`.TwoPhaseTransaction.prepare` was called.
+
+        """
+
+    def commit_twophase(self, conn, xid, is_prepared):
+        """Intercept commit_twophase() events.
+
+        :param conn: :class:`.Connection` object
+        :param xid: two-phase XID identifier
+        :param is_prepared: boolean, indicates if
+         :meth:`.TwoPhaseTransaction.prepare` was called.
+
+        """
+
+
+class DialectEvents(event.Events):
+    """event interface for execution-replacement functions.
+
+    These events allow direct instrumentation and replacement
+    of key dialect functions which interact with the DBAPI.
+
+    .. note::
+
+        :class:`.DialectEvents` hooks should be considered **semi-public**
+        and experimental.
+        These hooks are not for general use and are only for those situations
+        where intricate re-statement of DBAPI mechanics must be injected onto
+        an existing dialect.  For general-use statement-interception events,
+        please use the :class:`.ConnectionEvents` interface.
+
+    .. seealso::
+
+        :meth:`.ConnectionEvents.before_cursor_execute`
+
+        :meth:`.ConnectionEvents.before_execute`
+
+        :meth:`.ConnectionEvents.after_cursor_execute`
+
+        :meth:`.ConnectionEvents.after_execute`
+
+
+    .. versionadded:: 0.9.4
+
+    """
+
+    _target_class_doc = "SomeEngine"
+    _dispatch_target = Dialect
+
+    @classmethod
+    def _listen(cls, event_key, retval=False):
+        target = event_key.dispatch_target
+
+        target._has_events = True
+        event_key.base_listen()
+
+    @classmethod
+    def _accept_with(cls, target):
+        if isinstance(target, type):
+            if issubclass(target, Engine):
+                return Dialect
+            elif issubclass(target, Dialect):
+                return target
+        elif isinstance(target, Engine):
+            return target.dialect
+        else:
+            return target
+
+    def do_connect(self, dialect, conn_rec, cargs, cparams):
+        """Receive connection arguments before a connection is made.
+
+        Return a DBAPI connection to halt further events from invoking;
+        the returned connection will be used.
+
+        Alternatively, the event can manipulate the cargs and/or cparams
+        collections; cargs will always be a Python list that can be mutated
+        in-place and cparams a Python dictionary.  Return None to
+        allow control to pass to the next event handler and ultimately
+        to allow the dialect to connect normally, given the updated
+        arguments.
+
+        .. versionadded:: 1.0.3
+
+        """
+
+    def do_executemany(self, cursor, statement, parameters, context):
+        """Receive a cursor to have executemany() called.
+
+        Return the value True to halt further events from invoking,
+        and to indicate that the cursor execution has already taken
+        place within the event handler.
+
+        """
+
+    def do_execute_no_params(self, cursor, statement, context):
+        """Receive a cursor to have execute() with no parameters called.
+
+        Return the value True to halt further events from invoking,
+        and to indicate that the cursor execution has already taken
+        place within the event handler.
+
+        """
+
+    def do_execute(self, cursor, statement, parameters, context):
+        """Receive a cursor to have execute() called.
+
+        Return the value True to halt further events from invoking,
+        and to indicate that the cursor execution has already taken
+        place within the event handler.
+
+        """
+
+    def do_setinputsizes(
+        self, inputsizes, cursor, statement, parameters, context
+    ):
+        """Receive the setinputsizes dictionary for possible modification.
+
+        This event is emitted in the case where the dialect makes use of the
+        DBAPI ``cursor.setinputsizes()`` method which passes information about
+        parameter binding for a particular statement.   The given
+        ``inputsizes`` dictionary will contain :class:`.BindParameter` objects
+        as keys, linked to DBAPI-specific type objects as values; for
+        parameters that are not bound, they are added to the dictionary with
+        ``None`` as the value, which means the parameter will not be included
+        in the ultimate setinputsizes call.   The event may be used to inspect
+        and/or log the datatypes that are being bound, as well as to modify the
+        dictionary in place.  Parameters can be added, modified, or removed
+        from this dictionary.   Callers will typically want to inspect the
+        :attr:`.BindParameter.type` attribute of the given bind objects in
+        order to make decisions about the DBAPI object.
+
+        After the event, the ``inputsizes`` dictionary is converted into
+        an appropriate datastructure to be passed to ``cursor.setinputsizes``;
+        either a list for a positional bound parameter execution style,
+        or a dictionary of string parameter keys to DBAPI type objects for
+        a named bound parameter execution style.
+
+        Most dialects **do not use** this method at all; the only built-in
+        dialect which uses this hook is the cx_Oracle dialect.   The hook here
+        is made available so as to allow customization of how datatypes are set
+        up with the  cx_Oracle DBAPI.
+
+        .. versionadded:: 1.2.9
+
+        .. seealso::
+
+            :ref:`cx_oracle_setinputsizes`
+
+        """
+        pass
index 42f030a6413d1e1ca4f54a8c59f7963c3a0ef448..93ef43815bd12acfb82772c5761c534cba1d2e7c 100644 (file)
 
 """Core event interfaces."""
 
-from . import event
-from . import exc
-from . import util
-from .engine import Connectable
-from .engine import Dialect
-from .engine import Engine
-from .pool import Pool
-from .sql.base import SchemaEventTarget
-
-
-class DDLEvents(event.Events):
-    """
-    Define event listeners for schema objects,
-    that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
-    subclasses, including :class:`.MetaData`, :class:`.Table`,
-    :class:`.Column`.
-
-    :class:`.MetaData` and :class:`.Table` support events
-    specifically regarding when CREATE and DROP
-    DDL is emitted to the database.
-
-    Attachment events are also provided to customize
-    behavior whenever a child schema element is associated
-    with a parent, such as, when a :class:`.Column` is associated
-    with its :class:`.Table`, when a :class:`.ForeignKeyConstraint`
-    is associated with a :class:`.Table`, etc.
-
-    Example using the ``after_create`` event::
-
-        from sqlalchemy import event
-        from sqlalchemy import Table, Column, Metadata, Integer
-
-        m = MetaData()
-        some_table = Table('some_table', m, Column('data', Integer))
-
-        def after_create(target, connection, **kw):
-            connection.execute("ALTER TABLE %s SET name=foo_%s" %
-                                    (target.name, target.name))
-
-        event.listen(some_table, "after_create", after_create)
-
-    DDL events integrate closely with the
-    :class:`.DDL` class and the :class:`.DDLElement` hierarchy
-    of DDL clause constructs, which are themselves appropriate
-    as listener callables::
-
-        from sqlalchemy import DDL
-        event.listen(
-            some_table,
-            "after_create",
-            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s")
-        )
-
-    The methods here define the name of an event as well
-    as the names of members that are passed to listener
-    functions.
-
-    For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
-    will ensure that a given event handler is propagated to copies of the
-    object, which are made when using the :meth:`.Table.tometadata` method::
-
-        from sqlalchemy import DDL
-        event.listen(
-            some_table,
-            "after_create",
-            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
-            propagate=True
-        )
-
-        new_table = some_table.tometadata(new_metadata)
-
-    The above :class:`.DDL` object will also be associated with the
-    :class:`.Table` object represented by ``new_table``.
-
-    .. seealso::
-
-        :ref:`event_toplevel`
-
-        :class:`.DDLElement`
-
-        :class:`.DDL`
-
-        :ref:`schema_ddl_sequences`
-
-    """
-
-    _target_class_doc = "SomeSchemaClassOrObject"
-    _dispatch_target = SchemaEventTarget
-
-    def before_create(self, target, connection, **kw):
-        r"""Called before CREATE statements are emitted.
-
-        :param target: the :class:`.MetaData` or :class:`.Table`
-         object which is the target of the event.
-        :param connection: the :class:`.Connection` where the
-         CREATE statement or statements will be emitted.
-        :param \**kw: additional keyword arguments relevant
-         to the event.  The contents of this dictionary
-         may vary across releases, and include the
-         list of tables being generated for a metadata-level
-         event, the checkfirst flag, and other
-         elements used by internal events.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def after_create(self, target, connection, **kw):
-        r"""Called after CREATE statements are emitted.
-
-        :param target: the :class:`.MetaData` or :class:`.Table`
-         object which is the target of the event.
-        :param connection: the :class:`.Connection` where the
-         CREATE statement or statements have been emitted.
-        :param \**kw: additional keyword arguments relevant
-         to the event.  The contents of this dictionary
-         may vary across releases, and include the
-         list of tables being generated for a metadata-level
-         event, the checkfirst flag, and other
-         elements used by internal events.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def before_drop(self, target, connection, **kw):
-        r"""Called before DROP statements are emitted.
-
-        :param target: the :class:`.MetaData` or :class:`.Table`
-         object which is the target of the event.
-        :param connection: the :class:`.Connection` where the
-         DROP statement or statements will be emitted.
-        :param \**kw: additional keyword arguments relevant
-         to the event.  The contents of this dictionary
-         may vary across releases, and include the
-         list of tables being generated for a metadata-level
-         event, the checkfirst flag, and other
-         elements used by internal events.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def after_drop(self, target, connection, **kw):
-        r"""Called after DROP statements are emitted.
-
-        :param target: the :class:`.MetaData` or :class:`.Table`
-         object which is the target of the event.
-        :param connection: the :class:`.Connection` where the
-         DROP statement or statements have been emitted.
-        :param \**kw: additional keyword arguments relevant
-         to the event.  The contents of this dictionary
-         may vary across releases, and include the
-         list of tables being generated for a metadata-level
-         event, the checkfirst flag, and other
-         elements used by internal events.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def before_parent_attach(self, target, parent):
-        """Called before a :class:`.SchemaItem` is associated with
-        a parent :class:`.SchemaItem`.
-
-        :param target: the target object
-        :param parent: the parent to which the target is being attached.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def after_parent_attach(self, target, parent):
-        """Called after a :class:`.SchemaItem` is associated with
-        a parent :class:`.SchemaItem`.
-
-        :param target: the target object
-        :param parent: the parent to which the target is being attached.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-    def column_reflect(self, inspector, table, column_info):
-        """Called for each unit of 'column info' retrieved when
-        a :class:`.Table` is being reflected.
-
-        The dictionary of column information as returned by the
-        dialect is passed, and can be modified.  The dictionary
-        is that returned in each element of the list returned
-        by :meth:`.reflection.Inspector.get_columns`:
-
-            * ``name`` - the column's name
-
-            * ``type`` - the type of this column, which should be an instance
-              of :class:`~sqlalchemy.types.TypeEngine`
-
-            * ``nullable`` - boolean flag if the column is NULL or NOT NULL
-
-            * ``default`` - the column's server default value.  This is
-              normally specified as a plain string SQL expression, however the
-              event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
-              or :func:`.sql.expression.text` object as well.
-
-              .. versionchanged:: 1.1.6
-
-                    The :meth:`.DDLEvents.column_reflect` event allows a non
-                    string :class:`.FetchedValue`,
-                    :func:`.sql.expression.text`, or derived object to be
-                    specified as the value of ``default`` in the column
-                    dictionary.
-
-            * ``attrs``  - dict containing optional column attributes
-
-        The event is called before any action is taken against
-        this dictionary, and the contents can be modified.
-        The :class:`.Column` specific arguments ``info``, ``key``,
-        and ``quote`` can also be added to the dictionary and
-        will be passed to the constructor of :class:`.Column`.
-
-        Note that this event is only meaningful if either
-        associated with the :class:`.Table` class across the
-        board, e.g.::
-
-            from sqlalchemy.schema import Table
-            from sqlalchemy import event
-
-            def listen_for_reflect(inspector, table, column_info):
-                "receive a column_reflect event"
-                # ...
-
-            event.listen(
-                    Table,
-                    'column_reflect',
-                    listen_for_reflect)
-
-        ...or with a specific :class:`.Table` instance using
-        the ``listeners`` argument::
-
-            def listen_for_reflect(inspector, table, column_info):
-                "receive a column_reflect event"
-                # ...
-
-            t = Table(
-                'sometable',
-                autoload=True,
-                listeners=[
-                    ('column_reflect', listen_for_reflect)
-                ])
-
-        This because the reflection process initiated by ``autoload=True``
-        completes within the scope of the constructor for :class:`.Table`.
-
-        :func:`.event.listen` also accepts the ``propagate=True``
-        modifier for this event; when True, the listener function will
-        be established for any copies made of the target object,
-        i.e. those copies that are generated when
-        :meth:`.Table.tometadata` is used.
-
-        """
-
-
-class PoolEvents(event.Events):
-    """Available events for :class:`.Pool`.
-
-    The methods here define the name of an event as well
-    as the names of members that are passed to listener
-    functions.
-
-    e.g.::
-
-        from sqlalchemy import event
-
-        def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
-            "handle an on checkout event"
-
-        event.listen(Pool, 'checkout', my_on_checkout)
-
-    In addition to accepting the :class:`.Pool` class and
-    :class:`.Pool` instances, :class:`.PoolEvents` also accepts
-    :class:`.Engine` objects and the :class:`.Engine` class as
-    targets, which will be resolved to the ``.pool`` attribute of the
-    given engine or the :class:`.Pool` class::
-
-        engine = create_engine("postgresql://scott:tiger@localhost/test")
-
-        # will associate with engine.pool
-        event.listen(engine, 'checkout', my_on_checkout)
-
-    """
-
-    _target_class_doc = "SomeEngineOrPool"
-    _dispatch_target = Pool
-
-    @classmethod
-    def _accept_with(cls, target):
-        if isinstance(target, type):
-            if issubclass(target, Engine):
-                return Pool
-            elif issubclass(target, Pool):
-                return target
-        elif isinstance(target, Engine):
-            return target.pool
-        else:
-            return target
-
-    def connect(self, dbapi_connection, connection_record):
-        """Called at the moment a particular DBAPI connection is first
-        created for a given :class:`.Pool`.
-
-        This event allows one to capture the point directly after which
-        the DBAPI module-level ``.connect()`` method has been used in order
-        to produce a new DBAPI connection.
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        """
-
-    def first_connect(self, dbapi_connection, connection_record):
-        """Called exactly once for the first time a DBAPI connection is
-        checked out from a particular :class:`.Pool`.
-
-        The rationale for :meth:`.PoolEvents.first_connect` is to determine
-        information about a particular series of database connections based
-        on the settings used for all connections.  Since a particular
-        :class:`.Pool` refers to a single "creator" function (which in terms
-        of a :class:`.Engine` refers to the URL and connection options used),
-        it is typically valid to make observations about a single connection
-        that can be safely assumed to be valid about all subsequent
-        connections, such as the database version, the server and client
-        encoding settings, collation settings, and many others.
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        """
-
-    def checkout(self, dbapi_connection, connection_record, connection_proxy):
-        """Called when a connection is retrieved from the Pool.
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        :param connection_proxy: the :class:`._ConnectionFairy` object which
-          will proxy the public interface of the DBAPI connection for the
-          lifespan of the checkout.
-
-        If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
-        connection will be disposed and a fresh connection retrieved.
-        Processing of all checkout listeners will abort and restart
-        using the new connection.
-
-        .. seealso:: :meth:`.ConnectionEvents.engine_connect` - a similar event
-           which occurs upon creation of a new :class:`.Connection`.
-
-        """
-
-    def checkin(self, dbapi_connection, connection_record):
-        """Called when a connection returns to the pool.
-
-        Note that the connection may be closed, and may be None if the
-        connection has been invalidated.  ``checkin`` will not be called
-        for detached connections.  (They do not return to the pool.)
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        """
-
-    def reset(self, dbapi_connection, connection_record):
-        """Called before the "reset" action occurs for a pooled connection.
-
-        This event represents
-        when the ``rollback()`` method is called on the DBAPI connection
-        before it is returned to the pool.  The behavior of "reset" can
-        be controlled, including disabled, using the ``reset_on_return``
-        pool argument.
-
-
-        The :meth:`.PoolEvents.reset` event is usually followed by the
-        :meth:`.PoolEvents.checkin` event is called, except in those
-        cases where the connection is discarded immediately after reset.
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        .. seealso::
-
-            :meth:`.ConnectionEvents.rollback`
-
-            :meth:`.ConnectionEvents.commit`
-
-        """
-
-    def invalidate(self, dbapi_connection, connection_record, exception):
-        """Called when a DBAPI connection is to be "invalidated".
-
-        This event is called any time the :meth:`._ConnectionRecord.invalidate`
-        method is invoked, either from API usage or via "auto-invalidation",
-        without the ``soft`` flag.
-
-        The event occurs before a final attempt to call ``.close()`` on the
-        connection occurs.
-
-        :param dbapi_connection: a DBAPI connection.
-
-        :param connection_record: the :class:`._ConnectionRecord` managing the
-         DBAPI connection.
-
-        :param exception: the exception object corresponding to the reason
-         for this invalidation, if any.  May be ``None``.
-
-        .. versionadded:: 0.9.2 Added support for connection invalidation
-           listening.
-
-        .. seealso::
-
-            :ref:`pool_connection_invalidation`
-
-        """
-
-    def soft_invalidate(self, dbapi_connection, connection_record, exception):
-        """Called when a DBAPI connection is to be "soft invalidated".
-
-        This event is called any time the :meth:`._ConnectionRecord.invalidate`
-        method is invoked with the ``soft`` flag.
-
-        Soft invalidation refers to when the connection record that tracks
-        this connection will force a reconnect after the current connection
-        is checked in.   It does not actively close the dbapi_connection
-        at the point at which it is called.
-
-        .. versionadded:: 1.0.3
-
-        """
-
-    def close(self, dbapi_connection, connection_record):
-        """Called when a DBAPI connection is closed.
-
-        The event is emitted before the close occurs.
-
-        The close of a connection can fail; typically this is because
-        the connection is already closed.  If the close operation fails,
-        the connection is discarded.
-
-        The :meth:`.close` event corresponds to a connection that's still
-        associated with the pool. To intercept close events for detached
-        connections use :meth:`.close_detached`.
-
-        .. versionadded:: 1.1
-
-        """
-
-    def detach(self, dbapi_connection, connection_record):
-        """Called when a DBAPI connection is "detached" from a pool.
-
-        This event is emitted after the detach occurs.  The connection
-        is no longer associated with the given connection record.
-
-        .. versionadded:: 1.1
-
-        """
-
-    def close_detached(self, dbapi_connection):
-        """Called when a detached DBAPI connection is closed.
-
-        The event is emitted before the close occurs.
-
-        The close of a connection can fail; typically this is because
-        the connection is already closed.  If the close operation fails,
-        the connection is discarded.
-
-        .. versionadded:: 1.1
-
-        """
-
-
-class ConnectionEvents(event.Events):
-    """Available events for :class:`.Connectable`, which includes
-    :class:`.Connection` and :class:`.Engine`.
-
-    The methods here define the name of an event as well as the names of
-    members that are passed to listener functions.
-
-    An event listener can be associated with any :class:`.Connectable`
-    class or instance, such as an :class:`.Engine`, e.g.::
-
-        from sqlalchemy import event, create_engine
-
-        def before_cursor_execute(conn, cursor, statement, parameters, context,
-                                                        executemany):
-            log.info("Received statement: %s", statement)
-
-        engine = create_engine('postgresql://scott:tiger@localhost/test')
-        event.listen(engine, "before_cursor_execute", before_cursor_execute)
-
-    or with a specific :class:`.Connection`::
-
-        with engine.begin() as conn:
-            @event.listens_for(conn, 'before_cursor_execute')
-            def before_cursor_execute(conn, cursor, statement, parameters,
-                                            context, executemany):
-                log.info("Received statement: %s", statement)
-
-    When the methods are called with a `statement` parameter, such as in
-    :meth:`.after_cursor_execute`, :meth:`.before_cursor_execute` and
-    :meth:`.dbapi_error`, the statement is the exact SQL string that was
-    prepared for transmission to the DBAPI ``cursor`` in the connection's
-    :class:`.Dialect`.
-
-    The :meth:`.before_execute` and :meth:`.before_cursor_execute`
-    events can also be established with the ``retval=True`` flag, which
-    allows modification of the statement and parameters to be sent
-    to the database.  The :meth:`.before_cursor_execute` event is
-    particularly useful here to add ad-hoc string transformations, such
-    as comments, to all executions::
-
-        from sqlalchemy.engine import Engine
-        from sqlalchemy import event
-
-        @event.listens_for(Engine, "before_cursor_execute", retval=True)
-        def comment_sql_calls(conn, cursor, statement, parameters,
-                                            context, executemany):
-            statement = statement + " -- some comment"
-            return statement, parameters
-
-    .. note:: :class:`.ConnectionEvents` can be established on any
-       combination of :class:`.Engine`, :class:`.Connection`, as well
-       as instances of each of those classes.  Events across all
-       four scopes will fire off for a given instance of
-       :class:`.Connection`.  However, for performance reasons, the
-       :class:`.Connection` object determines at instantiation time
-       whether or not its parent :class:`.Engine` has event listeners
-       established.   Event listeners added to the :class:`.Engine`
-       class or to an instance of :class:`.Engine` *after* the instantiation
-       of a dependent :class:`.Connection` instance will usually
-       *not* be available on that :class:`.Connection` instance.  The newly
-       added listeners will instead take effect for :class:`.Connection`
-       instances created subsequent to those event listeners being
-       established on the parent :class:`.Engine` class or instance.
-
-    :param retval=False: Applies to the :meth:`.before_execute` and
-      :meth:`.before_cursor_execute` events only.  When True, the
-      user-defined event function must have a return value, which
-      is a tuple of parameters that replace the given statement
-      and parameters.  See those methods for a description of
-      specific return arguments.
-
-    """
-
-    _target_class_doc = "SomeEngine"
-    _dispatch_target = Connectable
-
-    @classmethod
-    def _listen(cls, event_key, retval=False):
-        target, identifier, fn = (
-            event_key.dispatch_target,
-            event_key.identifier,
-            event_key._listen_fn,
-        )
-
-        target._has_events = True
-
-        if not retval:
-            if identifier == "before_execute":
-                orig_fn = fn
-
-                def wrap_before_execute(
-                    conn, clauseelement, multiparams, params
-                ):
-                    orig_fn(conn, clauseelement, multiparams, params)
-                    return clauseelement, multiparams, params
-
-                fn = wrap_before_execute
-            elif identifier == "before_cursor_execute":
-                orig_fn = fn
-
-                def wrap_before_cursor_execute(
-                    conn, cursor, statement, parameters, context, executemany
-                ):
-                    orig_fn(
-                        conn,
-                        cursor,
-                        statement,
-                        parameters,
-                        context,
-                        executemany,
-                    )
-                    return statement, parameters
-
-                fn = wrap_before_cursor_execute
-        elif retval and identifier not in (
-            "before_execute",
-            "before_cursor_execute",
-            "handle_error",
-        ):
-            raise exc.ArgumentError(
-                "Only the 'before_execute', "
-                "'before_cursor_execute' and 'handle_error' engine "
-                "event listeners accept the 'retval=True' "
-                "argument."
-            )
-        event_key.with_wrapper(fn).base_listen()
-
-    def before_execute(self, conn, clauseelement, multiparams, params):
-        """Intercept high level execute() events, receiving uncompiled
-        SQL constructs and other objects prior to rendering into SQL.
-
-        This event is good for debugging SQL compilation issues as well
-        as early manipulation of the parameters being sent to the database,
-        as the parameter lists will be in a consistent format here.
-
-        This event can be optionally established with the ``retval=True``
-        flag.  The ``clauseelement``, ``multiparams``, and ``params``
-        arguments should be returned as a three-tuple in this case::
-
-            @event.listens_for(Engine, "before_execute", retval=True)
-            def before_execute(conn, clauseelement, multiparams, params):
-                # do something with clauseelement, multiparams, params
-                return clauseelement, multiparams, params
-
-        :param conn: :class:`.Connection` object
-        :param clauseelement: SQL expression construct, :class:`.Compiled`
-         instance, or string statement passed to :meth:`.Connection.execute`.
-        :param multiparams: Multiple parameter sets, a list of dictionaries.
-        :param params: Single parameter set, a single dictionary.
-
-        .. seealso::
-
-            :meth:`.before_cursor_execute`
-
-        """
-
-    def after_execute(self, conn, clauseelement, multiparams, params, result):
-        """Intercept high level execute() events after execute.
-
-
-        :param conn: :class:`.Connection` object
-        :param clauseelement: SQL expression construct, :class:`.Compiled`
-         instance, or string statement passed to :meth:`.Connection.execute`.
-        :param multiparams: Multiple parameter sets, a list of dictionaries.
-        :param params: Single parameter set, a single dictionary.
-        :param result: :class:`.ResultProxy` generated by the execution.
-
-        """
-
-    def before_cursor_execute(
-        self, conn, cursor, statement, parameters, context, executemany
-    ):
-        """Intercept low-level cursor execute() events before execution,
-        receiving the string SQL statement and DBAPI-specific parameter list to
-        be invoked against a cursor.
-
-        This event is a good choice for logging as well as late modifications
-        to the SQL string.  It's less ideal for parameter modifications except
-        for those which are specific to a target backend.
-
-        This event can be optionally established with the ``retval=True``
-        flag.  The ``statement`` and ``parameters`` arguments should be
-        returned as a two-tuple in this case::
-
-            @event.listens_for(Engine, "before_cursor_execute", retval=True)
-            def before_cursor_execute(conn, cursor, statement,
-                            parameters, context, executemany):
-                # do something with statement, parameters
-                return statement, parameters
-
-        See the example at :class:`.ConnectionEvents`.
-
-        :param conn: :class:`.Connection` object
-        :param cursor: DBAPI cursor object
-        :param statement: string SQL statement, as to be passed to the DBAPI
-        :param parameters: Dictionary, tuple, or list of parameters being
-         passed to the ``execute()`` or ``executemany()`` method of the
-         DBAPI ``cursor``.  In some cases may be ``None``.
-        :param context: :class:`.ExecutionContext` object in use.  May
-         be ``None``.
-        :param executemany: boolean, if ``True``, this is an ``executemany()``
-         call, if ``False``, this is an ``execute()`` call.
-
-        .. seealso::
-
-            :meth:`.before_execute`
-
-            :meth:`.after_cursor_execute`
-
-        """
-
-    def after_cursor_execute(
-        self, conn, cursor, statement, parameters, context, executemany
-    ):
-        """Intercept low-level cursor execute() events after execution.
-
-        :param conn: :class:`.Connection` object
-        :param cursor: DBAPI cursor object.  Will have results pending
-         if the statement was a SELECT, but these should not be consumed
-         as they will be needed by the :class:`.ResultProxy`.
-        :param statement: string SQL statement, as passed to the DBAPI
-        :param parameters: Dictionary, tuple, or list of parameters being
-         passed to the ``execute()`` or ``executemany()`` method of the
-         DBAPI ``cursor``.  In some cases may be ``None``.
-        :param context: :class:`.ExecutionContext` object in use.  May
-         be ``None``.
-        :param executemany: boolean, if ``True``, this is an ``executemany()``
-         call, if ``False``, this is an ``execute()`` call.
-
-        """
-
-    @util.deprecated(
-        "0.9",
-        "The :meth:`.ConnectionEvents.dbapi_error` "
-        "event is deprecated and will be removed in a future release. "
-        "Please refer to the :meth:`.ConnectionEvents.handle_error` "
-        "event.",
-    )
-    def dbapi_error(
-        self, conn, cursor, statement, parameters, context, exception
-    ):
-        """Intercept a raw DBAPI error.
-
-        This event is called with the DBAPI exception instance
-        received from the DBAPI itself, *before* SQLAlchemy wraps the
-        exception with it's own exception wrappers, and before any
-        other operations are performed on the DBAPI cursor; the
-        existing transaction remains in effect as well as any state
-        on the cursor.
-
-        The use case here is to inject low-level exception handling
-        into an :class:`.Engine`, typically for logging and
-        debugging purposes.
-
-        .. warning::
-
-            Code should **not** modify
-            any state or throw any exceptions here as this will
-            interfere with SQLAlchemy's cleanup and error handling
-            routines.  For exception modification, please refer to the
-            new :meth:`.ConnectionEvents.handle_error` event.
-
-        Subsequent to this hook, SQLAlchemy may attempt any
-        number of operations on the connection/cursor, including
-        closing the cursor, rolling back of the transaction in the
-        case of connectionless execution, and disposing of the entire
-        connection pool if a "disconnect" was detected.   The
-        exception is then wrapped in a SQLAlchemy DBAPI exception
-        wrapper and re-thrown.
-
-        :param conn: :class:`.Connection` object
-        :param cursor: DBAPI cursor object
-        :param statement: string SQL statement, as passed to the DBAPI
-        :param parameters: Dictionary, tuple, or list of parameters being
-         passed to the ``execute()`` or ``executemany()`` method of the
-         DBAPI ``cursor``.  In some cases may be ``None``.
-        :param context: :class:`.ExecutionContext` object in use.  May
-         be ``None``.
-        :param exception: The **unwrapped** exception emitted directly from the
-         DBAPI.  The class here is specific to the DBAPI module in use.
-
-        """
-
-    def handle_error(self, exception_context):
-        r"""Intercept all exceptions processed by the :class:`.Connection`.
-
-        This includes all exceptions emitted by the DBAPI as well as
-        within SQLAlchemy's statement invocation process, including
-        encoding errors and other statement validation errors.  Other areas
-        in which the event is invoked include transaction begin and end,
-        result row fetching, cursor creation.
-
-        Note that :meth:`.handle_error` may support new kinds of exceptions
-        and new calling scenarios at *any time*.  Code which uses this
-        event must expect new calling patterns to be present in minor
-        releases.
-
-        To support the wide variety of members that correspond to an exception,
-        as well as to allow extensibility of the event without backwards
-        incompatibility, the sole argument received is an instance of
-        :class:`.ExceptionContext`.   This object contains data members
-        representing detail about the exception.
-
-        Use cases supported by this hook include:
-
-        * read-only, low-level exception handling for logging and
-          debugging purposes
-        * exception re-writing
-        * Establishing or disabling whether a connection or the owning
-          connection pool is invalidated or expired in response to a
-          specific exception.
-
-        The hook is called while the cursor from the failed operation
-        (if any) is still open and accessible.   Special cleanup operations
-        can be called on this cursor; SQLAlchemy will attempt to close
-        this cursor subsequent to this hook being invoked.  If the connection
-        is in "autocommit" mode, the transaction also remains open within
-        the scope of this hook; the rollback of the per-statement transaction
-        also occurs after the hook is called.
-
-        For the common case of detecting a "disconnect" situation which
-        is not currently handled by the SQLAlchemy dialect, the
-        :attr:`.ExceptionContext.is_disconnect` flag can be set to True which
-        will cause the exception to be considered as a disconnect situation,
-        which typically results in the connection pool being invalidated::
-
-            @event.listens_for(Engine, "handle_error")
-            def handle_exception(context):
-                if isinstance(context.original_exception, pyodbc.Error):
-                    for code in (
-                        '08S01', '01002', '08003',
-                        '08007', '08S02', '08001', 'HYT00', 'HY010'):
-
-                        if code in str(context.original_exception):
-                            context.is_disconnect = True
-
-        A handler function has two options for replacing
-        the SQLAlchemy-constructed exception into one that is user
-        defined.   It can either raise this new exception directly, in
-        which case all further event listeners are bypassed and the
-        exception will be raised, after appropriate cleanup as taken
-        place::
-
-            @event.listens_for(Engine, "handle_error")
-            def handle_exception(context):
-                if isinstance(context.original_exception,
-                    psycopg2.OperationalError) and \
-                    "failed" in str(context.original_exception):
-                    raise MySpecialException("failed operation")
-
-        .. warning::  Because the :meth:`.ConnectionEvents.handle_error`
-           event specifically provides for exceptions to be re-thrown as
-           the ultimate exception raised by the failed statement,
-           **stack traces will be misleading** if the user-defined event
-           handler itself fails and throws an unexpected exception;
-           the stack trace may not illustrate the actual code line that
-           failed!  It is advised to code carefully here and use
-           logging and/or inline debugging if unexpected exceptions are
-           occurring.
-
-        Alternatively, a "chained" style of event handling can be
-        used, by configuring the handler with the ``retval=True``
-        modifier and returning the new exception instance from the
-        function.  In this case, event handling will continue onto the
-        next handler.   The "chained" exception is available using
-        :attr:`.ExceptionContext.chained_exception`::
-
-            @event.listens_for(Engine, "handle_error", retval=True)
-            def handle_exception(context):
-                if context.chained_exception is not None and \
-                    "special" in context.chained_exception.message:
-                    return MySpecialException("failed",
-                        cause=context.chained_exception)
-
-        Handlers that return ``None`` may be used within the chain; when
-        a handler returns ``None``, the previous exception instance,
-        if any, is maintained as the current exception that is passed onto the
-        next handler.
-
-        When a custom exception is raised or returned, SQLAlchemy raises
-        this new exception as-is, it is not wrapped by any SQLAlchemy
-        object.  If the exception is not a subclass of
-        :class:`sqlalchemy.exc.StatementError`,
-        certain features may not be available; currently this includes
-        the ORM's feature of adding a detail hint about "autoflush" to
-        exceptions raised within the autoflush process.
-
-        :param context: an :class:`.ExceptionContext` object.  See this
-         class for details on all available members.
-
-        .. versionadded:: 0.9.7 Added the
-            :meth:`.ConnectionEvents.handle_error` hook.
-
-        .. versionchanged:: 1.1 The :meth:`.handle_error` event will now
-           receive all exceptions that inherit from ``BaseException``,
-           including ``SystemExit`` and ``KeyboardInterrupt``.  The setting for
-           :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case and
-           the default for
-           :attr:`.ExceptionContext.invalidate_pool_on_disconnect` is
-           ``False``.
-
-        .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now
-           invoked when an :class:`.Engine` fails during the initial
-           call to :meth:`.Engine.connect`, as well as when a
-           :class:`.Connection` object encounters an error during a
-           reconnect operation.
-
-        .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is
-           not fired off when a dialect makes use of the
-           ``skip_user_error_events`` execution option.   This is used
-           by dialects which intend to catch SQLAlchemy-specific exceptions
-           within specific operations, such as when the MySQL dialect detects
-           a table not present within the ``has_table()`` dialect method.
-           Prior to 1.0.0, code which implements :meth:`.handle_error` needs
-           to ensure that exceptions thrown in these scenarios are re-raised
-           without modification.
-
-        """
-
-    def engine_connect(self, conn, branch):
-        """Intercept the creation of a new :class:`.Connection`.
-
-        This event is called typically as the direct result of calling
-        the :meth:`.Engine.connect` method.
-
-        It differs from the :meth:`.PoolEvents.connect` method, which
-        refers to the actual connection to a database at the DBAPI level;
-        a DBAPI connection may be pooled and reused for many operations.
-        In contrast, this event refers only to the production of a higher level
-        :class:`.Connection` wrapper around such a DBAPI connection.
-
-        It also differs from the :meth:`.PoolEvents.checkout` event
-        in that it is specific to the :class:`.Connection` object, not the
-        DBAPI connection that :meth:`.PoolEvents.checkout` deals with, although
-        this DBAPI connection is available here via the
-        :attr:`.Connection.connection` attribute.  But note there can in fact
-        be multiple :meth:`.PoolEvents.checkout` events within the lifespan
-        of a single :class:`.Connection` object, if that :class:`.Connection`
-        is invalidated and re-established.  There can also be multiple
-        :class:`.Connection` objects generated for the same already-checked-out
-        DBAPI connection, in the case that a "branch" of a :class:`.Connection`
-        is produced.
-
-        :param conn: :class:`.Connection` object.
-        :param branch: if True, this is a "branch" of an existing
-         :class:`.Connection`.  A branch is generated within the course
-         of a statement execution to invoke supplemental statements, most
-         typically to pre-execute a SELECT of a default value for the purposes
-         of an INSERT statement.
-
-        .. versionadded:: 0.9.0
-
-        .. seealso::
-
-            :ref:`pool_disconnects_pessimistic` - illustrates how to use
-            :meth:`.ConnectionEvents.engine_connect`
-            to transparently ensure pooled connections are connected to the
-            database.
-
-            :meth:`.PoolEvents.checkout` the lower-level pool checkout event
-            for an individual DBAPI connection
-
-            :meth:`.ConnectionEvents.set_connection_execution_options` - a copy
-            of a :class:`.Connection` is also made when the
-            :meth:`.Connection.execution_options` method is called.
-
-        """
-
-    def set_connection_execution_options(self, conn, opts):
-        """Intercept when the :meth:`.Connection.execution_options`
-        method is called.
-
-        This method is called after the new :class:`.Connection` has been
-        produced, with the newly updated execution options collection, but
-        before the :class:`.Dialect` has acted upon any of those new options.
-
-        Note that this method is not called when a new :class:`.Connection`
-        is produced which is inheriting execution options from its parent
-        :class:`.Engine`; to intercept this condition, use the
-        :meth:`.ConnectionEvents.engine_connect` event.
-
-        :param conn: The newly copied :class:`.Connection` object
-
-        :param opts: dictionary of options that were passed to the
-         :meth:`.Connection.execution_options` method.
-
-        .. versionadded:: 0.9.0
-
-        .. seealso::
-
-            :meth:`.ConnectionEvents.set_engine_execution_options` - event
-            which is called when :meth:`.Engine.execution_options` is called.
-
-
-        """
-
-    def set_engine_execution_options(self, engine, opts):
-        """Intercept when the :meth:`.Engine.execution_options`
-        method is called.
-
-        The :meth:`.Engine.execution_options` method produces a shallow
-        copy of the :class:`.Engine` which stores the new options.  That new
-        :class:`.Engine` is passed here.   A particular application of this
-        method is to add a :meth:`.ConnectionEvents.engine_connect` event
-        handler to the given :class:`.Engine` which will perform some per-
-        :class:`.Connection` task specific to these execution options.
-
-        :param conn: The newly copied :class:`.Engine` object
-
-        :param opts: dictionary of options that were passed to the
-         :meth:`.Connection.execution_options` method.
-
-        .. versionadded:: 0.9.0
-
-        .. seealso::
-
-            :meth:`.ConnectionEvents.set_connection_execution_options` - event
-            which is called when :meth:`.Connection.execution_options` is
-            called.
-
-        """
-
-    def engine_disposed(self, engine):
-        """Intercept when the :meth:`.Engine.dispose` method is called.
-
-        The :meth:`.Engine.dispose` method instructs the engine to
-        "dispose" of it's connection pool (e.g. :class:`.Pool`), and
-        replaces it with a new one.  Disposing of the old pool has the
-        effect that existing checked-in connections are closed.  The new
-        pool does not establish any new connections until it is first used.
-
-        This event can be used to indicate that resources related to the
-        :class:`.Engine` should also be cleaned up, keeping in mind that the
-        :class:`.Engine` can still be used for new requests in which case
-        it re-acquires connection resources.
-
-        .. versionadded:: 1.0.5
-
-        """
-
-    def begin(self, conn):
-        """Intercept begin() events.
-
-        :param conn: :class:`.Connection` object
-
-        """
-
-    def rollback(self, conn):
-        """Intercept rollback() events, as initiated by a
-        :class:`.Transaction`.
-
-        Note that the :class:`.Pool` also "auto-rolls back"
-        a DBAPI connection upon checkin, if the ``reset_on_return``
-        flag is set to its default value of ``'rollback'``.
-        To intercept this
-        rollback, use the :meth:`.PoolEvents.reset` hook.
-
-        :param conn: :class:`.Connection` object
-
-        .. seealso::
-
-            :meth:`.PoolEvents.reset`
-
-        """
-
-    def commit(self, conn):
-        """Intercept commit() events, as initiated by a
-        :class:`.Transaction`.
-
-        Note that the :class:`.Pool` may also "auto-commit"
-        a DBAPI connection upon checkin, if the ``reset_on_return``
-        flag is set to the value ``'commit'``.  To intercept this
-        commit, use the :meth:`.PoolEvents.reset` hook.
-
-        :param conn: :class:`.Connection` object
-        """
-
-    def savepoint(self, conn, name):
-        """Intercept savepoint() events.
-
-        :param conn: :class:`.Connection` object
-        :param name: specified name used for the savepoint.
-
-        """
-
-    def rollback_savepoint(self, conn, name, context):
-        """Intercept rollback_savepoint() events.
-
-        :param conn: :class:`.Connection` object
-        :param name: specified name used for the savepoint.
-        :param context: :class:`.ExecutionContext` in use.  May be ``None``.
-
-        """
-
-    def release_savepoint(self, conn, name, context):
-        """Intercept release_savepoint() events.
-
-        :param conn: :class:`.Connection` object
-        :param name: specified name used for the savepoint.
-        :param context: :class:`.ExecutionContext` in use.  May be ``None``.
-
-        """
-
-    def begin_twophase(self, conn, xid):
-        """Intercept begin_twophase() events.
-
-        :param conn: :class:`.Connection` object
-        :param xid: two-phase XID identifier
-
-        """
-
-    def prepare_twophase(self, conn, xid):
-        """Intercept prepare_twophase() events.
-
-        :param conn: :class:`.Connection` object
-        :param xid: two-phase XID identifier
-        """
-
-    def rollback_twophase(self, conn, xid, is_prepared):
-        """Intercept rollback_twophase() events.
-
-        :param conn: :class:`.Connection` object
-        :param xid: two-phase XID identifier
-        :param is_prepared: boolean, indicates if
-         :meth:`.TwoPhaseTransaction.prepare` was called.
-
-        """
-
-    def commit_twophase(self, conn, xid, is_prepared):
-        """Intercept commit_twophase() events.
-
-        :param conn: :class:`.Connection` object
-        :param xid: two-phase XID identifier
-        :param is_prepared: boolean, indicates if
-         :meth:`.TwoPhaseTransaction.prepare` was called.
-
-        """
-
-
-class DialectEvents(event.Events):
-    """event interface for execution-replacement functions.
-
-    These events allow direct instrumentation and replacement
-    of key dialect functions which interact with the DBAPI.
-
-    .. note::
-
-        :class:`.DialectEvents` hooks should be considered **semi-public**
-        and experimental.
-        These hooks are not for general use and are only for those situations
-        where intricate re-statement of DBAPI mechanics must be injected onto
-        an existing dialect.  For general-use statement-interception events,
-        please use the :class:`.ConnectionEvents` interface.
-
-    .. seealso::
-
-        :meth:`.ConnectionEvents.before_cursor_execute`
-
-        :meth:`.ConnectionEvents.before_execute`
-
-        :meth:`.ConnectionEvents.after_cursor_execute`
-
-        :meth:`.ConnectionEvents.after_execute`
-
-
-    .. versionadded:: 0.9.4
-
-    """
-
-    _target_class_doc = "SomeEngine"
-    _dispatch_target = Dialect
-
-    @classmethod
-    def _listen(cls, event_key, retval=False):
-        target = event_key.dispatch_target
-
-        target._has_events = True
-        event_key.base_listen()
-
-    @classmethod
-    def _accept_with(cls, target):
-        if isinstance(target, type):
-            if issubclass(target, Engine):
-                return Dialect
-            elif issubclass(target, Dialect):
-                return target
-        elif isinstance(target, Engine):
-            return target.dialect
-        else:
-            return target
-
-    def do_connect(self, dialect, conn_rec, cargs, cparams):
-        """Receive connection arguments before a connection is made.
-
-        Return a DBAPI connection to halt further events from invoking;
-        the returned connection will be used.
-
-        Alternatively, the event can manipulate the cargs and/or cparams
-        collections; cargs will always be a Python list that can be mutated
-        in-place and cparams a Python dictionary.  Return None to
-        allow control to pass to the next event handler and ultimately
-        to allow the dialect to connect normally, given the updated
-        arguments.
-
-        .. versionadded:: 1.0.3
-
-        """
-
-    def do_executemany(self, cursor, statement, parameters, context):
-        """Receive a cursor to have executemany() called.
-
-        Return the value True to halt further events from invoking,
-        and to indicate that the cursor execution has already taken
-        place within the event handler.
-
-        """
-
-    def do_execute_no_params(self, cursor, statement, context):
-        """Receive a cursor to have execute() with no parameters called.
-
-        Return the value True to halt further events from invoking,
-        and to indicate that the cursor execution has already taken
-        place within the event handler.
-
-        """
-
-    def do_execute(self, cursor, statement, parameters, context):
-        """Receive a cursor to have execute() called.
-
-        Return the value True to halt further events from invoking,
-        and to indicate that the cursor execution has already taken
-        place within the event handler.
-
-        """
-
-    def do_setinputsizes(
-        self, inputsizes, cursor, statement, parameters, context
-    ):
-        """Receive the setinputsizes dictionary for possible modification.
-
-        This event is emitted in the case where the dialect makes use of the
-        DBAPI ``cursor.setinputsizes()`` method which passes information about
-        parameter binding for a particular statement.   The given
-        ``inputsizes`` dictionary will contain :class:`.BindParameter` objects
-        as keys, linked to DBAPI-specific type objects as values; for
-        parameters that are not bound, they are added to the dictionary with
-        ``None`` as the value, which means the parameter will not be included
-        in the ultimate setinputsizes call.   The event may be used to inspect
-        and/or log the datatypes that are being bound, as well as to modify the
-        dictionary in place.  Parameters can be added, modified, or removed
-        from this dictionary.   Callers will typically want to inspect the
-        :attr:`.BindParameter.type` attribute of the given bind objects in
-        order to make decisions about the DBAPI object.
-
-        After the event, the ``inputsizes`` dictionary is converted into
-        an appropriate datastructure to be passed to ``cursor.setinputsizes``;
-        either a list for a positional bound parameter execution style,
-        or a dictionary of string parameter keys to DBAPI type objects for
-        a named bound parameter execution style.
-
-        Most dialects **do not use** this method at all; the only built-in
-        dialect which uses this hook is the cx_Oracle dialect.   The hook here
-        is made available so as to allow customization of how datatypes are set
-        up with the  cx_Oracle DBAPI.
-
-        .. versionadded:: 1.2.9
-
-        .. seealso::
-
-            :ref:`cx_oracle_setinputsizes`
-
-        """
-        pass
+from .engine.events import ConnectionEvents  # noqa
+from .engine.events import DialectEvents  # noqa
+from .pool.events import PoolEvents  # noqa
+from .sql.base import SchemaEventTarget  # noqa
+from .sql.events import DDLEvents  # noqa
index ba9871378bfd7e8cf1462b9621b2a3d555f328fb..058d595c8bf795ddcffd41355b58140cf594549f 100644 (file)
@@ -17,6 +17,7 @@ regular DB-API connect() methods to be transparently managed by a
 SQLAlchemy connection pool.
 """
 
+from . import events  # noqa
 from .base import _ConnectionFairy  # noqa
 from .base import _ConnectionRecord  # noqa
 from .base import _finalize_fairy  # noqa
diff --git a/lib/sqlalchemy/pool/events.py b/lib/sqlalchemy/pool/events.py
new file mode 100644 (file)
index 0000000..7a3a091
--- /dev/null
@@ -0,0 +1,236 @@
+# sqlalchemy/pool/events.py
+# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
+# <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from .base import Pool
+from .. import event
+from ..engine.base import Engine
+
+
+class PoolEvents(event.Events):
+    """Available events for :class:`.Pool`.
+
+    The methods here define the name of an event as well
+    as the names of members that are passed to listener
+    functions.
+
+    e.g.::
+
+        from sqlalchemy import event
+
+        def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
+            "handle an on checkout event"
+
+        event.listen(Pool, 'checkout', my_on_checkout)
+
+    In addition to accepting the :class:`.Pool` class and
+    :class:`.Pool` instances, :class:`.PoolEvents` also accepts
+    :class:`.Engine` objects and the :class:`.Engine` class as
+    targets, which will be resolved to the ``.pool`` attribute of the
+    given engine or the :class:`.Pool` class::
+
+        engine = create_engine("postgresql://scott:tiger@localhost/test")
+
+        # will associate with engine.pool
+        event.listen(engine, 'checkout', my_on_checkout)
+
+    """
+
+    _target_class_doc = "SomeEngineOrPool"
+    _dispatch_target = Pool
+
+    @classmethod
+    def _accept_with(cls, target):
+        if isinstance(target, type):
+            if issubclass(target, Engine):
+                return Pool
+            elif issubclass(target, Pool):
+                return target
+        elif isinstance(target, Engine):
+            return target.pool
+        else:
+            return target
+
+    def connect(self, dbapi_connection, connection_record):
+        """Called at the moment a particular DBAPI connection is first
+        created for a given :class:`.Pool`.
+
+        This event allows one to capture the point directly after which
+        the DBAPI module-level ``.connect()`` method has been used in order
+        to produce a new DBAPI connection.
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        """
+
+    def first_connect(self, dbapi_connection, connection_record):
+        """Called exactly once for the first time a DBAPI connection is
+        checked out from a particular :class:`.Pool`.
+
+        The rationale for :meth:`.PoolEvents.first_connect` is to determine
+        information about a particular series of database connections based
+        on the settings used for all connections.  Since a particular
+        :class:`.Pool` refers to a single "creator" function (which in terms
+        of a :class:`.Engine` refers to the URL and connection options used),
+        it is typically valid to make observations about a single connection
+        that can be safely assumed to be valid about all subsequent
+        connections, such as the database version, the server and client
+        encoding settings, collation settings, and many others.
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        """
+
+    def checkout(self, dbapi_connection, connection_record, connection_proxy):
+        """Called when a connection is retrieved from the Pool.
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        :param connection_proxy: the :class:`._ConnectionFairy` object which
+          will proxy the public interface of the DBAPI connection for the
+          lifespan of the checkout.
+
+        If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
+        connection will be disposed and a fresh connection retrieved.
+        Processing of all checkout listeners will abort and restart
+        using the new connection.
+
+        .. seealso:: :meth:`.ConnectionEvents.engine_connect` - a similar event
+           which occurs upon creation of a new :class:`.Connection`.
+
+        """
+
+    def checkin(self, dbapi_connection, connection_record):
+        """Called when a connection returns to the pool.
+
+        Note that the connection may be closed, and may be None if the
+        connection has been invalidated.  ``checkin`` will not be called
+        for detached connections.  (They do not return to the pool.)
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        """
+
+    def reset(self, dbapi_connection, connection_record):
+        """Called before the "reset" action occurs for a pooled connection.
+
+        This event represents
+        when the ``rollback()`` method is called on the DBAPI connection
+        before it is returned to the pool.  The behavior of "reset" can
+        be controlled, including disabled, using the ``reset_on_return``
+        pool argument.
+
+
+        The :meth:`.PoolEvents.reset` event is usually followed by the
+        :meth:`.PoolEvents.checkin` event is called, except in those
+        cases where the connection is discarded immediately after reset.
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        .. seealso::
+
+            :meth:`.ConnectionEvents.rollback`
+
+            :meth:`.ConnectionEvents.commit`
+
+        """
+
+    def invalidate(self, dbapi_connection, connection_record, exception):
+        """Called when a DBAPI connection is to be "invalidated".
+
+        This event is called any time the :meth:`._ConnectionRecord.invalidate`
+        method is invoked, either from API usage or via "auto-invalidation",
+        without the ``soft`` flag.
+
+        The event occurs before a final attempt to call ``.close()`` on the
+        connection occurs.
+
+        :param dbapi_connection: a DBAPI connection.
+
+        :param connection_record: the :class:`._ConnectionRecord` managing the
+         DBAPI connection.
+
+        :param exception: the exception object corresponding to the reason
+         for this invalidation, if any.  May be ``None``.
+
+        .. versionadded:: 0.9.2 Added support for connection invalidation
+           listening.
+
+        .. seealso::
+
+            :ref:`pool_connection_invalidation`
+
+        """
+
+    def soft_invalidate(self, dbapi_connection, connection_record, exception):
+        """Called when a DBAPI connection is to be "soft invalidated".
+
+        This event is called any time the :meth:`._ConnectionRecord.invalidate`
+        method is invoked with the ``soft`` flag.
+
+        Soft invalidation refers to when the connection record that tracks
+        this connection will force a reconnect after the current connection
+        is checked in.   It does not actively close the dbapi_connection
+        at the point at which it is called.
+
+        .. versionadded:: 1.0.3
+
+        """
+
+    def close(self, dbapi_connection, connection_record):
+        """Called when a DBAPI connection is closed.
+
+        The event is emitted before the close occurs.
+
+        The close of a connection can fail; typically this is because
+        the connection is already closed.  If the close operation fails,
+        the connection is discarded.
+
+        The :meth:`.close` event corresponds to a connection that's still
+        associated with the pool. To intercept close events for detached
+        connections use :meth:`.close_detached`.
+
+        .. versionadded:: 1.1
+
+        """
+
+    def detach(self, dbapi_connection, connection_record):
+        """Called when a DBAPI connection is "detached" from a pool.
+
+        This event is emitted after the detach occurs.  The connection
+        is no longer associated with the given connection record.
+
+        .. versionadded:: 1.1
+
+        """
+
+    def close_detached(self, dbapi_connection):
+        """Called when a detached DBAPI connection is closed.
+
+        The event is emitted before the close occurs.
+
+        The close of a connection can fail; typically this is because
+        the connection is already closed.  If the close operation fails,
+        the connection is discarded.
+
+        .. versionadded:: 1.1
+
+        """
index 2b49392278fafec991e1c2a875b85ba10cf527cf..6554faaa0853085bc9ec59b71a72c60d5f29ec28 100644 (file)
@@ -98,6 +98,7 @@ def __go(lcls):
     from . import base
     from . import coercions
     from . import elements
+    from . import events  # noqa
     from . import selectable
     from . import schema
     from . import sqltypes
diff --git a/lib/sqlalchemy/sql/events.py b/lib/sqlalchemy/sql/events.py
new file mode 100644 (file)
index 0000000..cd48d99
--- /dev/null
@@ -0,0 +1,285 @@
+# sqlalchemy/sql/events.py
+# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
+# <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from .base import SchemaEventTarget
+from .. import event
+
+
+class DDLEvents(event.Events):
+    """
+    Define event listeners for schema objects,
+    that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
+    subclasses, including :class:`.MetaData`, :class:`.Table`,
+    :class:`.Column`.
+
+    :class:`.MetaData` and :class:`.Table` support events
+    specifically regarding when CREATE and DROP
+    DDL is emitted to the database.
+
+    Attachment events are also provided to customize
+    behavior whenever a child schema element is associated
+    with a parent, such as, when a :class:`.Column` is associated
+    with its :class:`.Table`, when a :class:`.ForeignKeyConstraint`
+    is associated with a :class:`.Table`, etc.
+
+    Example using the ``after_create`` event::
+
+        from sqlalchemy import event
+        from sqlalchemy import Table, Column, Metadata, Integer
+
+        m = MetaData()
+        some_table = Table('some_table', m, Column('data', Integer))
+
+        def after_create(target, connection, **kw):
+            connection.execute("ALTER TABLE %s SET name=foo_%s" %
+                                    (target.name, target.name))
+
+        event.listen(some_table, "after_create", after_create)
+
+    DDL events integrate closely with the
+    :class:`.DDL` class and the :class:`.DDLElement` hierarchy
+    of DDL clause constructs, which are themselves appropriate
+    as listener callables::
+
+        from sqlalchemy import DDL
+        event.listen(
+            some_table,
+            "after_create",
+            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s")
+        )
+
+    The methods here define the name of an event as well
+    as the names of members that are passed to listener
+    functions.
+
+    For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
+    will ensure that a given event handler is propagated to copies of the
+    object, which are made when using the :meth:`.Table.tometadata` method::
+
+        from sqlalchemy import DDL
+        event.listen(
+            some_table,
+            "after_create",
+            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
+            propagate=True
+        )
+
+        new_table = some_table.tometadata(new_metadata)
+
+    The above :class:`.DDL` object will also be associated with the
+    :class:`.Table` object represented by ``new_table``.
+
+    .. seealso::
+
+        :ref:`event_toplevel`
+
+        :class:`.DDLElement`
+
+        :class:`.DDL`
+
+        :ref:`schema_ddl_sequences`
+
+    """
+
+    _target_class_doc = "SomeSchemaClassOrObject"
+    _dispatch_target = SchemaEventTarget
+
+    def before_create(self, target, connection, **kw):
+        r"""Called before CREATE statements are emitted.
+
+        :param target: the :class:`.MetaData` or :class:`.Table`
+         object which is the target of the event.
+        :param connection: the :class:`.Connection` where the
+         CREATE statement or statements will be emitted.
+        :param \**kw: additional keyword arguments relevant
+         to the event.  The contents of this dictionary
+         may vary across releases, and include the
+         list of tables being generated for a metadata-level
+         event, the checkfirst flag, and other
+         elements used by internal events.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def after_create(self, target, connection, **kw):
+        r"""Called after CREATE statements are emitted.
+
+        :param target: the :class:`.MetaData` or :class:`.Table`
+         object which is the target of the event.
+        :param connection: the :class:`.Connection` where the
+         CREATE statement or statements have been emitted.
+        :param \**kw: additional keyword arguments relevant
+         to the event.  The contents of this dictionary
+         may vary across releases, and include the
+         list of tables being generated for a metadata-level
+         event, the checkfirst flag, and other
+         elements used by internal events.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def before_drop(self, target, connection, **kw):
+        r"""Called before DROP statements are emitted.
+
+        :param target: the :class:`.MetaData` or :class:`.Table`
+         object which is the target of the event.
+        :param connection: the :class:`.Connection` where the
+         DROP statement or statements will be emitted.
+        :param \**kw: additional keyword arguments relevant
+         to the event.  The contents of this dictionary
+         may vary across releases, and include the
+         list of tables being generated for a metadata-level
+         event, the checkfirst flag, and other
+         elements used by internal events.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def after_drop(self, target, connection, **kw):
+        r"""Called after DROP statements are emitted.
+
+        :param target: the :class:`.MetaData` or :class:`.Table`
+         object which is the target of the event.
+        :param connection: the :class:`.Connection` where the
+         DROP statement or statements have been emitted.
+        :param \**kw: additional keyword arguments relevant
+         to the event.  The contents of this dictionary
+         may vary across releases, and include the
+         list of tables being generated for a metadata-level
+         event, the checkfirst flag, and other
+         elements used by internal events.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def before_parent_attach(self, target, parent):
+        """Called before a :class:`.SchemaItem` is associated with
+        a parent :class:`.SchemaItem`.
+
+        :param target: the target object
+        :param parent: the parent to which the target is being attached.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def after_parent_attach(self, target, parent):
+        """Called after a :class:`.SchemaItem` is associated with
+        a parent :class:`.SchemaItem`.
+
+        :param target: the target object
+        :param parent: the parent to which the target is being attached.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
+
+    def column_reflect(self, inspector, table, column_info):
+        """Called for each unit of 'column info' retrieved when
+        a :class:`.Table` is being reflected.
+
+        The dictionary of column information as returned by the
+        dialect is passed, and can be modified.  The dictionary
+        is that returned in each element of the list returned
+        by :meth:`.reflection.Inspector.get_columns`:
+
+            * ``name`` - the column's name
+
+            * ``type`` - the type of this column, which should be an instance
+              of :class:`~sqlalchemy.types.TypeEngine`
+
+            * ``nullable`` - boolean flag if the column is NULL or NOT NULL
+
+            * ``default`` - the column's server default value.  This is
+              normally specified as a plain string SQL expression, however the
+              event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
+              or :func:`.sql.expression.text` object as well.
+
+              .. versionchanged:: 1.1.6
+
+                    The :meth:`.DDLEvents.column_reflect` event allows a non
+                    string :class:`.FetchedValue`,
+                    :func:`.sql.expression.text`, or derived object to be
+                    specified as the value of ``default`` in the column
+                    dictionary.
+
+            * ``attrs``  - dict containing optional column attributes
+
+        The event is called before any action is taken against
+        this dictionary, and the contents can be modified.
+        The :class:`.Column` specific arguments ``info``, ``key``,
+        and ``quote`` can also be added to the dictionary and
+        will be passed to the constructor of :class:`.Column`.
+
+        Note that this event is only meaningful if either
+        associated with the :class:`.Table` class across the
+        board, e.g.::
+
+            from sqlalchemy.schema import Table
+            from sqlalchemy import event
+
+            def listen_for_reflect(inspector, table, column_info):
+                "receive a column_reflect event"
+                # ...
+
+            event.listen(
+                    Table,
+                    'column_reflect',
+                    listen_for_reflect)
+
+        ...or with a specific :class:`.Table` instance using
+        the ``listeners`` argument::
+
+            def listen_for_reflect(inspector, table, column_info):
+                "receive a column_reflect event"
+                # ...
+
+            t = Table(
+                'sometable',
+                autoload=True,
+                listeners=[
+                    ('column_reflect', listen_for_reflect)
+                ])
+
+        This because the reflection process initiated by ``autoload=True``
+        completes within the scope of the constructor for :class:`.Table`.
+
+        :func:`.event.listen` also accepts the ``propagate=True``
+        modifier for this event; when True, the listener function will
+        be established for any copies made of the target object,
+        i.e. those copies that are generated when
+        :meth:`.Table.tometadata` is used.
+
+        """
index 756a85b22a2dddabcd0203a97aebcde926ed9245..f530177dd2046ab1ea02aeff84292745c035bab5 100644 (file)
@@ -12,6 +12,7 @@
 
 import re
 
+from . import events  # noqa
 from .elements import _defer_name
 from .elements import _defer_none_name
 from .elements import conv
@@ -24,7 +25,6 @@ from .schema import PrimaryKeyConstraint
 from .schema import Table
 from .schema import UniqueConstraint
 from .. import event
-from .. import events  # noqa
 from .. import exc
 
 
index 68a3a07497f7be6d33059463ee0dbc798ccf7b97..03ff7c4394a3876ece4b1f4443932ea48be55cf1 100644 (file)
@@ -5,8 +5,8 @@ import operator
 from . import operators
 from .visitors import ExtendedInternalTraversal
 from .visitors import InternalTraversal
-from .. import inspect
 from .. import util
+from ..inspection import inspect
 
 SKIP_TRAVERSE = util.symbol("skip_traverse")
 COMPARE_FAILED = False