[Sqlalchemy-commits] sqlalchemy: - [feature] Added new connection event
Brought to you by:
zzzeek
From: <co...@sq...> - 2012-04-10 23:38:45
|
details: http://hg.sqlalchemy.org/sqlalchemy/sqlalchemy/rev/6bfddcfa68ae changeset: 8185:6bfddcfa68ae user: Mike Bayer <mi...@zz...> date: Tue Apr 10 19:38:22 2012 -0400 description: - [feature] Added new connection event dbapi_error(). Is called for all DBAPI-level errors passing the original DBAPI exception before SQLAlchemy modifies the state of the cursor. diffstat: CHANGES | 6 ++++++ lib/sqlalchemy/engine/base.py | 9 +++++++++ lib/sqlalchemy/events.py | 30 ++++++++++++++++++++++++++++++ test/engine/test_execute.py | 17 +++++++++++++++++ 4 files changed, 62 insertions(+), 0 deletions(-) diffs (106 lines): diff -r 64e57ff24c70 -r 6bfddcfa68ae CHANGES --- a/CHANGES Sun Apr 08 11:18:39 2012 -0400 +++ b/CHANGES Tue Apr 10 19:38:22 2012 -0400 @@ -35,6 +35,12 @@ as an Index could be a placeholder for just an index of a certain name. + - [feature] Added new connection event + dbapi_error(). Is called for all DBAPI-level + errors passing the original DBAPI exception + before SQLAlchemy modifies the state + of the cursor. + - mssql - [feature] Added interim create_engine flag supports_unicode_binds to PyODBC dialect, diff -r 64e57ff24c70 -r 6bfddcfa68ae lib/sqlalchemy/engine/base.py --- a/lib/sqlalchemy/engine/base.py Sun Apr 08 11:18:39 2012 -0400 +++ b/lib/sqlalchemy/engine/base.py Tue Apr 10 19:38:22 2012 -0400 @@ -1806,10 +1806,19 @@ (statement is not None and context is None) if should_wrap and context: + if self._has_events: + self.engine.dispatch.dbapi_error(self, + cursor, + statement, + parameters, + context, + e) context.handle_dbapi_exception(e) is_disconnect = isinstance(e, self.dialect.dbapi.Error) and \ self.dialect.is_disconnect(e, self.__connection, cursor) + + if is_disconnect: self.invalidate(e) self.engine.dispose() diff -r 64e57ff24c70 -r 6bfddcfa68ae lib/sqlalchemy/events.py --- a/lib/sqlalchemy/events.py Sun Apr 08 11:18:39 2012 -0400 +++ b/lib/sqlalchemy/events.py Tue Apr 10 19:38:22 2012 -0400 @@ -401,6 +401,36 @@ parameters, context, executemany): """Intercept low-level cursor execute() events.""" + def dbapi_error(self, conn, cursor, statement, parameters, + context, exception): + """Intercept a raw DBAPI error. + + This event is called with the DBAPI exception instance + received from the DBAPI itself, *before* SQLAlchemy wraps the + exception with it's own exception wrappers, and before any + other operations are performed on the DBAPI cursor; the + existing transaction remains in effect as well as any state + on the cursor. + + The use case here is to inject low-level exception handling + into an :class:`.Engine`, typically for logging and + debugging purposes. 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 + 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. + + New in 0.7.7. + + """ + def begin(self, conn): """Intercept begin() events.""" diff -r 64e57ff24c70 -r 6bfddcfa68ae test/engine/test_execute.py --- a/test/engine/test_execute.py Sun Apr 08 11:18:39 2012 -0400 +++ b/test/engine/test_execute.py Tue Apr 10 19:38:22 2012 -0400 @@ -984,6 +984,23 @@ e1.execute(select([1]).compile(dialect=e1.dialect)) e1._execute_compiled(select([1]).compile(dialect=e1.dialect), [], {}) + def test_exception_event(self): + engine = engines.testing_engine() + canary = [] + + @event.listens_for(engine, 'dbapi_error') + def err(conn, cursor, stmt, parameters, context, exception): + canary.append((stmt, parameters, exception)) + + conn = engine.connect() + try: + conn.execute("SELECT FOO FROM I_DONT_EXIST") + assert False + except tsa.exc.DBAPIError, e: + assert canary[0][2] is e.orig + assert canary[0][0] == "SELECT FOO FROM I_DONT_EXIST" + + @testing.fails_on('firebird', 'Data type unknown') def test_execute_events(self): |