Commit 21a9d98f authored by Tim Peters's avatar Tim Peters

In accord with the endless debate on zodb-dev, remove the ``order=``

argument from addBeforeCommitHook().
parent 252441d2
...@@ -10,12 +10,10 @@ Zope3 development). These are the dates of the internal releases: ...@@ -10,12 +10,10 @@ Zope3 development). These are the dates of the internal releases:
Commit hooks Commit hooks
------------ ------------
- (3.6a1) A new ``addBeforeCommitHook()`` method of transactions generalizes - (3.6a1) The ``beforeCommitHook()`` method has been replaced by the new
the older ``beforeCommitHook()`` method, with a more-robust signature ``addBeforeCommitHook()`` method, with a more-robust signature.
and an optional new ``order`` argument to influence the order in which ``beforeCommitHook()`` is now deprecated, and will be removed in ZODB 3.8.
commit hooks are invoked. ``beforeCommitHook()`` is now deprecated, and Thanks to Julien Anguenot for contributing code and tests.
will be removed in ZODB 3.7. Thanks to Julien Anguenot for contributing
code and tests.
PersistentMapping PersistentMapping
----------------- -----------------
......
...@@ -146,7 +146,6 @@ committed or aborted. The methods are passed the current Transaction ...@@ -146,7 +146,6 @@ committed or aborted. The methods are passed the current Transaction
as their only argument. as their only argument.
""" """
import bisect
import logging import logging
import sys import sys
import thread import thread
...@@ -239,16 +238,8 @@ class Transaction(object): ...@@ -239,16 +238,8 @@ class Transaction(object):
# raised, incorporating this traceback. # raised, incorporating this traceback.
self._failure_traceback = None self._failure_traceback = None
# List of (order, index, hook, args, kws) tuples added by # List of (hook, args, kws) tuples added by addbeforeCommitHook().
# addbeforeCommitHook(). `index` is used to resolve ties on equal
# `order` values, preserving the order in which the hooks were
# registered. Each time we append a tuple to _before_commit,
# the current value of _before_commit_index is used for the
# index, and then the latter is incremented by 1.
# TODO: in Python 2.4, change to collections.deque; lists can be
# inefficient for FIFO access of this kind.
self._before_commit = [] self._before_commit = []
self._before_commit_index = 0
# Raise TransactionFailedError, due to commit()/join()/register() # Raise TransactionFailedError, due to commit()/join()/register()
# getting called when the current transaction has already suffered # getting called when the current transaction has already suffered
...@@ -415,34 +406,27 @@ class Transaction(object): ...@@ -415,34 +406,27 @@ class Transaction(object):
raise t, v, tb raise t, v, tb
def getBeforeCommitHooks(self): def getBeforeCommitHooks(self):
# Don't return the hook order and index values because of return iter(self._before_commit)
# backward compatibility, and because they're internal details.
return iter([x[2:] for x in self._before_commit]) def addBeforeCommitHook(self, hook, args=(), kws=None):
def addBeforeCommitHook(self, hook, args=(), kws=None, order=0):
if not isinstance(order, int):
raise ValueError("An integer value is required "
"for the order argument")
if kws is None: if kws is None:
kws = {} kws = {}
bisect.insort(self._before_commit, (order, self._before_commit_index, self._before_commit.append((hook, tuple(args), kws))
hook, tuple(args), kws))
self._before_commit_index += 1
def beforeCommitHook(self, hook, *args, **kws): def beforeCommitHook(self, hook, *args, **kws):
from ZODB.utils import deprecated38 from ZODB.utils import deprecated38
deprecated38("Use addBeforeCommitHook instead of beforeCommitHook.") deprecated38("Use addBeforeCommitHook instead of beforeCommitHook.")
# Default order is zero. self.addBeforeCommitHook(hook, args, kws)
self.addBeforeCommitHook(hook, args, kws, order=0)
def _callBeforeCommitHooks(self): def _callBeforeCommitHooks(self):
# Call all hooks registered, allowing further registrations # Call all hooks registered, allowing further registrations
# during processing. # during processing. Note that calls to addBeforeCommitHook() may
while self._before_commit: # add additional hooks while hooks are running, and iterating over a
order, index, hook, args, kws = self._before_commit.pop(0) # growing list is well-defined in Python.
for hook, args, kws in self._before_commit:
hook(*args, **kws) hook(*args, **kws)
self._before_commit_index = 0 self._before_commit = []
def _commitResources(self): def _commitResources(self):
# Execute the two-phase commit protocol. # Execute the two-phase commit protocol.
......
...@@ -195,7 +195,7 @@ class ITransaction(zope.interface.Interface): ...@@ -195,7 +195,7 @@ class ITransaction(zope.interface.Interface):
instead. instead.
""" """
def addBeforeCommitHook(hook, args=(), kws=None, order=0): def addBeforeCommitHook(hook, args=(), kws=None):
"""Register a hook to call before the transaction is committed. """Register a hook to call before the transaction is committed.
The specified hook function will be called after the transaction's The specified hook function will be called after the transaction's
...@@ -208,20 +208,10 @@ class ITransaction(zope.interface.Interface): ...@@ -208,20 +208,10 @@ class ITransaction(zope.interface.Interface):
arguments are passed). arguments are passed).
Multiple hooks can be registered and will be called in the order they Multiple hooks can be registered and will be called in the order they
were registered (first registered, first called), except that were registered (first registered, first called). This method can
hooks registered with different `order` arguments are invoked from also be called from a hook: an executing hook can register more
smallest `order` value to largest. `order` must be an integer, hooks. Applications should take care to avoid creating infinite loops
and defaults to 0. by recursively registering hooks.
For instance, a hook registered with order=1 will be invoked after
another hook registered with order=-1 and before another registered
with order=2, regardless of which was registered first. When two
hooks are registered with the same order, the first one registered is
called first.
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 Hooks are called only for a top-level commit. A subtransaction
commit or savepoint creation does not call any hooks. If the commit or savepoint creation does not call any hooks. If the
......
...@@ -414,28 +414,6 @@ def test_join(): ...@@ -414,28 +414,6 @@ def test_join():
def hook(): def hook():
pass pass
class BeforeCommitHookTests(unittest.TestCase):
def test_01_beforecommithook_order_exceptions(self):
# string
t = transaction.Transaction()
self.assertRaises(ValueError, t.addBeforeCommitHook,
hook, order='string')
def test_02_beforecommithook_order_exceptions(self):
# float
t = transaction.Transaction()
self.assertRaises(ValueError, t.addBeforeCommitHook,
hook, order=1.2)
def test_03_beforecommithook_order_exceptions(self):
# object
t = transaction.Transaction()
class foo:
pass
self.assertRaises(ValueError, t.addBeforeCommitHook,
hook, order=foo())
# deprecated38; remove this then # deprecated38; remove this then
def test_beforeCommitHook(): def test_beforeCommitHook():
"""Test beforeCommitHook. """Test beforeCommitHook.
...@@ -613,7 +591,7 @@ def test_beforeCommitHook(): ...@@ -613,7 +591,7 @@ def test_beforeCommitHook():
""" """
def test_addBeforeCommitHook(): def test_addBeforeCommitHook():
"""Test addBeforeCommitHook, without order arguments. """Test addBeforeCommitHook.
Let's define a hook to call, and a way to see that it was called. Let's define a hook to call, and a way to see that it was called.
...@@ -752,92 +730,11 @@ def test_addBeforeCommitHook(): ...@@ -752,92 +730,11 @@ def test_addBeforeCommitHook():
>>> reset_log() >>> reset_log()
""" """
def test_addBeforeCommitHookOrder():
"""Test addBeforeCommitHook with order arguments.
Register a hook with an order explicitly equal to 0 (the default value):
>>> import transaction
>>> t = transaction.begin()
>>> t.addBeforeCommitHook(hook, '1', order=0)
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 called first.
>>> t.addBeforeCommitHook(hook, '2', order=-999999)
>>> [(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 called last.
>>> t.addBeforeCommitHook(hook, '3', order=999999)
>>> 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 check that insertion with the same order values respects the order
of registration.
>>> t.addBeforeCommitHook(hook, '4') # order=0 implied
>>> for hook, args, kws in t.getBeforeCommitHooks():
... print (hook.func_name, args, kws)
('hook', ('2',), {})
('hook', ('1',), {})
('hook', ('4',), {})
('hook', ('3',), {})
>>> t.addBeforeCommitHook(hook, '5', order=999999)
>>> for hook, args, kws in t.getBeforeCommitHooks():
... print (hook.func_name, args, kws)
('hook', ('2',), {})
('hook', ('1',), {})
('hook', ('4',), {})
('hook', ('3',), {})
('hook', ('5',), {})
>>> t.addBeforeCommitHook(hook, '6', order=-999999)
>>> 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',), {})
>>> def hook2():
... pass
>>> t.addBeforeCommitHook(hook2, '8', order=0)
>>> for hook, args, kws in t.getBeforeCommitHooks():
... print (hook.func_name, args, kws)
('hook', ('2',), {})
('hook', ('6',), {})
('hook', ('1',), {})
('hook', ('4',), {})
('hook2', ('8',), {})
('hook', ('3',), {})
('hook', ('5',), {})
"""
def test_suite(): def test_suite():
from zope.testing.doctest import DocTestSuite from zope.testing.doctest import DocTestSuite
return unittest.TestSuite(( return unittest.TestSuite((
DocTestSuite(), DocTestSuite(),
unittest.makeSuite(TransactionTests), unittest.makeSuite(TransactionTests),
unittest.makeSuite(BeforeCommitHookTests),
)) ))
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment