]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- fix test_single test to use default dialect
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 16 Sep 2010 02:20:01 +0000 (22:20 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 16 Sep 2010 02:20:01 +0000 (22:20 -0400)
- The exception raised by Session when it is used
subsequent to a subtransaction rollback (which is what
happens when a flush fails in autocommit=False mode) has
now been reworded (this is the "inactive due to a
rollback in a subtransaction" message). In particular,
if the rollback was due to an exception during flush(),
the message states this is the case, and reiterates the
string form of the original exception that occurred
during flush. If the session is closed due to explicit
usage of subtransactions (not very common), the message
just states this is the case.

- The exception raised by Mapper when repeated requests to
its initialization are made after initialization already
failed no longer assumes the "hasattr" case, since
there's other scenarios in which this message gets
emitted, and the message also does not compound onto
itself multiple times - you get the same message for
each attempt at usage. The misnomer "compiles" is being
traded out for "initialize".

CHANGES
lib/sqlalchemy/ext/declarative.py
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/session.py
test/ext/test_declarative.py
test/orm/inheritance/test_single.py
test/orm/test_mapper.py
test/orm/test_session.py

diff --git a/CHANGES b/CHANGES
index 9c1f0a1fc9287399f3a6c7ce9238e5a996a613d7..35255cba42a0c6bbfbc7ae17cd20f7411dd03100 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -15,6 +15,27 @@ CHANGES
   - Fixed a regression in 0.6.4 which occurred if you 
     passed an empty list to "include_properties" on 
     mapper() [ticket:1918]
+  
+  - The exception raised by Session when it is used
+    subsequent to a subtransaction rollback (which is what
+    happens when a flush fails in autocommit=False mode) has
+    now been reworded (this is the "inactive due to a
+    rollback in a subtransaction" message). In particular,
+    if the rollback was due to an exception during flush(),
+    the message states this is the case, and reiterates the
+    string form of the original exception that occurred
+    during flush. If the session is closed due to explicit
+    usage of subtransactions (not very common), the message
+    just states this is the case.
+
+  - The exception raised by Mapper when repeated requests to
+    its initialization are made after initialization already
+    failed no longer assumes the "hasattr" case, since
+    there's other scenarios in which this message gets
+    emitted, and the message also does not compound onto
+    itself multiple times - you get the same message for
+    each attempt at usage. The misnomer "compiles" is being
+    traded out for "initialize".
     
   - Added an assertion during flush which ensures
     that no NULL-holding identity keys were generated
@@ -42,11 +63,11 @@ CHANGES
     after a flush. The flag is only intended for very
     specific use cases.
     
-  - Slight improvement to the behavior of "passive_updates=False"
-    when placed only on the many-to-one side of a
-    relationship; documentation has been clarified
-    that passive_updates=False should really be on the 
-    one-to-many side.
+  - Slight improvement to the behavior of
+    "passive_updates=False" when placed only on the
+    many-to-one side of a relationship; documentation has
+    been clarified that passive_updates=False should really
+    be on the one-to-many side.
 
   - Placing passive_deletes=True on a many-to-one emits
     a warning, since you probably intended to put it on
index 6d4bdda437646992e0e42765442a6a2512e8c754..40263c7b802974700deb529d4a78111d2a3b2f75 100755 (executable)
@@ -1192,7 +1192,7 @@ def _deferred_relationship(cls, prop):
                     return x
             except NameError, n:
                 raise exceptions.InvalidRequestError(
-                    "When compiling mapper %s, expression %r failed to "
+                    "When initializing mapper %s, expression %r failed to "
                     "locate a name (%r). If this is a class name, consider "
                     "adding this relationship() to the %r class after "
                     "both dependent classes have been defined." % 
index 662cfc67b0b5badbb339e00f45d86676a7f96c60..4458a85470e8cb4c44cc5baf757f0386fda40ad8 100644 (file)
@@ -806,8 +806,10 @@ class DetectKeySwitch(DependencyProcessor):
                 if not issubclass(state.class_, self.parent.class_):
                     continue
                 dict_ = state.dict
-                related = state.get_impl(self.key).get(state, dict_, passive=self.passive_updates)
-                if related is not attributes.PASSIVE_NO_RESULT and related is not None:
+                related = state.get_impl(self.key).get(state, dict_,
+                        passive=self.passive_updates)
+                if related is not attributes.PASSIVE_NO_RESULT and \
+                    related is not None:
                     related_state = attributes.instance_state(dict_[self.key])
                     if related_state in switchers:
                         uowcommit.register_object(state, 
index 8d31dd89ad243539cfe34afb7b842df7d657a16e..49f5d2190f641cedd3772079975808b3b3367d5e 100644 (file)
@@ -30,6 +30,7 @@ from sqlalchemy.orm.util import (
      ExtensionCarrier, _INSTRUMENTOR, _class_to_mapper, 
      _state_mapper, class_mapper, instance_str, state_str,
      )
+import sys
 
 __all__ = (
     'Mapper',
@@ -787,12 +788,13 @@ class Mapper(object):
                     # the order of mapper compilation
                     for mapper in list(_mapper_registry):
                         if getattr(mapper, '_compile_failed', False):
-                            raise sa_exc.InvalidRequestError(
-                                    "One or more mappers failed to compile. "
-                                    "Exception was probably "
-                                    "suppressed within a hasattr() call. "
-                                    "Message was: %s" %
-                                    mapper._compile_failed)
+                            e = sa_exc.InvalidRequestError(
+                                    "One or more mappers failed to initialize - "
+                                    "can't proceed with initialization of other "
+                                    "mappers.  Original exception was: %s"
+                                    % mapper._compile_failed)
+                            e._compile_failed = mapper._compile_failed
+                            raise e
                         if not mapper.compiled:
                             mapper._post_configure_properties()
 
@@ -801,9 +803,9 @@ class Mapper(object):
                 finally:
                     _already_compiling = False
             except:
-                import sys
                 exc = sys.exc_info()[1]
-                self._compile_failed = exc
+                if not hasattr(exc, '_compile_failed'):
+                    self._compile_failed = exc
                 raise
         finally:
             self._expire_memoizations()
index 54b41fcc61bfcb8d31199215d962b6f631e4ffdd..bab98f4fafc3810dd9ce953d0010f4bdd5619be8 100644 (file)
@@ -22,6 +22,7 @@ from sqlalchemy.orm.util import (
 from sqlalchemy.orm.mapper import Mapper, _none_set
 from sqlalchemy.orm.unitofwork import UOWTransaction
 from sqlalchemy.orm import identity
+import sys
 
 __all__ = ['Session', 'SessionTransaction', 'SessionExtension']
 
@@ -206,7 +207,9 @@ class SessionTransaction(object):
       single: thread safety; SessionTransaction
 
     """
-
+    
+    _rollback_exception = None
+    
     def __init__(self, session, parent=None, nested=False):
         self.session = session
         self._connections = {}
@@ -229,9 +232,21 @@ class SessionTransaction(object):
     def _assert_is_active(self):
         self._assert_is_open()
         if not self._active:
-            raise sa_exc.InvalidRequestError(
-                "The transaction is inactive due to a rollback in a "
-                "subtransaction.  Issue rollback() to cancel the transaction.")
+            if self._rollback_exception:
+                raise sa_exc.InvalidRequestError(
+                    "This Session's transaction has been rolled back "
+                    "due to a previous exception during flush."
+                    " To begin a new transaction with this Session, "
+                    "first issue Session.rollback()."
+                    " Original exception was: %s"
+                    % self._rollback_exception
+                )
+            else:
+                raise sa_exc.InvalidRequestError(
+                    "This Session's transaction has been rolled back "
+                    "by a nested rollback() call.  To begin a new "
+                    "transaction, issue Session.rollback() first."
+                    )
 
     def _assert_is_open(self, error_msg="The transaction is closed"):
         if self.session is None:
@@ -288,14 +303,16 @@ class SessionTransaction(object):
         assert not self.session._deleted
 
         for s in self.session.identity_map.all_states():
-            _expire_state(s, s.dict, None, instance_dict=self.session.identity_map)
+            _expire_state(s, s.dict, None,
+                          instance_dict=self.session.identity_map)
 
     def _remove_snapshot(self):
         assert self._is_transaction_boundary
 
         if not self.nested and self.session.expire_on_commit:
             for s in self.session.identity_map.all_states():
-                _expire_state(s, s.dict, None, instance_dict=self.session.identity_map)
+                _expire_state(s, s.dict, None,
+                              instance_dict=self.session.identity_map)
 
     def _connection_for_bind(self, bind):
         self._assert_is_active()
@@ -379,7 +396,7 @@ class SessionTransaction(object):
         self.close()
         return self._parent
 
-    def rollback(self):
+    def rollback(self, _capture_exception=False):
         self._assert_is_open()
 
         stx = self.session.transaction
@@ -397,6 +414,8 @@ class SessionTransaction(object):
                     transaction._deactivate()
 
         self.close()
+        if self._parent and _capture_exception:
+            self._parent._rollback_exception = sys.exc_info()[1]
         return self._parent
 
     def _rollback_impl(self):
@@ -415,7 +434,8 @@ class SessionTransaction(object):
     def close(self):
         self.session.transaction = self._parent
         if self._parent is None:
-            for connection, transaction, autoclose in set(self._connections.values()):
+            for connection, transaction, autoclose in \
+                set(self._connections.values()):
                 if autoclose:
                     connection.close()
                 else:
@@ -1451,7 +1471,7 @@ class Session(object):
                 ext.after_flush(self, flush_context)
             transaction.commit()
         except:
-            transaction.rollback()
+            transaction.rollback(_capture_exception=True)
             raise
         
         flush_context.finalize_flush_changes()
index 26e1563fe24e73cb5b1bda8aa3aa8ac65f7eae2c..65ec92ca290e095497fec3d4e54973f02066ba4e 100644 (file)
@@ -369,11 +369,14 @@ class DeclarativeTest(DeclarativeTestBase):
 
         hasattr(User.addresses, 'property')
 
-        # the exeption is preserved
-
-        assert_raises_message(sa.exc.InvalidRequestError,
-                              r"suppressed within a hasattr\(\)",
-                              compile_mappers)
+        # the exception is preserved.  Remains the 
+        # same through repeated calls.
+        for i in range(3):
+            assert_raises_message(sa.exc.InvalidRequestError,
+                            "^One or more mappers failed to initialize - "
+                            "can't proceed with initialization of other "
+                            "mappers.  Original exception was: When initializing.*",
+                            compile_mappers)
 
     def test_custom_base(self):
         class MyBase(object):
index 42043095402f62beb030519d84b4b38b7f069bf1..2efde2b32ab2359bc343d23066bd71ee3862727b 100644 (file)
@@ -252,10 +252,12 @@ class RelationshipFromSingleTest(testing.AssertsCompiledSQL, MappedTest):
                             'employee_stuff_name, anon_1.employee_id '
                             'AS anon_1_employee_id FROM (SELECT '
                             'employee.id AS employee_id FROM employee '
-                            'WHERE employee.type IN (?)) AS anon_1 '
+                            'WHERE employee.type IN (:type_1)) AS anon_1 '
                             'JOIN employee_stuff ON anon_1.employee_id '
                             '= employee_stuff.employee_id ORDER BY '
-                            'anon_1.employee_id')
+                            'anon_1.employee_id',
+                            use_default_dialect=True
+                            )
 
 class RelationshipToSingleTest(MappedTest):
     @classmethod
index a19af5f4f4d4b1bb63342bf2e98a06b46e3ce615..f041c8896bdf39719cd307b441d6dd7c8865f9cf 100644 (file)
@@ -88,14 +88,25 @@ class MapperTest(_fixtures.FixtureTest):
 
     @testing.resolve_artifact_names
     def test_exceptions_sticky(self):
-        """test preservation of mapper compile errors raised during hasattr()."""
+        """test preservation of mapper compile errors raised during hasattr(),
+        as well as for redundant mapper compile calls.  Test that 
+        repeated calls don't stack up error messages.
+        
+        """
         
         mapper(Address, addresses, properties={
             'user':relationship(User)
         })
         
         hasattr(Address.user, 'property')
-        assert_raises_message(sa.exc.InvalidRequestError, r"suppressed within a hasattr\(\)", compile_mappers)
+        for i in range(3):
+            assert_raises_message(sa.exc.InvalidRequestError,
+                                  "^One or more mappers failed to "
+                                  "initialize - can't proceed with "
+                                  "initialization of other mappers.  "
+                                  "Original exception was: Class "
+                                  "'test.orm._fixtures.User' is not mapped$"
+                                  , compile_mappers)
     
     @testing.resolve_artifact_names
     def test_column_prefix(self):
@@ -157,13 +168,15 @@ class MapperTest(_fixtures.FixtureTest):
 
     @testing.resolve_artifact_names
     def test_column_not_present(self):
-        assert_raises_message(sa.exc.ArgumentError, "not represented in the mapper's table", mapper, User, users, properties={
-            'foo':addresses.c.user_id
-        })
+        assert_raises_message(sa.exc.ArgumentError,
+                              "not represented in the mapper's table",
+                              mapper, User, users, properties={'foo'
+                              : addresses.c.user_id})
         
     @testing.resolve_artifact_names
     def test_bad_constructor(self):
         """If the construction of a mapped class fails, the instance does not get placed in the session"""
+        
         class Foo(object):
             def __init__(self, one, two, _sa_session=None):
                 pass
index b7ae104a163851e562a00d19a18ae185f2ceb7d1..6ac42a6b38497e691a65ef82797e496858bfc5d0 100644 (file)
@@ -713,11 +713,38 @@ class SessionTest(_fixtures.FixtureTest):
         sess.flush()
         sess.rollback()
         assert_raises_message(sa.exc.InvalidRequestError,
-                              'inactive due to a rollback in a '
-                              'subtransaction', sess.begin,
-                              subtransactions=True)
+                              "This Session's transaction has been "
+                              r"rolled back by a nested rollback\(\) "
+                              "call.  To begin a new transaction, "
+                              r"issue Session.rollback\(\) first.",
+                              sess.begin, subtransactions=True)
         sess.close()
 
+    @testing.resolve_artifact_names
+    def test_preserve_flush_error(self):
+        mapper(User, users)
+        sess = Session()
+
+        sess.add(User(id=5))
+        assert_raises(
+            sa.exc.DBAPIError,
+            sess.commit
+        )
+        
+        for i in range(5):
+            assert_raises_message(sa.exc.InvalidRequestError,
+                              "^This Session's transaction has been "
+                              r"rolled back due to a previous exception during flush. To "
+                              "begin a new transaction with this "
+                              "Session, first issue "
+                              r"Session.rollback\(\). Original exception "
+                              "was:",
+                              sess.commit)
+        sess.rollback()
+        sess.add(User(id=5, name='some name'))
+        sess.commit()
+        
+        
     @testing.resolve_artifact_names
     def test_no_autocommit_with_explicit_commit(self):
         mapper(User, users)