[Checkins] SVN: transaction/branches/chrism-job/transaction/ job decorator as per maillist discussion

Chris McDonough cvs-admin at zope.org
Sun Apr 1 04:47:53 UTC 2012


Log message for revision 124838:
  job decorator as per maillist discussion

Changed:
  U   transaction/branches/chrism-job/transaction/__init__.py
  U   transaction/branches/chrism-job/transaction/_manager.py
  D   transaction/branches/chrism-job/transaction/tests/test_attempt.py
  A   transaction/branches/chrism-job/transaction/tests/test_manager.py

-=-
Modified: transaction/branches/chrism-job/transaction/__init__.py
===================================================================
--- transaction/branches/chrism-job/transaction/__init__.py	2012-04-01 03:16:40 UTC (rev 124837)
+++ transaction/branches/chrism-job/transaction/__init__.py	2012-04-01 04:47:48 UTC (rev 124838)
@@ -35,3 +35,4 @@
 isDoomed = manager.isDoomed
 savepoint = manager.savepoint
 attempts = manager.attempts
+job = manager.job

Modified: transaction/branches/chrism-job/transaction/_manager.py
===================================================================
--- transaction/branches/chrism-job/transaction/_manager.py	2012-04-01 03:16:40 UTC (rev 124837)
+++ transaction/branches/chrism-job/transaction/_manager.py	2012-04-01 04:47:48 UTC (rev 124838)
@@ -21,6 +21,7 @@
 from transaction._transaction import Transaction
 from transaction.interfaces import TransientError
 
+import functools
 import threading
 
 # We have to remember sets of synch objects, especially Connections.
@@ -118,7 +119,40 @@
             if (should_retry is not None) and should_retry(error):
                 return True
 
+    def job(self, func=None, retries=0, manager=None):
+        if manager is None:
+            manager = self
+            
+        if func is None:
+            return lambda f: self.job(f, retries, manager)
 
