Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/events.py : 68%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# sqlalchemy/events.py
2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: http://www.opensource.org/licenses/mit-license.php
8"""Core event interfaces."""
10from . import event
11from . import exc
12from . import util
13from .engine import Connectable
14from .engine import Dialect
15from .engine import Engine
16from .pool import Pool
17from .sql.base import SchemaEventTarget
20class DDLEvents(event.Events):
21 """
22 Define event listeners for schema objects,
23 that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
24 subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`,
25 :class:`_schema.Column`.
27 :class:`_schema.MetaData` and :class:`_schema.Table` support events
28 specifically regarding when CREATE and DROP
29 DDL is emitted to the database.
31 Attachment events are also provided to customize
32 behavior whenever a child schema element is associated
33 with a parent, such as, when a :class:`_schema.Column` is associated
34 with its :class:`_schema.Table`, when a
35 :class:`_schema.ForeignKeyConstraint`
36 is associated with a :class:`_schema.Table`, etc.
38 Example using the ``after_create`` event::
40 from sqlalchemy import event
41 from sqlalchemy import Table, Column, Metadata, Integer
43 m = MetaData()
44 some_table = Table('some_table', m, Column('data', Integer))
46 def after_create(target, connection, **kw):
47 connection.execute("ALTER TABLE %s SET name=foo_%s" %
48 (target.name, target.name))
50 event.listen(some_table, "after_create", after_create)
52 DDL events integrate closely with the
53 :class:`.DDL` class and the :class:`.DDLElement` hierarchy
54 of DDL clause constructs, which are themselves appropriate
55 as listener callables::
57 from sqlalchemy import DDL
58 event.listen(
59 some_table,
60 "after_create",
61 DDL("ALTER TABLE %(table)s SET name=foo_%(table)s")
62 )
64 The methods here define the name of an event as well
65 as the names of members that are passed to listener
66 functions.
68 For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
69 will ensure that a given event handler is propagated to copies of the
70 object, which are made when using the :meth:`_schema.Table.tometadata`
71 method::
73 from sqlalchemy import DDL
74 event.listen(
75 some_table,
76 "after_create",
77 DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
78 propagate=True
79 )
81 new_table = some_table.tometadata(new_metadata)
83 The above :class:`.DDL` object will also be associated with the
84 :class:`_schema.Table` object represented by ``new_table``.
86 .. seealso::
88 :ref:`event_toplevel`
90 :class:`.DDLElement`
92 :class:`.DDL`
94 :ref:`schema_ddl_sequences`
96 """
98 _target_class_doc = "SomeSchemaClassOrObject"
99 _dispatch_target = SchemaEventTarget
101 def before_create(self, target, connection, **kw):
102 r"""Called before CREATE statements are emitted.
104 :param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
105 object which is the target of the event.
106 :param connection: the :class:`_engine.Connection` where the
107 CREATE statement or statements will be emitted.
108 :param \**kw: additional keyword arguments relevant
109 to the event. The contents of this dictionary
110 may vary across releases, and include the
111 list of tables being generated for a metadata-level
112 event, the checkfirst flag, and other
113 elements used by internal events.
115 :func:`.event.listen` also accepts the ``propagate=True``
116 modifier for this event; when True, the listener function will
117 be established for any copies made of the target object,
118 i.e. those copies that are generated when
119 :meth:`_schema.Table.tometadata` is used.
121 """
123 def after_create(self, target, connection, **kw):
124 r"""Called after CREATE statements are emitted.
126 :param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
127 object which is the target of the event.
128 :param connection: the :class:`_engine.Connection` where the
129 CREATE statement or statements have been emitted.
130 :param \**kw: additional keyword arguments relevant
131 to the event. The contents of this dictionary
132 may vary across releases, and include the
133 list of tables being generated for a metadata-level
134 event, the checkfirst flag, and other
135 elements used by internal events.
137 :func:`.event.listen` also accepts the ``propagate=True``
138 modifier for this event; when True, the listener function will
139 be established for any copies made of the target object,
140 i.e. those copies that are generated when
141 :meth:`_schema.Table.tometadata` is used.
143 """
145 def before_drop(self, target, connection, **kw):
146 r"""Called before DROP statements are emitted.
148 :param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
149 object which is the target of the event.
150 :param connection: the :class:`_engine.Connection` where the
151 DROP statement or statements will be emitted.
152 :param \**kw: additional keyword arguments relevant
153 to the event. The contents of this dictionary
154 may vary across releases, and include the
155 list of tables being generated for a metadata-level
156 event, the checkfirst flag, and other
157 elements used by internal events.
159 :func:`.event.listen` also accepts the ``propagate=True``
160 modifier for this event; when True, the listener function will
161 be established for any copies made of the target object,
162 i.e. those copies that are generated when
163 :meth:`_schema.Table.tometadata` is used.
165 """
167 def after_drop(self, target, connection, **kw):
168 r"""Called after DROP statements are emitted.
170 :param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
171 object which is the target of the event.
172 :param connection: the :class:`_engine.Connection` where the
173 DROP statement or statements have been emitted.
174 :param \**kw: additional keyword arguments relevant
175 to the event. The contents of this dictionary
176 may vary across releases, and include the
177 list of tables being generated for a metadata-level
178 event, the checkfirst flag, and other
179 elements used by internal events.
181 :func:`.event.listen` also accepts the ``propagate=True``
182 modifier for this event; when True, the listener function will
183 be established for any copies made of the target object,
184 i.e. those copies that are generated when
185 :meth:`_schema.Table.tometadata` is used.
187 """
189 def before_parent_attach(self, target, parent):
190 """Called before a :class:`.SchemaItem` is associated with
191 a parent :class:`.SchemaItem`.
193 :param target: the target object
194 :param parent: the parent to which the target is being attached.
196 :func:`.event.listen` also accepts the ``propagate=True``
197 modifier for this event; when True, the listener function will
198 be established for any copies made of the target object,
199 i.e. those copies that are generated when
200 :meth:`_schema.Table.tometadata` is used.
202 """
204 def after_parent_attach(self, target, parent):
205 """Called after a :class:`.SchemaItem` is associated with
206 a parent :class:`.SchemaItem`.
208 :param target: the target object
209 :param parent: the parent to which the target is being attached.
211 :func:`.event.listen` also accepts the ``propagate=True``
212 modifier for this event; when True, the listener function will
213 be established for any copies made of the target object,
214 i.e. those copies that are generated when
215 :meth:`_schema.Table.tometadata` is used.
217 """
219 def column_reflect(self, inspector, table, column_info):
220 """Called for each unit of 'column info' retrieved when
221 a :class:`_schema.Table` is being reflected.
223 The dictionary of column information as returned by the
224 dialect is passed, and can be modified. The dictionary
225 is that returned in each element of the list returned
226 by :meth:`.reflection.Inspector.get_columns`:
228 * ``name`` - the column's name
230 * ``type`` - the type of this column, which should be an instance
231 of :class:`~sqlalchemy.types.TypeEngine`
233 * ``nullable`` - boolean flag if the column is NULL or NOT NULL
235 * ``default`` - the column's server default value. This is
236 normally specified as a plain string SQL expression, however the
237 event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
238 or :func:`_expression.text` object as well.
240 .. versionchanged:: 1.1.6
242 The :meth:`.DDLEvents.column_reflect` event allows a non
243 string :class:`.FetchedValue`,
244 :func:`_expression.text`, or derived object to be
245 specified as the value of ``default`` in the column
246 dictionary.
248 * ``attrs`` - dict containing optional column attributes
250 The event is called before any action is taken against
251 this dictionary, and the contents can be modified.
252 The :class:`_schema.Column` specific arguments ``info``, ``key``,
253 and ``quote`` can also be added to the dictionary and
254 will be passed to the constructor of :class:`_schema.Column`.
256 Note that this event is only meaningful if either
257 associated with the :class:`_schema.Table` class across the
258 board, e.g.::
260 from sqlalchemy.schema import Table
261 from sqlalchemy import event
263 def listen_for_reflect(inspector, table, column_info):
264 "receive a column_reflect event"
265 # ...
267 event.listen(
268 Table,
269 'column_reflect',
270 listen_for_reflect)
272 ...or with a specific :class:`_schema.Table` instance using
273 the ``listeners`` argument::
275 def listen_for_reflect(inspector, table, column_info):
276 "receive a column_reflect event"
277 # ...
279 t = Table(
280 'sometable',
281 autoload=True,
282 listeners=[
283 ('column_reflect', listen_for_reflect)
284 ])
286 This because the reflection process initiated by ``autoload=True``
287 completes within the scope of the constructor for
288 :class:`_schema.Table`.
290 :func:`.event.listen` also accepts the ``propagate=True``
291 modifier for this event; when True, the listener function will
292 be established for any copies made of the target object,
293 i.e. those copies that are generated when
294 :meth:`_schema.Table.tometadata` is used.
296 """
299class PoolEvents(event.Events):
300 """Available events for :class:`_pool.Pool`.
302 The methods here define the name of an event as well
303 as the names of members that are passed to listener
304 functions.
306 e.g.::
308 from sqlalchemy import event
310 def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
311 "handle an on checkout event"
313 event.listen(Pool, 'checkout', my_on_checkout)
315 In addition to accepting the :class:`_pool.Pool` class and
316 :class:`_pool.Pool` instances, :class:`_events.PoolEvents` also accepts
317 :class:`_engine.Engine` objects and the :class:`_engine.Engine` class as
318 targets, which will be resolved to the ``.pool`` attribute of the
319 given engine or the :class:`_pool.Pool` class::
321 engine = create_engine("postgresql://scott:tiger@localhost/test")
323 # will associate with engine.pool
324 event.listen(engine, 'checkout', my_on_checkout)
326 """
328 _target_class_doc = "SomeEngineOrPool"
329 _dispatch_target = Pool
331 @classmethod
332 def _accept_with(cls, target):
333 if isinstance(target, type):
334 if issubclass(target, Engine):
335 return Pool
336 elif issubclass(target, Pool):
337 return target
338 elif isinstance(target, Engine):
339 return target.pool
340 else:
341 return target
343 def connect(self, dbapi_connection, connection_record):
344 """Called at the moment a particular DBAPI connection is first
345 created for a given :class:`_pool.Pool`.
347 This event allows one to capture the point directly after which
348 the DBAPI module-level ``.connect()`` method has been used in order
349 to produce a new DBAPI connection.
351 :param dbapi_connection: a DBAPI connection.
353 :param connection_record: the :class:`._ConnectionRecord` managing the
354 DBAPI connection.
356 """
358 def first_connect(self, dbapi_connection, connection_record):
359 """Called exactly once for the first time a DBAPI connection is
360 checked out from a particular :class:`_pool.Pool`.
362 The rationale for :meth:`_events.PoolEvents.first_connect`
363 is to determine
364 information about a particular series of database connections based
365 on the settings used for all connections. Since a particular
366 :class:`_pool.Pool`
367 refers to a single "creator" function (which in terms
368 of a :class:`_engine.Engine`
369 refers to the URL and connection options used),
370 it is typically valid to make observations about a single connection
371 that can be safely assumed to be valid about all subsequent
372 connections, such as the database version, the server and client
373 encoding settings, collation settings, and many others.
375 :param dbapi_connection: a DBAPI connection.
377 :param connection_record: the :class:`._ConnectionRecord` managing the
378 DBAPI connection.
380 """
382 def checkout(self, dbapi_connection, connection_record, connection_proxy):
383 """Called when a connection is retrieved from the Pool.
385 :param dbapi_connection: a DBAPI connection.
387 :param connection_record: the :class:`._ConnectionRecord` managing the
388 DBAPI connection.
390 :param connection_proxy: the :class:`._ConnectionFairy` object which
391 will proxy the public interface of the DBAPI connection for the
392 lifespan of the checkout.
394 If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
395 connection will be disposed and a fresh connection retrieved.
396 Processing of all checkout listeners will abort and restart
397 using the new connection.
399 .. seealso:: :meth:`_events.ConnectionEvents.engine_connect`
400 - a similar event
401 which occurs upon creation of a new :class:`_engine.Connection`.
403 """
405 def checkin(self, dbapi_connection, connection_record):
406 """Called when a connection returns to the pool.
408 Note that the connection may be closed, and may be None if the
409 connection has been invalidated. ``checkin`` will not be called
410 for detached connections. (They do not return to the pool.)
412 :param dbapi_connection: a DBAPI connection.
414 :param connection_record: the :class:`._ConnectionRecord` managing the
415 DBAPI connection.
417 """
419 def reset(self, dbapi_connection, connection_record):
420 """Called before the "reset" action occurs for a pooled connection.
422 This event represents
423 when the ``rollback()`` method is called on the DBAPI connection
424 before it is returned to the pool. The behavior of "reset" can
425 be controlled, including disabled, using the ``reset_on_return``
426 pool argument.
429 The :meth:`_events.PoolEvents.reset` event is usually followed by the
430 :meth:`_events.PoolEvents.checkin` event is called, except in those
431 cases where the connection is discarded immediately after reset.
433 :param dbapi_connection: a DBAPI connection.
435 :param connection_record: the :class:`._ConnectionRecord` managing the
436 DBAPI connection.
438 .. seealso::
440 :meth:`_events.ConnectionEvents.rollback`
442 :meth:`_events.ConnectionEvents.commit`
444 """
446 def invalidate(self, dbapi_connection, connection_record, exception):
447 """Called when a DBAPI connection is to be "invalidated".
449 This event is called any time the :meth:`._ConnectionRecord.invalidate`
450 method is invoked, either from API usage or via "auto-invalidation",
451 without the ``soft`` flag.
453 The event occurs before a final attempt to call ``.close()`` on the
454 connection occurs.
456 :param dbapi_connection: a DBAPI connection.
458 :param connection_record: the :class:`._ConnectionRecord` managing the
459 DBAPI connection.
461 :param exception: the exception object corresponding to the reason
462 for this invalidation, if any. May be ``None``.
464 .. versionadded:: 0.9.2 Added support for connection invalidation
465 listening.
467 .. seealso::
469 :ref:`pool_connection_invalidation`
471 """
473 def soft_invalidate(self, dbapi_connection, connection_record, exception):
474 """Called when a DBAPI connection is to be "soft invalidated".
476 This event is called any time the :meth:`._ConnectionRecord.invalidate`
477 method is invoked with the ``soft`` flag.
479 Soft invalidation refers to when the connection record that tracks
480 this connection will force a reconnect after the current connection
481 is checked in. It does not actively close the dbapi_connection
482 at the point at which it is called.
484 .. versionadded:: 1.0.3
486 """
488 def close(self, dbapi_connection, connection_record):
489 """Called when a DBAPI connection is closed.
491 The event is emitted before the close occurs.
493 The close of a connection can fail; typically this is because
494 the connection is already closed. If the close operation fails,
495 the connection is discarded.
497 The :meth:`.close` event corresponds to a connection that's still
498 associated with the pool. To intercept close events for detached
499 connections use :meth:`.close_detached`.
501 .. versionadded:: 1.1
503 """
505 def detach(self, dbapi_connection, connection_record):
506 """Called when a DBAPI connection is "detached" from a pool.
508 This event is emitted after the detach occurs. The connection
509 is no longer associated with the given connection record.
511 .. versionadded:: 1.1
513 """
515 def close_detached(self, dbapi_connection):
516 """Called when a detached DBAPI connection is closed.
518 The event is emitted before the close occurs.
520 The close of a connection can fail; typically this is because
521 the connection is already closed. If the close operation fails,
522 the connection is discarded.
524 .. versionadded:: 1.1
526 """
529class ConnectionEvents(event.Events):
530 """Available events for :class:`.Connectable`, which includes
531 :class:`_engine.Connection` and :class:`_engine.Engine`.
533 The methods here define the name of an event as well as the names of
534 members that are passed to listener functions.
536 An event listener can be associated with any :class:`.Connectable`
537 class or instance, such as an :class:`_engine.Engine`, e.g.::
539 from sqlalchemy import event, create_engine
541 def before_cursor_execute(conn, cursor, statement, parameters, context,
542 executemany):
543 log.info("Received statement: %s", statement)
545 engine = create_engine('postgresql://scott:tiger@localhost/test')
546 event.listen(engine, "before_cursor_execute", before_cursor_execute)
548 or with a specific :class:`_engine.Connection`::
550 with engine.begin() as conn:
551 @event.listens_for(conn, 'before_cursor_execute')
552 def before_cursor_execute(conn, cursor, statement, parameters,
553 context, executemany):
554 log.info("Received statement: %s", statement)
556 When the methods are called with a `statement` parameter, such as in
557 :meth:`.after_cursor_execute`, :meth:`.before_cursor_execute` and
558 :meth:`.dbapi_error`, the statement is the exact SQL string that was
559 prepared for transmission to the DBAPI ``cursor`` in the connection's
560 :class:`.Dialect`.
562 The :meth:`.before_execute` and :meth:`.before_cursor_execute`
563 events can also be established with the ``retval=True`` flag, which
564 allows modification of the statement and parameters to be sent
565 to the database. The :meth:`.before_cursor_execute` event is
566 particularly useful here to add ad-hoc string transformations, such
567 as comments, to all executions::
569 from sqlalchemy.engine import Engine
570 from sqlalchemy import event
572 @event.listens_for(Engine, "before_cursor_execute", retval=True)
573 def comment_sql_calls(conn, cursor, statement, parameters,
574 context, executemany):
575 statement = statement + " -- some comment"
576 return statement, parameters
578 .. note:: :class:`_events.ConnectionEvents` can be established on any
579 combination of :class:`_engine.Engine`, :class:`_engine.Connection`,
580 as well
581 as instances of each of those classes. Events across all
582 four scopes will fire off for a given instance of
583 :class:`_engine.Connection`. However, for performance reasons, the
584 :class:`_engine.Connection` object determines at instantiation time
585 whether or not its parent :class:`_engine.Engine` has event listeners
586 established. Event listeners added to the :class:`_engine.Engine`
587 class or to an instance of :class:`_engine.Engine`
588 *after* the instantiation
589 of a dependent :class:`_engine.Connection` instance will usually
590 *not* be available on that :class:`_engine.Connection` instance.
591 The newly
592 added listeners will instead take effect for
593 :class:`_engine.Connection`
594 instances created subsequent to those event listeners being
595 established on the parent :class:`_engine.Engine` class or instance.
597 :param retval=False: Applies to the :meth:`.before_execute` and
598 :meth:`.before_cursor_execute` events only. When True, the
599 user-defined event function must have a return value, which
600 is a tuple of parameters that replace the given statement
601 and parameters. See those methods for a description of
602 specific return arguments.
604 """
606 _target_class_doc = "SomeEngine"
607 _dispatch_target = Connectable
609 @classmethod
610 def _listen(cls, event_key, retval=False):
611 target, identifier, fn = (
612 event_key.dispatch_target,
613 event_key.identifier,
614 event_key._listen_fn,
615 )
617 target._has_events = True
619 if not retval:
620 if identifier == "before_execute":
621 orig_fn = fn
623 def wrap_before_execute(
624 conn, clauseelement, multiparams, params
625 ):
626 orig_fn(conn, clauseelement, multiparams, params)
627 return clauseelement, multiparams, params
629 fn = wrap_before_execute
630 elif identifier == "before_cursor_execute":
631 orig_fn = fn
633 def wrap_before_cursor_execute(
634 conn, cursor, statement, parameters, context, executemany
635 ):
636 orig_fn(
637 conn,
638 cursor,
639 statement,
640 parameters,
641 context,
642 executemany,
643 )
644 return statement, parameters
646 fn = wrap_before_cursor_execute
647 elif retval and identifier not in (
648 "before_execute",
649 "before_cursor_execute",
650 "handle_error",
651 ):
652 raise exc.ArgumentError(
653 "Only the 'before_execute', "
654 "'before_cursor_execute' and 'handle_error' engine "
655 "event listeners accept the 'retval=True' "
656 "argument."
657 )
658 event_key.with_wrapper(fn).base_listen()
660 def before_execute(self, conn, clauseelement, multiparams, params):
661 """Intercept high level execute() events, receiving uncompiled
662 SQL constructs and other objects prior to rendering into SQL.
664 This event is good for debugging SQL compilation issues as well
665 as early manipulation of the parameters being sent to the database,
666 as the parameter lists will be in a consistent format here.
668 This event can be optionally established with the ``retval=True``
669 flag. The ``clauseelement``, ``multiparams``, and ``params``
670 arguments should be returned as a three-tuple in this case::
672 @event.listens_for(Engine, "before_execute", retval=True)
673 def before_execute(conn, clauseelement, multiparams, params):
674 # do something with clauseelement, multiparams, params
675 return clauseelement, multiparams, params
677 :param conn: :class:`_engine.Connection` object
678 :param clauseelement: SQL expression construct, :class:`.Compiled`
679 instance, or string statement passed to
680 :meth:`_engine.Connection.execute`.
681 :param multiparams: Multiple parameter sets, a list of dictionaries.
682 :param params: Single parameter set, a single dictionary.
684 .. seealso::
686 :meth:`.before_cursor_execute`
688 """
690 def after_execute(self, conn, clauseelement, multiparams, params, result):
691 """Intercept high level execute() events after execute.
694 :param conn: :class:`_engine.Connection` object
695 :param clauseelement: SQL expression construct, :class:`.Compiled`
696 instance, or string statement passed to
697 :meth:`_engine.Connection.execute`.
698 :param multiparams: Multiple parameter sets, a list of dictionaries.
699 :param params: Single parameter set, a single dictionary.
700 :param result: :class:`_engine.ResultProxy` generated by the execution
701 .
703 """
705 def before_cursor_execute(
706 self, conn, cursor, statement, parameters, context, executemany
707 ):
708 """Intercept low-level cursor execute() events before execution,
709 receiving the string SQL statement and DBAPI-specific parameter list to
710 be invoked against a cursor.
712 This event is a good choice for logging as well as late modifications
713 to the SQL string. It's less ideal for parameter modifications except
714 for those which are specific to a target backend.
716 This event can be optionally established with the ``retval=True``
717 flag. The ``statement`` and ``parameters`` arguments should be
718 returned as a two-tuple in this case::
720 @event.listens_for(Engine, "before_cursor_execute", retval=True)
721 def before_cursor_execute(conn, cursor, statement,
722 parameters, context, executemany):
723 # do something with statement, parameters
724 return statement, parameters
726 See the example at :class:`_events.ConnectionEvents`.
728 :param conn: :class:`_engine.Connection` object
729 :param cursor: DBAPI cursor object
730 :param statement: string SQL statement, as to be passed to the DBAPI
731 :param parameters: Dictionary, tuple, or list of parameters being
732 passed to the ``execute()`` or ``executemany()`` method of the
733 DBAPI ``cursor``. In some cases may be ``None``.
734 :param context: :class:`.ExecutionContext` object in use. May
735 be ``None``.
736 :param executemany: boolean, if ``True``, this is an ``executemany()``
737 call, if ``False``, this is an ``execute()`` call.
739 .. seealso::
741 :meth:`.before_execute`
743 :meth:`.after_cursor_execute`
745 """
747 def after_cursor_execute(
748 self, conn, cursor, statement, parameters, context, executemany
749 ):
750 """Intercept low-level cursor execute() events after execution.
752 :param conn: :class:`_engine.Connection` object
753 :param cursor: DBAPI cursor object. Will have results pending
754 if the statement was a SELECT, but these should not be consumed
755 as they will be needed by the :class:`_engine.ResultProxy`.
756 :param statement: string SQL statement, as passed to the DBAPI
757 :param parameters: Dictionary, tuple, or list of parameters being
758 passed to the ``execute()`` or ``executemany()`` method of the
759 DBAPI ``cursor``. In some cases may be ``None``.
760 :param context: :class:`.ExecutionContext` object in use. May
761 be ``None``.
762 :param executemany: boolean, if ``True``, this is an ``executemany()``
763 call, if ``False``, this is an ``execute()`` call.
765 """
767 @util.deprecated(
768 "0.9",
769 "The :meth:`_events.ConnectionEvents.dbapi_error` "
770 "event is deprecated and will be removed in a future release. "
771 "Please refer to the :meth:`_events.ConnectionEvents.handle_error` "
772 "event.",
773 )
774 def dbapi_error(
775 self, conn, cursor, statement, parameters, context, exception
776 ):
777 """Intercept a raw DBAPI error.
779 This event is called with the DBAPI exception instance
780 received from the DBAPI itself, *before* SQLAlchemy wraps the
781 exception with it's own exception wrappers, and before any
782 other operations are performed on the DBAPI cursor; the
783 existing transaction remains in effect as well as any state
784 on the cursor.
786 The use case here is to inject low-level exception handling
787 into an :class:`_engine.Engine`, typically for logging and
788 debugging purposes.
790 .. warning::
792 Code should **not** modify
793 any state or throw any exceptions here as this will
794 interfere with SQLAlchemy's cleanup and error handling
795 routines. For exception modification, please refer to the
796 new :meth:`_events.ConnectionEvents.handle_error` event.
798 Subsequent to this hook, SQLAlchemy may attempt any
799 number of operations on the connection/cursor, including
800 closing the cursor, rolling back of the transaction in the
801 case of connectionless execution, and disposing of the entire
802 connection pool if a "disconnect" was detected. The
803 exception is then wrapped in a SQLAlchemy DBAPI exception
804 wrapper and re-thrown.
806 :param conn: :class:`_engine.Connection` object
807 :param cursor: DBAPI cursor object
808 :param statement: string SQL statement, as passed to the DBAPI
809 :param parameters: Dictionary, tuple, or list of parameters being
810 passed to the ``execute()`` or ``executemany()`` method of the
811 DBAPI ``cursor``. In some cases may be ``None``.
812 :param context: :class:`.ExecutionContext` object in use. May
813 be ``None``.
814 :param exception: The **unwrapped** exception emitted directly from the
815 DBAPI. The class here is specific to the DBAPI module in use.
817 """
819 def handle_error(self, exception_context):
820 r"""Intercept all exceptions processed by the
821 :class:`_engine.Connection`.
823 This includes all exceptions emitted by the DBAPI as well as
824 within SQLAlchemy's statement invocation process, including
825 encoding errors and other statement validation errors. Other areas
826 in which the event is invoked include transaction begin and end,
827 result row fetching, cursor creation.
829 Note that :meth:`.handle_error` may support new kinds of exceptions
830 and new calling scenarios at *any time*. Code which uses this
831 event must expect new calling patterns to be present in minor
832 releases.
834 To support the wide variety of members that correspond to an exception,
835 as well as to allow extensibility of the event without backwards
836 incompatibility, the sole argument received is an instance of
837 :class:`.ExceptionContext`. This object contains data members
838 representing detail about the exception.
840 Use cases supported by this hook include:
842 * read-only, low-level exception handling for logging and
843 debugging purposes
844 * exception re-writing
845 * Establishing or disabling whether a connection or the owning
846 connection pool is invalidated or expired in response to a
847 specific exception.
849 The hook is called while the cursor from the failed operation
850 (if any) is still open and accessible. Special cleanup operations
851 can be called on this cursor; SQLAlchemy will attempt to close
852 this cursor subsequent to this hook being invoked. If the connection
853 is in "autocommit" mode, the transaction also remains open within
854 the scope of this hook; the rollback of the per-statement transaction
855 also occurs after the hook is called.
857 For the common case of detecting a "disconnect" situation which
858 is not currently handled by the SQLAlchemy dialect, the
859 :attr:`.ExceptionContext.is_disconnect` flag can be set to True which
860 will cause the exception to be considered as a disconnect situation,
861 which typically results in the connection pool being invalidated::
863 @event.listens_for(Engine, "handle_error")
864 def handle_exception(context):
865 if isinstance(context.original_exception, pyodbc.Error):
866 for code in (
867 '08S01', '01002', '08003',
868 '08007', '08S02', '08001', 'HYT00', 'HY010'):
870 if code in str(context.original_exception):
871 context.is_disconnect = True
873 A handler function has two options for replacing
874 the SQLAlchemy-constructed exception into one that is user
875 defined. It can either raise this new exception directly, in
876 which case all further event listeners are bypassed and the
877 exception will be raised, after appropriate cleanup as taken
878 place::
880 @event.listens_for(Engine, "handle_error")
881 def handle_exception(context):
882 if isinstance(context.original_exception,
883 psycopg2.OperationalError) and \
884 "failed" in str(context.original_exception):
885 raise MySpecialException("failed operation")
887 .. warning:: Because the
888 :meth:`_events.ConnectionEvents.handle_error`
889 event specifically provides for exceptions to be re-thrown as
890 the ultimate exception raised by the failed statement,
891 **stack traces will be misleading** if the user-defined event
892 handler itself fails and throws an unexpected exception;
893 the stack trace may not illustrate the actual code line that
894 failed! It is advised to code carefully here and use
895 logging and/or inline debugging if unexpected exceptions are
896 occurring.
898 Alternatively, a "chained" style of event handling can be
899 used, by configuring the handler with the ``retval=True``
900 modifier and returning the new exception instance from the
901 function. In this case, event handling will continue onto the
902 next handler. The "chained" exception is available using
903 :attr:`.ExceptionContext.chained_exception`::
905 @event.listens_for(Engine, "handle_error", retval=True)
906 def handle_exception(context):
907 if context.chained_exception is not None and \
908 "special" in context.chained_exception.message:
909 return MySpecialException("failed",
910 cause=context.chained_exception)
912 Handlers that return ``None`` may be used within the chain; when
913 a handler returns ``None``, the previous exception instance,
914 if any, is maintained as the current exception that is passed onto the
915 next handler.
917 When a custom exception is raised or returned, SQLAlchemy raises
918 this new exception as-is, it is not wrapped by any SQLAlchemy
919 object. If the exception is not a subclass of
920 :class:`sqlalchemy.exc.StatementError`,
921 certain features may not be available; currently this includes
922 the ORM's feature of adding a detail hint about "autoflush" to
923 exceptions raised within the autoflush process.
925 :param context: an :class:`.ExceptionContext` object. See this
926 class for details on all available members.
928 .. versionadded:: 0.9.7 Added the
929 :meth:`_events.ConnectionEvents.handle_error` hook.
931 .. versionchanged:: 1.1 The :meth:`.handle_error` event will now
932 receive all exceptions that inherit from ``BaseException``,
933 including ``SystemExit`` and ``KeyboardInterrupt``. The setting for
934 :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case and
935 the default for
936 :attr:`.ExceptionContext.invalidate_pool_on_disconnect` is
937 ``False``.
939 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now
940 invoked when an :class:`_engine.Engine` fails during the initial
941 call to :meth:`_engine.Engine.connect`, as well as when a
942 :class:`_engine.Connection` object encounters an error during a
943 reconnect operation.
945 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is
946 not fired off when a dialect makes use of the
947 ``skip_user_error_events`` execution option. This is used
948 by dialects which intend to catch SQLAlchemy-specific exceptions
949 within specific operations, such as when the MySQL dialect detects
950 a table not present within the ``has_table()`` dialect method.
951 Prior to 1.0.0, code which implements :meth:`.handle_error` needs
952 to ensure that exceptions thrown in these scenarios are re-raised
953 without modification.
955 """
957 def engine_connect(self, conn, branch):
958 """Intercept the creation of a new :class:`_engine.Connection`.
960 This event is called typically as the direct result of calling
961 the :meth:`_engine.Engine.connect` method.
963 It differs from the :meth:`_events.PoolEvents.connect` method, which
964 refers to the actual connection to a database at the DBAPI level;
965 a DBAPI connection may be pooled and reused for many operations.
966 In contrast, this event refers only to the production of a higher level
967 :class:`_engine.Connection` wrapper around such a DBAPI connection.
969 It also differs from the :meth:`_events.PoolEvents.checkout` event
970 in that it is specific to the :class:`_engine.Connection` object,
971 not the
972 DBAPI connection that :meth:`_events.PoolEvents.checkout` deals with,
973 although
974 this DBAPI connection is available here via the
975 :attr:`_engine.Connection.connection` attribute.
976 But note there can in fact
977 be multiple :meth:`_events.PoolEvents.checkout`
978 events within the lifespan
979 of a single :class:`_engine.Connection` object, if that
980 :class:`_engine.Connection`
981 is invalidated and re-established. There can also be multiple
982 :class:`_engine.Connection`
983 objects generated for the same already-checked-out
984 DBAPI connection, in the case that a "branch" of a
985 :class:`_engine.Connection`
986 is produced.
988 :param conn: :class:`_engine.Connection` object.
989 :param branch: if True, this is a "branch" of an existing
990 :class:`_engine.Connection`. A branch is generated within the course
991 of a statement execution to invoke supplemental statements, most
992 typically to pre-execute a SELECT of a default value for the purposes
993 of an INSERT statement.
995 .. versionadded:: 0.9.0
997 .. seealso::
999 :ref:`pool_disconnects_pessimistic` - illustrates how to use
1000 :meth:`_events.ConnectionEvents.engine_connect`
1001 to transparently ensure pooled connections are connected to the
1002 database.
1004 :meth:`_events.PoolEvents.checkout`
1005 the lower-level pool checkout event
1006 for an individual DBAPI connection
1008 :meth:`_events.ConnectionEvents.set_connection_execution_options`
1009 - a copy
1010 of a :class:`_engine.Connection` is also made when the
1011 :meth:`_engine.Connection.execution_options` method is called.
1013 """
1015 def set_connection_execution_options(self, conn, opts):
1016 """Intercept when the :meth:`_engine.Connection.execution_options`
1017 method is called.
1019 This method is called after the new :class:`_engine.Connection`
1020 has been
1021 produced, with the newly updated execution options collection, but
1022 before the :class:`.Dialect` has acted upon any of those new options.
1024 Note that this method is not called when a new
1025 :class:`_engine.Connection`
1026 is produced which is inheriting execution options from its parent
1027 :class:`_engine.Engine`; to intercept this condition, use the
1028 :meth:`_events.ConnectionEvents.engine_connect` event.
1030 :param conn: The newly copied :class:`_engine.Connection` object
1032 :param opts: dictionary of options that were passed to the
1033 :meth:`_engine.Connection.execution_options` method.
1035 .. versionadded:: 0.9.0
1037 .. seealso::
1039 :meth:`_events.ConnectionEvents.set_engine_execution_options`
1040 - event
1041 which is called when :meth:`_engine.Engine.execution_options`
1042 is called.
1045 """
1047 def set_engine_execution_options(self, engine, opts):
1048 """Intercept when the :meth:`_engine.Engine.execution_options`
1049 method is called.
1051 The :meth:`_engine.Engine.execution_options` method produces a shallow
1052 copy of the :class:`_engine.Engine` which stores the new options.
1053 That new
1054 :class:`_engine.Engine` is passed here.
1055 A particular application of this
1056 method is to add a :meth:`_events.ConnectionEvents.engine_connect`
1057 event
1058 handler to the given :class:`_engine.Engine`
1059 which will perform some per-
1060 :class:`_engine.Connection` task specific to these execution options.
1062 :param conn: The newly copied :class:`_engine.Engine` object
1064 :param opts: dictionary of options that were passed to the
1065 :meth:`_engine.Connection.execution_options` method.
1067 .. versionadded:: 0.9.0
1069 .. seealso::
1071 :meth:`_events.ConnectionEvents.set_connection_execution_options`
1072 - event
1073 which is called when :meth:`_engine.Connection.execution_options`
1074 is
1075 called.
1077 """
1079 def engine_disposed(self, engine):
1080 """Intercept when the :meth:`_engine.Engine.dispose` method is called.
1082 The :meth:`_engine.Engine.dispose` method instructs the engine to
1083 "dispose" of it's connection pool (e.g. :class:`_pool.Pool`), and
1084 replaces it with a new one. Disposing of the old pool has the
1085 effect that existing checked-in connections are closed. The new
1086 pool does not establish any new connections until it is first used.
1088 This event can be used to indicate that resources related to the
1089 :class:`_engine.Engine` should also be cleaned up,
1090 keeping in mind that the
1091 :class:`_engine.Engine`
1092 can still be used for new requests in which case
1093 it re-acquires connection resources.
1095 .. versionadded:: 1.0.5
1097 """
1099 def begin(self, conn):
1100 """Intercept begin() events.
1102 :param conn: :class:`_engine.Connection` object
1104 """
1106 def rollback(self, conn):
1107 """Intercept rollback() events, as initiated by a
1108 :class:`.Transaction`.
1110 Note that the :class:`_pool.Pool` also "auto-rolls back"
1111 a DBAPI connection upon checkin, if the ``reset_on_return``
1112 flag is set to its default value of ``'rollback'``.
1113 To intercept this
1114 rollback, use the :meth:`_events.PoolEvents.reset` hook.
1116 :param conn: :class:`_engine.Connection` object
1118 .. seealso::
1120 :meth:`_events.PoolEvents.reset`
1122 """
1124 def commit(self, conn):
1125 """Intercept commit() events, as initiated by a
1126 :class:`.Transaction`.
1128 Note that the :class:`_pool.Pool` may also "auto-commit"
1129 a DBAPI connection upon checkin, if the ``reset_on_return``
1130 flag is set to the value ``'commit'``. To intercept this
1131 commit, use the :meth:`_events.PoolEvents.reset` hook.
1133 :param conn: :class:`_engine.Connection` object
1134 """
1136 def savepoint(self, conn, name):
1137 """Intercept savepoint() events.
1139 :param conn: :class:`_engine.Connection` object
1140 :param name: specified name used for the savepoint.
1142 """
1144 def rollback_savepoint(self, conn, name, context):
1145 """Intercept rollback_savepoint() events.
1147 :param conn: :class:`_engine.Connection` object
1148 :param name: specified name used for the savepoint.
1149 :param context: :class:`.ExecutionContext` in use. May be ``None``.
1151 """
1153 def release_savepoint(self, conn, name, context):
1154 """Intercept release_savepoint() events.
1156 :param conn: :class:`_engine.Connection` object
1157 :param name: specified name used for the savepoint.
1158 :param context: :class:`.ExecutionContext` in use. May be ``None``.
1160 """
1162 def begin_twophase(self, conn, xid):
1163 """Intercept begin_twophase() events.
1165 :param conn: :class:`_engine.Connection` object
1166 :param xid: two-phase XID identifier
1168 """
1170 def prepare_twophase(self, conn, xid):
1171 """Intercept prepare_twophase() events.
1173 :param conn: :class:`_engine.Connection` object
1174 :param xid: two-phase XID identifier
1175 """
1177 def rollback_twophase(self, conn, xid, is_prepared):
1178 """Intercept rollback_twophase() events.
1180 :param conn: :class:`_engine.Connection` object
1181 :param xid: two-phase XID identifier
1182 :param is_prepared: boolean, indicates if
1183 :meth:`.TwoPhaseTransaction.prepare` was called.
1185 """
1187 def commit_twophase(self, conn, xid, is_prepared):
1188 """Intercept commit_twophase() events.
1190 :param conn: :class:`_engine.Connection` object
1191 :param xid: two-phase XID identifier
1192 :param is_prepared: boolean, indicates if
1193 :meth:`.TwoPhaseTransaction.prepare` was called.
1195 """
1198class DialectEvents(event.Events):
1199 """event interface for execution-replacement functions.
1201 These events allow direct instrumentation and replacement
1202 of key dialect functions which interact with the DBAPI.
1204 .. note::
1206 :class:`.DialectEvents` hooks should be considered **semi-public**
1207 and experimental.
1208 These hooks are not for general use and are only for those situations
1209 where intricate re-statement of DBAPI mechanics must be injected onto
1210 an existing dialect. For general-use statement-interception events,
1211 please use the :class:`_events.ConnectionEvents` interface.
1213 .. seealso::
1215 :meth:`_events.ConnectionEvents.before_cursor_execute`
1217 :meth:`_events.ConnectionEvents.before_execute`
1219 :meth:`_events.ConnectionEvents.after_cursor_execute`
1221 :meth:`_events.ConnectionEvents.after_execute`
1224 .. versionadded:: 0.9.4
1226 """
1228 _target_class_doc = "SomeEngine"
1229 _dispatch_target = Dialect
1231 @classmethod
1232 def _listen(cls, event_key, retval=False):
1233 target = event_key.dispatch_target
1235 target._has_events = True
1236 event_key.base_listen()
1238 @classmethod
1239 def _accept_with(cls, target):
1240 if isinstance(target, type):
1241 if issubclass(target, Engine):
1242 return Dialect
1243 elif issubclass(target, Dialect):
1244 return target
1245 elif isinstance(target, Engine):
1246 return target.dialect
1247 else:
1248 return target
1250 def do_connect(self, dialect, conn_rec, cargs, cparams):
1251 """Receive connection arguments before a connection is made.
1253 Return a DBAPI connection to halt further events from invoking;
1254 the returned connection will be used.
1256 Alternatively, the event can manipulate the cargs and/or cparams
1257 collections; cargs will always be a Python list that can be mutated
1258 in-place and cparams a Python dictionary. Return None to
1259 allow control to pass to the next event handler and ultimately
1260 to allow the dialect to connect normally, given the updated
1261 arguments.
1263 .. versionadded:: 1.0.3
1265 .. seealso::
1267 :ref:`custom_dbapi_args`
1269 """
1271 def do_executemany(self, cursor, statement, parameters, context):
1272 """Receive a cursor to have executemany() called.
1274 Return the value True to halt further events from invoking,
1275 and to indicate that the cursor execution has already taken
1276 place within the event handler.
1278 """
1280 def do_execute_no_params(self, cursor, statement, context):
1281 """Receive a cursor to have execute() with no parameters called.
1283 Return the value True to halt further events from invoking,
1284 and to indicate that the cursor execution has already taken
1285 place within the event handler.
1287 """
1289 def do_execute(self, cursor, statement, parameters, context):
1290 """Receive a cursor to have execute() called.
1292 Return the value True to halt further events from invoking,
1293 and to indicate that the cursor execution has already taken
1294 place within the event handler.
1296 """
1298 def do_setinputsizes(
1299 self, inputsizes, cursor, statement, parameters, context
1300 ):
1301 """Receive the setinputsizes dictionary for possible modification.
1303 This event is emitted in the case where the dialect makes use of the
1304 DBAPI ``cursor.setinputsizes()`` method which passes information about
1305 parameter binding for a particular statement. The given
1306 ``inputsizes`` dictionary will contain :class:`.BindParameter` objects
1307 as keys, linked to DBAPI-specific type objects as values; for
1308 parameters that are not bound, they are added to the dictionary with
1309 ``None`` as the value, which means the parameter will not be included
1310 in the ultimate setinputsizes call. The event may be used to inspect
1311 and/or log the datatypes that are being bound, as well as to modify the
1312 dictionary in place. Parameters can be added, modified, or removed
1313 from this dictionary. Callers will typically want to inspect the
1314 :attr:`.BindParameter.type` attribute of the given bind objects in
1315 order to make decisions about the DBAPI object.
1317 After the event, the ``inputsizes`` dictionary is converted into
1318 an appropriate datastructure to be passed to ``cursor.setinputsizes``;
1319 either a list for a positional bound parameter execution style,
1320 or a dictionary of string parameter keys to DBAPI type objects for
1321 a named bound parameter execution style.
1323 Most dialects **do not use** this method at all; the only built-in
1324 dialect which uses this hook is the cx_Oracle dialect. The hook here
1325 is made available so as to allow customization of how datatypes are set
1326 up with the cx_Oracle DBAPI.
1328 .. versionadded:: 1.2.9
1330 .. seealso::
1332 :ref:`cx_oracle_setinputsizes`
1334 """
1335 pass