[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