summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/events.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-02-17 13:43:04 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2022-03-01 09:09:02 -0500
commita4bb502cf95ea3523e4d383c4377e50f402d7d52 (patch)
tree124400f741b6b91f0e9e582b510268607394dfaa /lib/sqlalchemy/engine/events.py
parent60fca2ac8cf44bdaf68552ab5c69854a6776c73c (diff)
downloadsqlalchemy-a4bb502cf95ea3523e4d383c4377e50f402d7d52.tar.gz
pep-484 for engine
All modules in sqlalchemy.engine are strictly typed with the exception of cursor, default, and reflection. cursor and default pass with non-strict typing, reflection is waiting on the multi-reflection refactor. Behavioral changes: * create_connect_args() methods return a tuple of list, dict, rather than a list of list, dict * removed allow_chars parameter from pyodbc connector ._get_server_version_info() method * the parameter list passed to do_executemany is now a list in all cases. previously, this was being run through dialect.execute_sequence_format, which defaults to tuple and was only intended for individual tuple params. * broke up dialect.dbapi into dialect.import_dbapi class method and dialect.dbapi module object. added a deprecation path for legacy dialects. it's not really feasible to type a single attr as a classmethod vs. module type. The "type_compiler" attribute also has this problem with greater ability to work around, left that one for now. * lots of constants changing to be Enum, so that we can type them. for fixed tuple-position constants in cursor.py / compiler.py (which are used to avoid the speed overhead of namedtuple), using Literal[value] which seems to work well * some tightening up in Row regarding __getitem__, which we can do since we are on full 2.0 style result use * altered the set_connection_execution_options and set_engine_execution_options event flows so that the dictionary of options may be mutated within the event hook, where it will then take effect as the actual options used. Previously, changing the dict would be silently ignored which seems counter-intuitive and not very useful. * A lot of DefaultDialect/DefaultExecutionContext methods and attributes, including underscored ones, move to interfaces. This is not fully ideal as it means the Dialect/ExecutionContext interfaces aren't publicly subclassable directly, but their current purpose is more of documentation for dialect authors who should (and certainly are) still be subclassing the DefaultXYZ versions in all cases Overall, Result was the most extremely difficult class hierarchy to type here as this hierarchy passes through largely amorphous "row" datatypes throughout, which can in fact by all kinds of different things, like raw DBAPI rows, or Row objects, or "scalar"/Any, but at the same time these types have meaning so I tried still maintaining some level of semantic markings for these, it highlights how complex Result is now, as it's trying to be extremely efficient and inlined while also being very open-ended and extensible. Change-Id: I98b75c0c09eab5355fc7a33ba41dd9874274f12a
Diffstat (limited to 'lib/sqlalchemy/engine/events.py')
-rw-r--r--lib/sqlalchemy/engine/events.py193
1 files changed, 148 insertions, 45 deletions
diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py
index ab462bbe1..0cbf56a6d 100644
--- a/lib/sqlalchemy/engine/events.py
+++ b/lib/sqlalchemy/engine/events.py
@@ -8,14 +8,41 @@
from __future__ import annotations
+import typing
+from typing import Any
+from typing import Dict
+from typing import Optional
+from typing import Tuple
+from typing import Type
+from typing import Union
+
from .base import Engine
from .interfaces import ConnectionEventsTarget
+from .interfaces import DBAPIConnection
+from .interfaces import DBAPICursor
from .interfaces import Dialect
from .. import event
from .. import exc
-
-
-class ConnectionEvents(event.Events):
+from ..util.typing import Literal
+
+if typing.TYPE_CHECKING:
+ from .base import Connection
+ from .interfaces import _CoreAnyExecuteParams
+ from .interfaces import _CoreMultiExecuteParams
+ from .interfaces import _CoreSingleExecuteParams
+ from .interfaces import _DBAPIAnyExecuteParams
+ from .interfaces import _DBAPIMultiExecuteParams
+ from .interfaces import _DBAPISingleExecuteParams
+ from .interfaces import _ExecuteOptions
+ from .interfaces import ExceptionContext
+ from .interfaces import ExecutionContext
+ from .result import Result
+ from ..pool import ConnectionPoolEntry
+ from ..sql import Executable
+ from ..sql.elements import BindParameter
+
+
+class ConnectionEvents(event.Events[ConnectionEventsTarget]):
"""Available events for
:class:`_engine.Connection` and :class:`_engine.Engine`.
@@ -96,7 +123,12 @@ class ConnectionEvents(event.Events):
_dispatch_target = ConnectionEventsTarget
@classmethod
- def _listen(cls, event_key, retval=False):
+ def _listen( # type: ignore[override]
+ cls,
+ event_key: event._EventKey[ConnectionEventsTarget],
+ retval: bool = False,
+ **kw: Any,
+ ) -> None:
target, identifier, fn = (
event_key.dispatch_target,
event_key.identifier,
@@ -109,7 +141,7 @@ class ConnectionEvents(event.Events):
if identifier == "before_execute":
orig_fn = fn
- def wrap_before_execute(
+ def wrap_before_execute( # type: ignore
conn, clauseelement, multiparams, params, execution_options
):
orig_fn(
@@ -125,7 +157,7 @@ class ConnectionEvents(event.Events):
elif identifier == "before_cursor_execute":
orig_fn = fn
- def wrap_before_cursor_execute(
+ def wrap_before_cursor_execute( # type: ignore
conn, cursor, statement, parameters, context, executemany
):
orig_fn(
@@ -163,8 +195,15 @@ class ConnectionEvents(event.Events):
),
)
def before_execute(
- self, conn, clauseelement, multiparams, params, execution_options
- ):
+ self,
+ conn: Connection,
+ clauseelement: Executable,
+ multiparams: _CoreMultiExecuteParams,
+ params: _CoreSingleExecuteParams,
+ execution_options: _ExecuteOptions,
+ ) -> Optional[
+ Tuple[Executable, _CoreMultiExecuteParams, _CoreSingleExecuteParams]
+ ]:
"""Intercept high level execute() events, receiving uncompiled
SQL constructs and other objects prior to rendering into SQL.
@@ -214,13 +253,13 @@ class ConnectionEvents(event.Events):
)
def after_execute(
self,
- conn,
- clauseelement,
- multiparams,
- params,
- execution_options,
- result,
- ):
+ conn: Connection,
+ clauseelement: Executable,
+ multiparams: _CoreMultiExecuteParams,
+ params: _CoreSingleExecuteParams,
+ execution_options: _ExecuteOptions,
+ result: Result,
+ ) -> None:
"""Intercept high level execute() events after execute.
@@ -244,8 +283,14 @@ class ConnectionEvents(event.Events):
"""
def before_cursor_execute(
- self, conn, cursor, statement, parameters, context, executemany
- ):
+ self,
+ conn: Connection,
+ cursor: DBAPICursor,
+ statement: str,
+ parameters: _DBAPIAnyExecuteParams,
+ context: Optional[ExecutionContext],
+ executemany: bool,
+ ) -> Optional[Tuple[str, _DBAPIAnyExecuteParams]]:
"""Intercept low-level cursor execute() events before execution,
receiving the string SQL statement and DBAPI-specific parameter list to
be invoked against a cursor.
@@ -286,8 +331,14 @@ class ConnectionEvents(event.Events):
"""
def after_cursor_execute(
- self, conn, cursor, statement, parameters, context, executemany
- ):
+ self,
+ conn: Connection,
+ cursor: DBAPICursor,
+ statement: str,
+ parameters: _DBAPIAnyExecuteParams,
+ context: Optional[ExecutionContext],
+ executemany: bool,
+ ) -> None:
"""Intercept low-level cursor execute() events after execution.
:param conn: :class:`_engine.Connection` object
@@ -305,7 +356,9 @@ class ConnectionEvents(event.Events):
"""
- def handle_error(self, exception_context):
+ def handle_error(
+ self, exception_context: ExceptionContext
+ ) -> Optional[BaseException]:
r"""Intercept all exceptions processed by the
:class:`_engine.Connection`.
@@ -439,7 +492,7 @@ class ConnectionEvents(event.Events):
@event._legacy_signature(
"2.0", ["conn", "branch"], converter=lambda conn: (conn, False)
)
- def engine_connect(self, conn):
+ def engine_connect(self, conn: Connection) -> None:
"""Intercept the creation of a new :class:`_engine.Connection`.
This event is called typically as the direct result of calling
@@ -475,7 +528,9 @@ class ConnectionEvents(event.Events):
"""
- def set_connection_execution_options(self, conn, opts):
+ def set_connection_execution_options(
+ self, conn: Connection, opts: Dict[str, Any]
+ ) -> None:
"""Intercept when the :meth:`_engine.Connection.execution_options`
method is called.
@@ -494,8 +549,12 @@ class ConnectionEvents(event.Events):
:param opts: dictionary of options that were passed to the
:meth:`_engine.Connection.execution_options` method.
+ This dictionary may be modified in place to affect the ultimate
+ options which take effect.
+
+ .. versionadded:: 2.0 the ``opts`` dictionary may be modified
+ in place.
- .. versionadded:: 0.9.0
.. seealso::
@@ -507,7 +566,9 @@ class ConnectionEvents(event.Events):
"""
- def set_engine_execution_options(self, engine, opts):
+ def set_engine_execution_options(
+ self, engine: Engine, opts: Dict[str, Any]
+ ) -> None:
"""Intercept when the :meth:`_engine.Engine.execution_options`
method is called.
@@ -526,8 +587,11 @@ class ConnectionEvents(event.Events):
:param opts: dictionary of options that were passed to the
:meth:`_engine.Connection.execution_options` method.
+ This dictionary may be modified in place to affect the ultimate
+ options which take effect.
- .. versionadded:: 0.9.0
+ .. versionadded:: 2.0 the ``opts`` dictionary may be modified
+ in place.
.. seealso::
@@ -539,7 +603,7 @@ class ConnectionEvents(event.Events):
"""
- def engine_disposed(self, engine):
+ def engine_disposed(self, engine: Engine) -> None:
"""Intercept when the :meth:`_engine.Engine.dispose` method is called.
The :meth:`_engine.Engine.dispose` method instructs the engine to
@@ -559,14 +623,14 @@ class ConnectionEvents(event.Events):
"""
- def begin(self, conn):
+ def begin(self, conn: Connection) -> None:
"""Intercept begin() events.
:param conn: :class:`_engine.Connection` object
"""
- def rollback(self, conn):
+ def rollback(self, conn: Connection) -> None:
"""Intercept rollback() events, as initiated by a
:class:`.Transaction`.
@@ -584,7 +648,7 @@ class ConnectionEvents(event.Events):
"""
- def commit(self, conn):
+ def commit(self, conn: Connection) -> None:
"""Intercept commit() events, as initiated by a
:class:`.Transaction`.
@@ -596,7 +660,7 @@ class ConnectionEvents(event.Events):
:param conn: :class:`_engine.Connection` object
"""
- def savepoint(self, conn, name):
+ def savepoint(self, conn: Connection, name: str) -> None:
"""Intercept savepoint() events.
:param conn: :class:`_engine.Connection` object
@@ -604,7 +668,9 @@ class ConnectionEvents(event.Events):
"""
- def rollback_savepoint(self, conn, name, context):
+ def rollback_savepoint(
+ self, conn: Connection, name: str, context: None
+ ) -> None:
"""Intercept rollback_savepoint() events.
:param conn: :class:`_engine.Connection` object
@@ -614,7 +680,9 @@ class ConnectionEvents(event.Events):
"""
# TODO: deprecate "context"
- def release_savepoint(self, conn, name, context):
+ def release_savepoint(
+ self, conn: Connection, name: str, context: None
+ ) -> None:
"""Intercept release_savepoint() events.
:param conn: :class:`_engine.Connection` object
@@ -624,7 +692,7 @@ class ConnectionEvents(event.Events):
"""
# TODO: deprecate "context"
- def begin_twophase(self, conn, xid):
+ def begin_twophase(self, conn: Connection, xid: Any) -> None:
"""Intercept begin_twophase() events.
:param conn: :class:`_engine.Connection` object
@@ -632,14 +700,16 @@ class ConnectionEvents(event.Events):
"""
- def prepare_twophase(self, conn, xid):
+ def prepare_twophase(self, conn: Connection, xid: Any) -> None:
"""Intercept prepare_twophase() events.
:param conn: :class:`_engine.Connection` object
:param xid: two-phase XID identifier
"""
- def rollback_twophase(self, conn, xid, is_prepared):
+ def rollback_twophase(
+ self, conn: Connection, xid: Any, is_prepared: bool
+ ) -> None:
"""Intercept rollback_twophase() events.
:param conn: :class:`_engine.Connection` object
@@ -649,7 +719,9 @@ class ConnectionEvents(event.Events):
"""
- def commit_twophase(self, conn, xid, is_prepared):
+ def commit_twophase(
+ self, conn: Connection, xid: Any, is_prepared: bool
+ ) -> None:
"""Intercept commit_twophase() events.
:param conn: :class:`_engine.Connection` object
@@ -660,7 +732,7 @@ class ConnectionEvents(event.Events):
"""
-class DialectEvents(event.Events):
+class DialectEvents(event.Events[Dialect]):
"""event interface for execution-replacement functions.
These events allow direct instrumentation and replacement
@@ -694,14 +766,20 @@ class DialectEvents(event.Events):
_dispatch_target = Dialect
@classmethod
- def _listen(cls, event_key, retval=False):
+ def _listen( # type: ignore
+ cls,
+ event_key: event._EventKey[Dialect],
+ retval: bool = False,
+ ) -> None:
target = event_key.dispatch_target
target._has_events = True
event_key.base_listen()
@classmethod
- def _accept_with(cls, target):
+ def _accept_with(
+ cls, target: Union[Engine, Type[Engine], Dialect, Type[Dialect]]
+ ) -> Union[Dialect, Type[Dialect]]:
if isinstance(target, type):
if issubclass(target, Engine):
return Dialect
@@ -712,7 +790,13 @@ class DialectEvents(event.Events):
else:
return target
- def do_connect(self, dialect, conn_rec, cargs, cparams):
+ def do_connect(
+ self,
+ dialect: Dialect,
+ conn_rec: ConnectionPoolEntry,
+ cargs: Tuple[Any, ...],
+ cparams: Dict[str, Any],
+ ) -> Optional[DBAPIConnection]:
"""Receive connection arguments before a connection is made.
This event is useful in that it allows the handler to manipulate the
@@ -745,7 +829,13 @@ class DialectEvents(event.Events):
"""
- def do_executemany(self, cursor, statement, parameters, context):
+ def do_executemany(
+ self,
+ cursor: DBAPICursor,
+ statement: str,
+ parameters: _DBAPIMultiExecuteParams,
+ context: ExecutionContext,
+ ) -> Optional[Literal[True]]:
"""Receive a cursor to have executemany() called.
Return the value True to halt further events from invoking,
@@ -754,7 +844,9 @@ class DialectEvents(event.Events):
"""
- def do_execute_no_params(self, cursor, statement, context):
+ def do_execute_no_params(
+ self, cursor: DBAPICursor, statement: str, context: ExecutionContext
+ ) -> Optional[Literal[True]]:
"""Receive a cursor to have execute() with no parameters called.
Return the value True to halt further events from invoking,
@@ -763,7 +855,13 @@ class DialectEvents(event.Events):
"""
- def do_execute(self, cursor, statement, parameters, context):
+ def do_execute(
+ self,
+ cursor: DBAPICursor,
+ statement: str,
+ parameters: _DBAPISingleExecuteParams,
+ context: ExecutionContext,
+ ) -> Optional[Literal[True]]:
"""Receive a cursor to have execute() called.
Return the value True to halt further events from invoking,
@@ -773,8 +871,13 @@ class DialectEvents(event.Events):
"""
def do_setinputsizes(
- self, inputsizes, cursor, statement, parameters, context
- ):
+ self,
+ inputsizes: Dict[BindParameter[Any], Any],
+ cursor: DBAPICursor,
+ statement: str,
+ parameters: _DBAPIAnyExecuteParams,
+ context: ExecutionContext,
+ ) -> None:
"""Receive the setinputsizes dictionary for possible modification.
This event is emitted in the case where the dialect makes use of the