[Checkins] SVN: transaction/trunk/ merge chrism-fix-attempts branch to trunk

Chris McDonough cvs-admin at zope.org
Thu Mar 29 22:25:30 UTC 2012


Log message for revision 124798:
  merge chrism-fix-attempts branch to trunk

Changed:
  U   transaction/trunk/CHANGES.txt
  U   transaction/trunk/transaction/_manager.py
  A   transaction/trunk/transaction/tests/test_attempt.py

-=-
Modified: transaction/trunk/CHANGES.txt
===================================================================
--- transaction/trunk/CHANGES.txt	2012-03-29 21:47:03 UTC (rev 124797)
+++ transaction/trunk/CHANGES.txt	2012-03-29 22:25:27 UTC (rev 124798)
@@ -6,6 +6,13 @@
 
 - Python 3.3 compatibility.
 
+- Fix "for attempt in transaction.attempts(x)" machinery, which would not
+  retry a transaction if its implicit call to ``.commit()`` itself raised a
+  transient error.  Symptom: seeing conflict errors even though you thought
+  you were retrying some number of times via the "attempts" machinery (the
+  first attempt to generate an exception during commit would cause that
+  exception to be raised).
+
 1.2.0 (2011-12-05)
 ------------------
 

Modified: transaction/trunk/transaction/_manager.py
===================================================================
--- transaction/trunk/transaction/_manager.py	2012-03-29 21:47:03 UTC (rev 124797)
+++ transaction/trunk/transaction/_manager.py	2012-03-29 22:25:27 UTC (rev 124798)
@@ -135,8 +135,15 @@
 
     def __exit__(self, t, v, tb):
         if v is None:
-            self.manager.commit()
+            try:
+                self.manager.commit()
+            except TransientError:
+                self.manager.abort()
+                return True # swallow
+            except:
+                self.manager.abort()
+                return False # don't swallow
         else:
             retry = self.manager._retryable(t, v)
             self.manager.abort()
-            return retry
+            return retry # swallow exception if True, else don't swallow

Copied: transaction/trunk/transaction/tests/test_attempt.py (from rev 124797, transaction/branches/chrism-fix-attempts/transaction/tests/test_attempt.py)
===================================================================
--- transaction/trunk/transaction/tests/test_attempt.py	                        (rev 0)
+++ transaction/trunk/transaction/tests/test_attempt.py	2012-03-29 22:25:27 UTC (rev 124798)
@@ -0,0 +1,85 @@
+import unittest
+
+class TestAttempt(unittest.TestCase):
+    def _makeOne(self, manager):
+        from transaction._manager import Attempt
+        return Attempt(manager)
+
+    def test___enter__(self):
+        manager = DummyManager()
+        inst = self._makeOne(manager)
+        inst.__enter__()
+        self.assertTrue(manager.entered)
+
+    def test___exit__no_exc_no_commit_exception(self):
+        manager = DummyManager()
+        inst = self._makeOne(manager)
+        result = inst.__exit__(None, None, None)
+        self.assertFalse(result)
+        self.assertTrue(manager.committed)
+
+    def test___exit__no_exc_nonretryable_commit_exception(self):
+        manager = DummyManager(raise_on_commit=ValueError)
+        inst = self._makeOne(manager)
+        result = inst.__exit__(None, None, None)
+        self.assertFalse(result)
+
+    def test___exit__no_exc_abort_exception_after_nonretryable_commit_exc(self):
+        manager = DummyManager(raise_on_abort=ValueError, 
+                               raise_on_commit=KeyError)
+        inst = self._makeOne(manager)
+        self.assertRaises(ValueError, inst.__exit__, None, None, None)
+        self.assertTrue(manager.committed)
+        self.assertTrue(manager.aborted)
+        
+    def test___exit__no_exc_retryable_commit_exception(self):
+        from transaction.interfaces import TransientError
+        manager = DummyManager(raise_on_commit=TransientError)
+        inst = self._makeOne(manager)
+        result = inst.__exit__(None, None, None)
+        self.assertTrue(result)
+        self.assertTrue(manager.committed)
+        self.assertTrue(manager.aborted)
+
+    def test___exit__with_exception_value_retryable(self):
+        from transaction.interfaces import TransientError
+        manager = DummyManager()
+        inst = self._makeOne(manager)
+        result = inst.__exit__(TransientError, TransientError(), None)
+        self.assertTrue(result)
+        self.assertFalse(manager.committed)
+        self.assertTrue(manager.aborted)
+
+    def test___exit__with_exception_value_nonretryable(self):
+        manager = DummyManager()
+        inst = self._makeOne(manager)
+        result = inst.__exit__(KeyError, KeyError(), None)
+        self.assertFalse(result)
+        self.assertFalse(manager.committed)
+        self.assertTrue(manager.aborted)
+        
+class DummyManager(object):
+    entered = False
+    committed = False
+    aborted = False
+    
+    def __init__(self, raise_on_commit=None, raise_on_abort=None):
+        self.raise_on_commit = raise_on_commit
+        self.raise_on_abort = raise_on_abort
+
+    def _retryable(self, t, v):
+        from transaction._manager import TransientError
+        return issubclass(t, TransientError)
+        
+    def __enter__(self):
+        self.entered = True
+
+    def abort(self):
+        self.aborted = True
+        if self.raise_on_abort:
+            raise self.raise_on_abort
+        
+    def commit(self):
+        self.committed = True
+        if self.raise_on_commit:
+            raise self.raise_on_commit



More information about the checkins mailing list