summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2022-06-01 13:58:17 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2022-06-01 13:58:17 +0000
commit79dbe94bb4ccd75888d57f388195a3ba4fa6117e (patch)
tree818b37b36f1627cd1f0dd98413ac4b43479c48b5 /lib/sqlalchemy
parent01ffcf387f821e22986b760b15d9db2af3607ab4 (diff)
parentad14471bc99c2fb2315d3333a95dd3d7bf0a33a7 (diff)
downloadsqlalchemy-79dbe94bb4ccd75888d57f388195a3ba4fa6117e.tar.gz
Merge "Support handle_error for pre_ping" into main
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/engine/base.py50
-rw-r--r--lib/sqlalchemy/engine/default.py18
-rw-r--r--lib/sqlalchemy/engine/events.py271
-rw-r--r--lib/sqlalchemy/engine/interfaces.py24
-rw-r--r--lib/sqlalchemy/event/api.py2
-rw-r--r--lib/sqlalchemy/event/base.py4
-rw-r--r--lib/sqlalchemy/exc.py12
-rw-r--r--lib/sqlalchemy/orm/events.py12
-rw-r--r--lib/sqlalchemy/pool/events.py4
9 files changed, 221 insertions, 176 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index b2bdd6a8d..fdccf076d 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -1960,15 +1960,14 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
newraise = None
- if (
- self._has_events or self.engine._has_events
- ) and not self._execution_options.get(
+ if (self.dialect._has_events) and not self._execution_options.get(
"skip_user_error_events", False
):
ctx = ExceptionContextImpl(
e,
sqlalchemy_exception,
self.engine,
+ self.dialect,
self,
cursor,
statement,
@@ -1978,7 +1977,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
invalidate_pool_on_disconnect,
)
- for fn in self.dispatch.handle_error:
+ for fn in self.dialect.dispatch.handle_error:
try:
# handler returns an exception;
# call next handler in a chain
@@ -2040,13 +2039,19 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
@classmethod
def _handle_dbapi_exception_noconnection(
- cls, e: BaseException, dialect: Dialect, engine: Engine
+ cls,
+ e: BaseException,
+ dialect: Dialect,
+ engine: Optional[Engine] = None,
+ is_disconnect: Optional[bool] = None,
+ invalidate_pool_on_disconnect: bool = True,
) -> NoReturn:
exc_info = sys.exc_info()
- is_disconnect = isinstance(
- e, dialect.loaded_dbapi.Error
- ) and dialect.is_disconnect(e, None, None)
+ if is_disconnect is None:
+ is_disconnect = isinstance(
+ e, dialect.loaded_dbapi.Error
+ ) and dialect.is_disconnect(e, None, None)
should_wrap = isinstance(e, dialect.loaded_dbapi.Error)
@@ -2056,28 +2061,32 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
None,
cast(Exception, e),
dialect.loaded_dbapi.Error,
- hide_parameters=engine.hide_parameters,
+ hide_parameters=engine.hide_parameters
+ if engine is not None
+ else False,
connection_invalidated=is_disconnect,
+ dialect=dialect,
)
else:
sqlalchemy_exception = None
newraise = None
- if engine._has_events:
+ if dialect._has_events:
ctx = ExceptionContextImpl(
e,
sqlalchemy_exception,
engine,
+ dialect,
None,
None,
None,
None,
None,
is_disconnect,
- True,
+ invalidate_pool_on_disconnect,
)
- for fn in engine.dispatch.handle_error:
+ for fn in dialect.dispatch.handle_error:
try:
# handler returns an exception;
# call next handler in a chain
@@ -2121,11 +2130,27 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
class ExceptionContextImpl(ExceptionContext):
"""Implement the :class:`.ExceptionContext` interface."""
+ __slots__ = (
+ "connection",
+ "engine",
+ "dialect",
+ "cursor",
+ "statement",
+ "parameters",
+ "original_exception",
+ "sqlalchemy_exception",
+ "chained_exception",
+ "execution_context",
+ "is_disconnect",
+ "invalidate_pool_on_disconnect",
+ )
+
def __init__(
self,
exception: BaseException,
sqlalchemy_exception: Optional[exc.StatementError],
engine: Optional[Engine],
+ dialect: Dialect,
connection: Optional[Connection],
cursor: Optional[DBAPICursor],
statement: Optional[str],
@@ -2135,6 +2160,7 @@ class ExceptionContextImpl(ExceptionContext):
invalidate_pool_on_disconnect: bool,
):
self.engine = engine
+ self.dialect = dialect
self.connection = connection
self.sqlalchemy_exception = sqlalchemy_exception
self.original_exception = exception
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index c188e155c..04b017f08 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -610,7 +610,23 @@ class DefaultDialect(Dialect):
finally:
cursor.close()
except self.loaded_dbapi.Error as err:
- if self.is_disconnect(err, dbapi_connection, cursor):
+ is_disconnect = self.is_disconnect(err, dbapi_connection, cursor)
+
+ if self._has_events:
+ try:
+ Connection._handle_dbapi_exception_noconnection(
+ err,
+ self,
+ is_disconnect=is_disconnect,
+ invalidate_pool_on_disconnect=False,
+ )
+ except exc.StatementError as new_err:
+ is_disconnect = new_err.connection_invalidated
+
+ # other exceptions modified by the event handler will be
+ # thrown
+
+ if is_disconnect:
return False
else:
raise
diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py
index 8e4526d22..fb59193e4 100644
--- a/lib/sqlalchemy/engine/events.py
+++ b/lib/sqlalchemy/engine/events.py
@@ -125,8 +125,9 @@ class ConnectionEvents(event.Events[ConnectionEventsTarget]):
def _accept_with(
cls,
target: Union[ConnectionEventsTarget, Type[ConnectionEventsTarget]],
+ identifier: str,
) -> Optional[Union[ConnectionEventsTarget, Type[ConnectionEventsTarget]]]:
- default_dispatch = super()._accept_with(target)
+ default_dispatch = super()._accept_with(target, identifier)
if default_dispatch is None and hasattr(
target, "_no_async_engine_events"
):
@@ -147,7 +148,6 @@ class ConnectionEvents(event.Events[ConnectionEventsTarget]):
event_key.identifier,
event_key._listen_fn,
)
-
target._has_events = True
if not retval:
@@ -187,7 +187,6 @@ class ConnectionEvents(event.Events[ConnectionEventsTarget]):
elif retval and identifier not in (
"before_execute",
"before_cursor_execute",
- "handle_error",
):
raise exc.ArgumentError(
"Only the 'before_execute', "
@@ -369,139 +368,6 @@ class ConnectionEvents(event.Events[ConnectionEventsTarget]):
"""
- def handle_error(
- self, exception_context: ExceptionContext
- ) -> Optional[BaseException]:
- r"""Intercept all exceptions processed by the
- :class:`_engine.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 [1]_.
-
- 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.
-
- .. note::
-
- .. [1] The pool "pre_ping" handler enabled using the
- :paramref:`_sa.create_engine.pool_pre_ping` parameter does
- **not** consult this event before deciding if the "ping"
- returned false, as opposed to receiving an unhandled error.
- For this use case, the :ref:`legacy recipe based on
- engine_connect() may be used
- <pool_disconnects_pessimistic_custom>`. A future API allow
- more comprehensive customization of the "disconnect"
- detection mechanism across all functions.
-
- 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:`_events.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:`_events.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.Engine` fails during the initial
- call to :meth:`_engine.Engine.connect`, as well as when a
- :class:`_engine.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.
-
- """
-
@event._legacy_signature(
"2.0", ["conn", "branch"], converter=lambda conn: (conn, False)
)
@@ -793,8 +659,11 @@ class DialectEvents(event.Events[Dialect]):
@classmethod
def _accept_with(
- cls, target: Union[Engine, Type[Engine], Dialect, Type[Dialect]]
+ cls,
+ target: Union[Engine, Type[Engine], Dialect, Type[Dialect]],
+ identifier: str,
) -> Optional[Union[Dialect, Type[Dialect]]]:
+
if isinstance(target, type):
if issubclass(target, Engine):
return Dialect
@@ -804,11 +673,139 @@ class DialectEvents(event.Events[Dialect]):
return target.dialect
elif isinstance(target, Dialect):
return target
+ elif isinstance(target, Connection) and identifier == "handle_error":
+ raise exc.InvalidRequestError(
+ "The handle_error() event hook as of SQLAlchemy 2.0 is "
+ "established on the Dialect, and may only be applied to the "
+ "Engine as a whole or to a specific Dialect as a whole, "
+ "not on a per-Connection basis."
+ )
elif hasattr(target, "_no_async_engine_events"):
target._no_async_engine_events()
else:
return None
+ def handle_error(
+ self, exception_context: ExceptionContext
+ ) -> Optional[BaseException]:
+ r"""Intercept all exceptions processed by the
+ :class:`_engine.Dialect`, typically but not limited to those
+ emitted within the scope of a :class:`_engine.Connection`.
+
+ .. versionchanged:: 2.0 the :meth:`.DialectEvents.handle_error` event
+ is moved to the :class:`.DialectEvents` class, moved from the
+ :class:`.ConnectionEvents` class, so that it may also participate in
+ the "pre ping" operation configured with the
+ :paramref:`_sa.create_engine.pool_pre_ping` parameter. The event
+ remains registered by using the :class:`_engine.Engine` as the event
+ target, however note that using the :class:`_engine.Connection` as
+ an event target for :meth:`.DialectEvents.handle_error` is no longer
+ supported.
+
+ 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
+ * Establishing whether a DBAPI connection error message indicates
+ that the database connection needs to be reconnected, including
+ for the "pre_ping" handler used by **some** dialects
+ * Establishing or disabling whether a connection or the owning
+ connection pool is invalidated or expired in response to a
+ specific exception
+ * exception re-writing
+
+ 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.
+
+ As of SQLAlchemy 2.0, the "pre_ping" handler enabled using the
+ :paramref:`_sa.create_engine.pool_pre_ping` parameter will also
+ participate in the :meth:`.handle_error` process, **for those dialects
+ that rely upon disconnect codes to detect database liveness**. Note
+ that some dialects such as psycopg, psycopg2, and most MySQL dialects
+ make use of a native ``ping()`` method supplied by the DBAPI which does
+ not make use of disconnect codes.
+
+ 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:`_events.DialectEvents.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.
+
+
+ .. seealso::
+
+ :ref:`pool_new_disconnect_codes`
+
+ """
+
def do_connect(
self,
dialect: Dialect,
diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py
index e5414b70f..c1008dc56 100644
--- a/lib/sqlalchemy/engine/interfaces.py
+++ b/lib/sqlalchemy/engine/interfaces.py
@@ -2541,11 +2541,21 @@ class ExceptionContext:
"""Encapsulate information about an error condition in progress.
This object exists solely to be passed to the
- :meth:`_events.ConnectionEvents.handle_error` event,
+ :meth:`_events.DialectEvents.handle_error` event,
supporting an interface that
can be extended without backwards-incompatibility.
- .. versionadded:: 0.9.7
+
+ """
+
+ __slots__ = ()
+
+ dialect: Dialect
+ """The :class:`_engine.Dialect` in use.
+
+ This member is present for all invocations of the event hook.
+
+ .. versionadded:: 2.0
"""
@@ -2565,10 +2575,8 @@ class ExceptionContext:
engine: Optional[Engine]
"""The :class:`_engine.Engine` in use during the exception.
- This member should always be present, even in the case of a failure
- when first connecting.
-
- .. versionadded:: 1.0.0
+ This member is present in all cases except for when handling an error
+ within the connection pool "pre-ping" process.
"""
@@ -2646,7 +2654,7 @@ class ExceptionContext:
condition.
This flag will always be True or False within the scope of the
- :meth:`_events.ConnectionEvents.handle_error` handler.
+ :meth:`_events.DialectEvents.handle_error` handler.
SQLAlchemy will defer to this flag in order to determine whether or not
the connection should be invalidated subsequently. That is, by
@@ -2671,7 +2679,7 @@ class ExceptionContext:
when a "disconnect" condition is in effect.
Setting this flag to False within the scope of the
- :meth:`_events.ConnectionEvents.handle_error`
+ :meth:`_events.DialectEvents.handle_error`
event will have the effect such
that the full collection of connections in the pool will not be
invalidated during a disconnect; only the current connection that is the
diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py
index 52f796b19..c853d1f5e 100644
--- a/lib/sqlalchemy/event/api.py
+++ b/lib/sqlalchemy/event/api.py
@@ -29,7 +29,7 @@ def _event_key(
target: _ET, identifier: str, fn: _ListenerFnType
) -> _EventKey[_ET]:
for evt_cls in _registrars[identifier]:
- tgt = evt_cls._accept_with(target)
+ tgt = evt_cls._accept_with(target, identifier)
if tgt is not None:
return _EventKey(target, identifier, fn, tgt)
else:
diff --git a/lib/sqlalchemy/event/base.py b/lib/sqlalchemy/event/base.py
index 83b34a17f..7db3a4f6c 100644
--- a/lib/sqlalchemy/event/base.py
+++ b/lib/sqlalchemy/event/base.py
@@ -250,7 +250,7 @@ class _HasEventsDispatch(Generic[_ET]):
@classmethod
def _accept_with(
- cls, target: Union[_ET, Type[_ET]]
+ cls, target: Union[_ET, Type[_ET]], identifier: str
) -> Optional[Union[_ET, Type[_ET]]]:
raise NotImplementedError()
@@ -334,7 +334,7 @@ class Events(_HasEventsDispatch[_ET]):
@classmethod
def _accept_with(
- cls, target: Union[_ET, Type[_ET]]
+ cls, target: Union[_ET, Type[_ET]], identifier: str
) -> Optional[Union[_ET, Type[_ET]]]:
def dispatch_is(*types: Type[Any]) -> bool:
return all(isinstance(target.dispatch, t) for t in types)
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index a55d14025..88edba328 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -547,23 +547,19 @@ class DBAPIError(StatementError):
code = "dbapi"
- # I dont think I'm going to try to do overloads like this everywhere
- # in the library, but as this module is early days for me typing everything
- # I am sort of just practicing
-
@overload
@classmethod
def instance(
cls,
statement: Optional[str],
params: Optional[_AnyExecuteParams],
- orig: DontWrapMixin,
+ orig: Exception,
dbapi_base_err: Type[Exception],
hide_parameters: bool = False,
connection_invalidated: bool = False,
dialect: Optional["Dialect"] = None,
ismulti: Optional[bool] = None,
- ) -> DontWrapMixin:
+ ) -> StatementError:
...
@overload
@@ -572,13 +568,13 @@ class DBAPIError(StatementError):
cls,
statement: Optional[str],
params: Optional[_AnyExecuteParams],
- orig: Exception,
+ orig: DontWrapMixin,
dbapi_base_err: Type[Exception],
hide_parameters: bool = False,
connection_invalidated: bool = False,
dialect: Optional["Dialect"] = None,
ismulti: Optional[bool] = None,
- ) -> StatementError:
+ ) -> DontWrapMixin:
...
@overload
diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py
index 726ea79b5..7cc0aa9c9 100644
--- a/lib/sqlalchemy/orm/events.py
+++ b/lib/sqlalchemy/orm/events.py
@@ -62,7 +62,7 @@ class InstrumentationEvents(event.Events):
_dispatch_target = instrumentation.InstrumentationFactory
@classmethod
- def _accept_with(cls, target):
+ def _accept_with(cls, target, identifier):
if isinstance(target, type):
return _InstrumentationEventsHold(target)
else:
@@ -203,7 +203,7 @@ class InstanceEvents(event.Events):
@classmethod
@util.preload_module("sqlalchemy.orm")
- def _accept_with(cls, target):
+ def _accept_with(cls, target, identifier):
orm = util.preloaded.orm
if isinstance(target, instrumentation.ClassManager):
@@ -705,7 +705,7 @@ class MapperEvents(event.Events):
@classmethod
@util.preload_module("sqlalchemy.orm")
- def _accept_with(cls, target):
+ def _accept_with(cls, target, identifier):
orm = util.preloaded.orm
if target is orm.mapper:
@@ -1383,7 +1383,7 @@ class SessionEvents(event.Events[Session]):
return fn
@classmethod
- def _accept_with(cls, target):
+ def _accept_with(cls, target, identifier):
if isinstance(target, scoped_session):
target = target.session_factory
@@ -1409,7 +1409,7 @@ class SessionEvents(event.Events[Session]):
target._no_async_engine_events()
else:
# allows alternate SessionEvents-like-classes to be consulted
- return event.Events._accept_with(target)
+ return event.Events._accept_with(target, identifier)
@classmethod
def _listen(
@@ -2263,7 +2263,7 @@ class AttributeEvents(event.Events):
return dispatch
@classmethod
- def _accept_with(cls, target):
+ def _accept_with(cls, target, identifier):
# TODO: coverage
if isinstance(target, interfaces.MapperProperty):
return getattr(target.parent.class_, target.key)
diff --git a/lib/sqlalchemy/pool/events.py b/lib/sqlalchemy/pool/events.py
index 1107c92b5..47ab106d7 100644
--- a/lib/sqlalchemy/pool/events.py
+++ b/lib/sqlalchemy/pool/events.py
@@ -58,7 +58,9 @@ class PoolEvents(event.Events[Pool]):
@util.preload_module("sqlalchemy.engine")
@classmethod
def _accept_with(
- cls, target: Union[Pool, Type[Pool], Engine, Type[Engine]]
+ cls,
+ target: Union[Pool, Type[Pool], Engine, Type[Engine]],
+ identifier: str,
) -> Optional[Union[Pool, Type[Pool]]]:
if not typing.TYPE_CHECKING:
Engine = util.preloaded.engine.Engine