summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/events.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-07-18 20:23:01 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-07-18 20:23:01 -0400
commit3e55ed778b38d1ee18e1317b431c53ac1f6b141f (patch)
tree5c2c113162d7823b1e0c61e83372245510244f82 /lib/sqlalchemy/events.py
parente8ff3047c6596d39bb38956eb5aba5651c104e63 (diff)
downloadsqlalchemy-3e55ed778b38d1ee18e1317b431c53ac1f6b141f.tar.gz
- [feature] Connection event listeners can
now be associated with individual Connection objects, not just Engine objects. [ticket:2511]
Diffstat (limited to 'lib/sqlalchemy/events.py')
-rw-r--r--lib/sqlalchemy/events.py148
1 files changed, 80 insertions, 68 deletions
diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py
index a5ebb031a..10717ee50 100644
--- a/lib/sqlalchemy/events.py
+++ b/lib/sqlalchemy/events.py
@@ -17,11 +17,11 @@ class DDLEvents(event.Events):
that is, :class:`.SchemaItem` and :class:`.SchemaEvent`
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.
-
+ 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
@@ -37,14 +37,14 @@ class DDLEvents(event.Events):
some_table = Table('some_table', m, Column('data', Integer))
def after_create(target, connection, **kw):
- connection.execute("ALTER TABLE %s SET name=foo_%s" %
+ 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
+ DDL events integrate closely with the
:class:`.DDL` class and the :class:`.DDLElement` hierarchy
- of DDL clause constructs, which are themselves appropriate
+ of DDL clause constructs, which are themselves appropriate
as listener callables::
from sqlalchemy import DDL
@@ -81,7 +81,7 @@ class DDLEvents(event.Events):
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
+ event, the checkfirst flag, and other
elements used by internal events.
"""
@@ -97,7 +97,7 @@ class DDLEvents(event.Events):
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
+ event, the checkfirst flag, and other
elements used by internal events.
"""
@@ -113,7 +113,7 @@ class DDLEvents(event.Events):
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
+ event, the checkfirst flag, and other
elements used by internal events.
"""
@@ -129,52 +129,52 @@ class DDLEvents(event.Events):
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
+ event, the checkfirst flag, and other
elements used by internal events.
"""
def before_parent_attach(self, target, parent):
- """Called before a :class:`.SchemaItem` is associated with
+ """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 a modifier for this event:
-
+
:param propagate=False: 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
+ """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 a modifier for this event:
-
+
:param propagate=False: 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.
-
+ 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`.
-
+ is that returned in each element of the list returned
+ by :meth:`.reflection.Inspector.get_columns`.
+
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``,
@@ -182,45 +182,46 @@ class DDLEvents(event.Events):
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
+ 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',
+ 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',
+ '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`.
-
+
"""
class SchemaEventTarget(object):
- """Base class for elements that are the targets of :class:`.DDLEvents` events.
-
+ """Base class for elements that are the targets of :class:`.DDLEvents`
+ events.
+
This includes :class:`.SchemaItem` as well as :class:`.SchemaType`.
-
+
"""
dispatch = event.dispatcher(DDLEvents)
@@ -230,9 +231,9 @@ class SchemaEventTarget(object):
raise NotImplementedError()
def _set_parent_with_dispatch(self, parent):
- self.dispatch.before_parent_attach(self, parent)
- self._set_parent(parent)
- self.dispatch.after_parent_attach(self, parent)
+ self.dispatch.before_parent_attach(self, parent)
+ self._set_parent(parent)
+ self.dispatch.after_parent_attach(self, parent)
class PoolEvents(event.Events):
"""Available events for :class:`.Pool`.
@@ -250,11 +251,11 @@ class PoolEvents(event.Events):
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::
+ 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")
@@ -334,11 +335,14 @@ class PoolEvents(event.Events):
"""
class ConnectionEvents(event.Events):
- """Available events for :class:`.Connection`.
+ """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.
+ The methods here define the name of an event as well as the names of
+ members that are passed to listener functions.
- e.g.::
+ An event listener can be associated with any :class:`.Connectable`,
+ such as an :class:`.Engine`, e.g.::
from sqlalchemy import event, create_engine
@@ -348,15 +352,19 @@ class ConnectionEvents(event.Events):
engine = create_engine('postgresql://scott:tiger@localhost/test')
event.listen(engine, "before_execute", before_execute)
- Some events allow modifiers to the listen() function.
+ Some events allow modifiers to the :func:`.event.listen` function.
- :param retval=False: Applies to the :meth:`.before_execute` and
+ :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
+ is a tuple of parameters that replace the given statement
and parameters. See those methods for a description of
specific return arguments.
+ .. versionchanged:: 0.8 :class:`.ConnectionEvents` can now be associated
+ with any :class:`.Connectable` including :class:`.Connection`,
+ in addition to the existing support for :class:`.Engine`.
+
"""
@classmethod
@@ -366,20 +374,24 @@ class ConnectionEvents(event.Events):
if not retval:
if identifier == 'before_execute':
orig_fn = fn
- def wrap(conn, clauseelement, multiparams, params):
+
+ def wrap_before_execute(conn, clauseelement,
+ multiparams, params):
orig_fn(conn, clauseelement, multiparams, params)
return clauseelement, multiparams, params
- fn = wrap
+ fn = wrap_before_execute
elif identifier == 'before_cursor_execute':
orig_fn = fn
- def wrap(conn, cursor, statement,
+
+ def wrap_before_cursor_execute(conn, cursor, statement,
parameters, context, executemany):
- orig_fn(conn, cursor, statement,
+ orig_fn(conn, cursor, statement,
parameters, context, executemany)
return statement, parameters
- fn = wrap
+ fn = wrap_before_cursor_execute
- elif retval and identifier not in ('before_execute', 'before_cursor_execute'):
+ elif retval and \
+ identifier not in ('before_execute', 'before_cursor_execute'):
raise exc.ArgumentError(
"Only the 'before_execute' and "
"'before_cursor_execute' engine "
@@ -393,40 +405,40 @@ class ConnectionEvents(event.Events):
def after_execute(self, conn, clauseelement, multiparams, params, result):
"""Intercept high level execute() events."""
- def before_cursor_execute(self, conn, cursor, statement,
+ def before_cursor_execute(self, conn, cursor, statement,
parameters, context, executemany):
"""Intercept low-level cursor execute() events."""
- def after_cursor_execute(self, conn, cursor, statement,
+ def after_cursor_execute(self, conn, cursor, statement,
parameters, context, executemany):
"""Intercept low-level cursor execute() events."""
- def dbapi_error(self, conn, cursor, statement, parameters,
+ 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
+
+ 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. In general, user code should **not** modify
any state or throw any exceptions here as this will
interfere with SQLAlchemy's cleanup and error handling
routines.
-
+
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
+ 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.
-
+
.. versionadded:: 0.7.7
"""