[Zodb-checkins]
SVN: ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/
Implements the ordering of before commit subscribers :
Julien Anguenot
ja at nuxeo.com
Mon Aug 8 12:34:29 EDT 2005
Log message for revision 37789:
Implements the ordering of before commit subscribers :
Compatibility notes :
o getBeforeCommitHooks() remove the hook's order values so it returns
the same data structure as before
o beforeCommitHook() and the order argument.
The issue here is that if the order argument is extracted from the
**kws then we would have to remove this argument before storing the
hook information and thus the order argument name will not be usable
from the __call__() method of the hook itself.
A beforeCommitHookOrdered(hook, order, *args, **kws) has been
created taking care of the insertion of the hook given the order. The
beforeCommitHook() method just call beforeCommitHookOrdered() with 0
as default value. Here, the goal is to provide backward compatibility
and a not an ugly implementation extracting the order from kws and then
deleting the entry when extracted before storing.
Notes about sorting and inserting :
- bisect.insort() can't be used since insort() will sort on the whole
tuple element which is no what we wanna do.
- an IOBTree use for the data structure will be a little bit overkill
and will complicate the implementation of the hooks that can call
other hooks at execution time. Plus de persistency is not necessarly in here.
Changed:
U ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/_transaction.py
U ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/interfaces.py
U ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/tests/test_transaction.py
-=-
Modified: ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/_transaction.py
===================================================================
--- ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/_transaction.py 2005-08-08 16:15:50 UTC (rev 37788)
+++ ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/_transaction.py 2005-08-08 16:34:29 UTC (rev 37789)
@@ -146,6 +146,8 @@
as their only argument.
"""
+from types import IntType
+
import logging
import sys
import thread
@@ -408,16 +410,27 @@
raise t, v, tb
def getBeforeCommitHooks(self):
- return iter(self._before_commit)
+ # Don't return the hook order value because of backward compatiblity.
+ return iter([(x[1], x[2], x[3]) for x in self._before_commit])
+ def beforeCommitHookOrdered(self, hook, order, *args, **kws):
+ if not isinstance(order, IntType):
+ order = 0
+ index = 0
+ for o, h, a, k in self._before_commit:
+ if order < o:
+ break
+ index += 1
+ self._before_commit.insert(index, (order, hook, args, kws))
+
def beforeCommitHook(self, hook, *args, **kws):
- self._before_commit.append((hook, args, kws))
+ self.beforeCommitHookOrdered(hook, 0, *args, **kws)
def _callBeforeCommitHooks(self):
# Call all hooks registered, allowing further registrations
# during processing.
while self._before_commit:
- hook, args, kws = self._before_commit.pop(0)
+ order, hook, args, kws = self._before_commit.pop(0)
hook(*args, **kws)
def _commitResources(self):
Modified: ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/interfaces.py
===================================================================
--- ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/interfaces.py 2005-08-08 16:15:50 UTC (rev 37788)
+++ ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/interfaces.py 2005-08-08 16:34:29 UTC (rev 37789)
@@ -192,6 +192,42 @@
instead.
"""
+ def beforeCommitHookOrdered(hook, order, *args, **kws):
+ """Register a hook to call before the transaction is committed.
+
+ The specified hook function will be called after the transaction's
+ commit method has been called, but before the commit process has been
+ started. The hook will be passed the specified positional and keyword
+ arguments.
+
+ Multiple hooks can be registered and will be called in the order they
+ were registered (first registered, first called) unless they
+ explicitly provide an argument named 'order' that will be used to
+ define the order in which the hooks will be invoked.
+
+ For instance, a hook registered with an order=1 will be invoked after
+ another hook registred within an order=-999999 and before another one
+ registred with an order=999999. If two hooks are registred with the
+ same order then those will be called in the order the were registred.
+
+ Note, a hook __call__() method can't define any 'order' argument since
+ this one is reserved by this method
+
+ This method can also be called from a hook: an executing hook can
+ register more hooks. Applications should take care to avoid creating
+ infinite loops by recursively registering hooks.
+
+ Hooks are called only for a top-level commit. A subtransaction
+ commit does not call any hooks. If the transaction is aborted, hooks
+ are not called, and are discarded. Calling a hook "consumes" its
+ registration too: hook registrations do not persist across
+ transactions. If it's desired to call the same hook on every
+ transaction commit, then beforeCommitHook() must be called with that
+ hook during every transaction; in such a case consider registering a
+ synchronizer object via a TransactionManager's registerSynch() method
+ instead.
+ """
+
def getBeforeCommitHooks():
"""Return iterable producing the registered beforeCommit hooks.
Modified: ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/tests/test_transaction.py
===================================================================
--- ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/tests/test_transaction.py 2005-08-08 16:15:50 UTC (rev 37788)
+++ ZODB/branches/anguenot-ordering-beforecommitsubscribers/src/transaction/tests/test_transaction.py 2005-08-08 16:34:29 UTC (rev 37789)
@@ -550,6 +550,116 @@
>>> reset_log()
"""
+def test_beforeCommitHookOrdered():
+ """Test the beforeCommitHookOrdered with order arguments.
+
+ Let's define a hook to call, and a way to see that it was called.
+
+ >>> log = []
+ >>> def reset_log():
+ ... del log[:]
+
+ >>> def hook(arg='no_arg', kw1='no_kw1', kw2='no_kw2'):
+ ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2))
+
+ Now register the hook within a transaction with an order explictly
+ equal to 0. (e.g : which is the default value)
+
+ >>> import transaction
+ >>> t = transaction.begin()
+ >>> t.beforeCommitHookOrdered(hook, 0, '1')
+
+ We can see that the hook is indeed registered.
+
+ >>> [(hook.func_name, args, kws)
+ ... for hook, args, kws in t.getBeforeCommitHooks()]
+ [('hook', ('1',), {})]
+
+ Let's add another one with a smaller order. It will be registered
+ to be call at first
+
+ >>> t.beforeCommitHookOrdered(hook, -999999, '2')
+ >>> [(hook.func_name, args, kws)
+ ... for hook, args, kws in t.getBeforeCommitHooks()]
+ [('hook', ('2',), {}), ('hook', ('1',), {})]
+
+ Let's add another one with a bigger order. It will be registered
+ to be call at last
+
+ >>> t.beforeCommitHookOrdered(hook, 999999, '3')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('1',), {})
+ ('hook', ('3',), {})
+
+ Above, we checked that the order parameter works as expected.
+ Now, we will check that the insertion with the same order values
+ respect the order of the registration.
+
+ >>> t.beforeCommitHookOrdered(hook, 0, '4')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('1',), {})
+ ('hook', ('4',), {})
+ ('hook', ('3',), {})
+
+ >>> t.beforeCommitHookOrdered(hook, 999999, '5')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('1',), {})
+ ('hook', ('4',), {})
+ ('hook', ('3',), {})
+ ('hook', ('5',), {})
+
+ >>> t.beforeCommitHookOrdered(hook, -999999, '6')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('6',), {})
+ ('hook', ('1',), {})
+ ('hook', ('4',), {})
+ ('hook', ('3',), {})
+ ('hook', ('5',), {})
+
+ Try to register an hook with an order value different than an
+ integer value. It will be replaced by the default order value (e.g
+ : 0)
+
+ >>> t.beforeCommitHookOrdered(hook, 'string_value', '7')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('6',), {})
+ ('hook', ('1',), {})
+ ('hook', ('4',), {})
+ ('hook', ('7',), {})
+ ('hook', ('3',), {})
+ ('hook', ('5',), {})
+
+ Ensure, the calls are made in the order of the registration
+ without taking the whole tuple while internal comparaison. For
+ instance bisect.insort() can't work in this case
+
+ >>> def hook2(arg='no_arg', kw1='no_kw1', kw2='no_kw2'):
+ ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2))
+
+ >>> t.beforeCommitHookOrdered(hook2, 0, '8')
+ >>> for hook, args, kws in t.getBeforeCommitHooks():
+ ... print (hook.func_name, args, kws)
+ ('hook', ('2',), {})
+ ('hook', ('6',), {})
+ ('hook', ('1',), {})
+ ('hook', ('4',), {})
+ ('hook', ('7',), {})
+ ('hook2', ('8',), {})
+ ('hook', ('3',), {})
+ ('hook', ('5',), {})
+
+ """
+
def test_suite():
from zope.testing.doctest import DocTestSuite
return unittest.TestSuite((
More information about the Zodb-checkins
mailing list