]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added mixin class sqlalchemy.ext.DontWrapMixin.
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Jun 2011 15:45:28 +0000 (11:45 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Jun 2011 15:45:28 +0000 (11:45 -0400)
User-defined exceptions of this type are never
wrapped in StatementException when they
occur in the context of a statement
execution.

- StatementException wrapping will display the
original exception class in the message.

CHANGES
lib/sqlalchemy/exc.py
lib/sqlalchemy/util/langhelpers.py
test/engine/test_execute.py

diff --git a/CHANGES b/CHANGES
index 49fa1e576f7bedd54278a3aba04cf93b84ee4238..e6ae6f1310fdb7f496905a32b780301e63e81b16 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -34,6 +34,15 @@ CHANGES
     no deprecation warning about cgi.parse_qsl()
     [ticket:1682]
 
+  - Added mixin class sqlalchemy.ext.DontWrapMixin.
+    User-defined exceptions of this type are never
+    wrapped in StatementException when they 
+    occur in the context of a statement 
+    execution.
+
+ - StatementException wrapping will display the
+   original exception class in the message.  
+
 - mssql
   - Adjusted the pyodbc dialect such that bound
     values are passed as bytes and not unicode
index 3e88ee3a6c19ad80dcfa1a95ae99c47410bf16b2..a7ea5b0e2251de04f7f09596f77f0364034a65ff 100644 (file)
@@ -12,6 +12,7 @@ result of DBAPI exceptions are all subclasses of
 
 """
 
+import traceback
 
 class SQLAlchemyError(Exception):
     """Generic error class."""
@@ -98,6 +99,26 @@ class UnboundExecutionError(InvalidRequestError):
     """SQL was attempted without a database connection to execute it on."""
 
 
+class DontWrapMixin(object):
+    """A mixin class which, when applied to a user-defined Exception class,
+    will not be wrapped inside of :class:`.StatementError` if the error is
+    emitted within the process of executing a statement.
+    
+    E.g.::
+        from sqlalchemy.exc import DontWrapMixin
+        
+        class MyCustomException(Exception, DontWrapMixin):
+            pass
+        
+        class MySpecialType(TypeDecorator):
+            impl = String
+            
+            def process_bind_param(self, value, dialect):
+                if value == 'invalid':
+                    raise MyCustomException("invalid!")
+            
+    """
+
 # Moved to orm.exc; compatibility definition installed by orm import until 0.6
 UnmappedColumnError = None
 
@@ -160,14 +181,18 @@ class DBAPIError(StatementError):
                         connection_invalidated=False):
         # Don't ever wrap these, just return them directly as if
         # DBAPIError didn't exist.
-        if isinstance(orig, (KeyboardInterrupt, SystemExit)):
+        if isinstance(orig, (KeyboardInterrupt, SystemExit, DontWrapMixin)):
             return orig
 
         if orig is not None:
             # not a DBAPI error, statement is present.
             # raise a StatementError
             if not isinstance(orig, dbapi_base_err) and statement:
-                return StatementError(str(orig), statement, params, orig)
+                return StatementError(
+                            "%s (original cause: %s)" % (
+                                str(orig), 
+                                traceback.format_exception_only(orig.__class__, orig)[-1].strip()
+                            ), statement, params, orig)
 
             name, glob = orig.__class__.__name__, globals()
             if name in glob and issubclass(glob[name], DBAPIError):
index ba612bc2c7ce106f09049b756d032b82b49d0c28..e9213845baa00c4fbde2d04e663198639ed4a0d6 100644 (file)
@@ -53,7 +53,6 @@ def decorator(target):
     return update_wrapper(decorate, target)
 
 
-
 def get_cls_kwargs(cls):
     """Return the full set of inherited kwargs for the given `cls`.
 
index 33b34ca1b35def5be3b0db01888b96eca5107729..967fc92426d37701f97adf90eb351d7e022dfb61 100644 (file)
@@ -174,7 +174,34 @@ class ExecuteTest(fixtures.TestBase):
         def _go(conn):
             assert_raises_message(
                 tsa.exc.StatementError,
-                "nope 'SELECT 1 ",
+                r"nope \(original cause: Exception: nope\) 'SELECT 1 ",
+                conn.execute,
+                    select([1]).\
+                        where(
+                            column('foo') == literal('bar', MyType())
+                        )
+            )
+        _go(testing.db)
+        conn = testing.db.connect()
+        try:
+            _go(conn)
+        finally:
+            conn.close()
+
+    @testing.requires.python25
+    def test_dont_wrap_mixin(self):
+        class MyException(Exception, tsa.exc.DontWrapMixin):
+            pass
+
+        class MyType(TypeDecorator):
+            impl = Integer
+            def process_bind_param(self, value, dialect):
+                raise MyException("nope")
+
+        def _go(conn):
+            assert_raises_message(
+                MyException,
+                "nope",
                 conn.execute,
                     select([1]).\
                         where(