[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