+        @functools.wraps(func)
+        def wrapper():
+            note = func.__doc__
+            if note:
+                note = note.split('\n', 1)[0].strip()
+            else:
+                note = func.__name__
+
+            for i in range(retries + 1):
+                t = manager.begin()
+                if i:
+                    t.note("%s (retry: %s)" % (note, i))
+                else:
+                    t.note(note)
+
+                try:
+                    func(t)
+                    t.commit()
+                except TransientError:
+                    t.abort()
+                    if i >= retries:
+                        raise
+                else:
+                    break
+
+        return wrapper
+
 class ThreadTransactionManager(TransactionManager, threading.local):
     """Thread-aware transaction manager.
 

Deleted: transaction/branches/chrism-job/transaction/tests/test_attempt.py
===================================================================
--- transaction/branches/chrism-job/transaction/tests/test_attempt.py	2012-04-01 03:16:40 UTC (rev 124837)
+++ transaction/branches/chrism-job/transaction/tests/test_attempt.py	2012-04-01 04:47:48 UTC (rev 124838)
@@ -1,85 +0,0 @@
-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

Copied: transaction/branches/chrism-job/transaction/tests/test_manager.py (from rev 124837, transaction/branches/chrism-job/transaction/tests/test_attempt.py)
===================================================================
--- transaction/branches/chrism-job/transaction/tests/test_manager.py	                        (rev 0)
+++ transaction/branches/chrism-job/transaction/tests/test_manager.py	2012-04-01 04:47:48 UTC (rev 124838)
@@ -0,0 +1,232 @@
+import unittest
+
+class TestTransactionManager(unittest.TestCase):
+    def setUp(self):
+        self.txn = DummyTransaction()
+        self.begun = 0
+        
+    def _makeOne(self):
+        from transaction._manager import TransactionManager
+        return TransactionManager()
+
+    def begin(self):
+        self.begun += 1
+        return self.txn
+
+    def test_job_no_args(self):
+        inst = self._makeOne()
+        @inst.job
+        def func(tx):
+            pass
+        self.assertEqual(func.__name__, 'func')
+
+    def test_job_no_retries_no_exception(self):
+        inst = self._makeOne()
+        inst.begin = self.begin
+        L = []
+        @inst.job
+        def func(tx):
+            L.append(1)
+        func()
+        self.assertEqual(L, [1])
+        self.assertEqual(self.begun, 1)
+        self.assertEqual(self.txn.notes, ['func'])
+        self.assertEqual(self.txn.committed, 1)
+
+    def test_job_no_retries_no_exception_func_has_doc(self):
+        inst = self._makeOne()
+        inst.begin = self.begin
+        L = []
+        @inst.job
+        def func(tx):
+            """ doc\n a """
+            L.append(1)
+        func()
+        self.assertEqual(L, [1])
+        self.assertEqual(self.begun, 1)
+        self.assertEqual(self.txn.notes, ['doc'])
+        self.assertEqual(self.txn.committed, 1)
+        
+    def test_job_no_retries_with_retryable_exception_in_func(self):
+        from transaction.interfaces import TransientError
+        inst = self._makeOne()
+        inst.begin = self.begin
+        @inst.job
+        def func(tx):
+            raise TransientError
+        self.assertRaises(TransientError, func)
+        self.assertEqual(self.begun, 1)
+        self.assertEqual(self.txn.aborted, 1)
+        self.assertEqual(self.txn.notes, ['func'])
+
+    def test_job_no_retries_with_retryable_exception_in_commit(self):
+        from transaction.interfaces import TransientError
+        inst = self._makeOne()
+        inst.begin = self.begin
+        self.txn.commitraises = TransientError
+        L = []
+        @inst.job
+        def func(tx):
+            L.append(1)
+        self.assertRaises(TransientError, func)
+        self.assertEqual(L, [1])
+        self.assertEqual(self.begun, 1)
+        self.assertEqual(self.txn.aborted, 1)
+        self.assertEqual(self.txn.notes, ['func'])
+        self.assertEqual(self.txn.committed, 1)
+
+    def test_job_retries_with_retryable_exception_in_func(self):
+        from transaction.interfaces import TransientError
+        inst = self._makeOne()
+        inst.begin = self.begin
+        @inst.job(retries=2)
+        def func(tx):
+            if tx.aborted == 2:
+                return
+            raise TransientError
+        func()
+        self.assertEqual(self.begun, 3)
+        self.assertEqual(self.txn.aborted, 2)
+        self.assertEqual(self.txn.notes, 
+                         ['func', 'func (retry: 1)', 'func (retry: 2)'])
+        self.assertEqual(self.txn.committed, 1)
+
+    def test_job_retries_with_retryable_exception_in_commit(self):
+        from transaction.interfaces import TransientError
+        inst = self._makeOne()
+        inst.begin = self.begin
+        def commit():
+            if self.txn.aborted == 2:
+                self.txn.committed += 1
+                return
+            raise TransientError
+        self.txn.commit = commit
+        L = []
+        @inst.job(retries=2)
+        def func(tx):
+            L.append(1)
+        func()
+        self.assertEqual(L, [1, 1, 1])
+        self.assertEqual(self.begun, 3)
+        self.assertEqual(self.txn.aborted, 2)
+        self.assertEqual(self.txn.notes, 
+                         ['func', 'func (retry: 1)', 'func (retry: 2)'])
+        self.assertEqual(self.txn.committed, 1)
+
+    def test_job_other_transaction_manager(self):
+        inst = self._makeOne()
+        L = []
+        manager = self._makeOne()
+        manager.begin = self.begin
+        @inst.job(manager=manager)
+        def func(tx):
+            L.append(1)
+        func()
+        self.assertEqual(L, [1])
+        self.assertEqual(self.begun, 1)
+        self.assertEqual(self.txn.notes, ['func'])
+        self.assertEqual(self.txn.committed, 1)
+        
+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
+
+class DummyTransaction(object):
+
+    committed = 0
+    aborted = 0
+    commitraises = None
+    
+    def __init__(self):
+        self.notes = []
+        
+    def note(self, note):
+        self.notes.append(note)
+
+    def commit(self):
+        self.committed += 1
+        if self.commitraises:
+            raise self.commitraises
+
+    def abort(self):
+        self.aborted += 1



More information about the checkins mailing list