Commit 127049ad authored by Martin Aspeli's avatar Martin Aspeli

Added IPubBeforeAbort event to mirror IPubBeforeCommit in failure scenarios.

This event is fired just before IPubFailure, but, crucially, while the transaction is still open.
parent 35d407e2
......@@ -11,6 +11,10 @@ Zope 2.12.2 (unreleased)
Features Added
++++++++++++++
- Added IPubBeforeAbort event to mirror IPubBeforeCommit in failure scenarios.
This event is fired just before IPubFailure, but, crucially, while the
transaction is still open.
- Include bytes limited cache size in the cache parameters ZMI screen.
- Officially supporting Python 2.6 only (with inofficial support for
......
......@@ -27,7 +27,7 @@ from zope.security.management import newInteraction, endInteraction
from zope.event import notify
from pubevents import PubStart, PubSuccess, PubFailure, \
PubBeforeCommit, PubAfterTraversal
PubBeforeCommit, PubAfterTraversal, PubBeforeAbort
class Retry(Exception):
"""Raise this to retry a request
......@@ -173,8 +173,12 @@ def publish(request, module_name, after_list, debug=0,
)
retry = True
finally:
# Note: 'abort's can fail. Nevertheless, we want end request handling
try:
notify(PubBeforeAbort(request, exc_info, retry))
if transactions_manager:
transactions_manager.abort()
finally:
......@@ -196,6 +200,9 @@ def publish(request, module_name, after_list, debug=0,
else:
# Note: 'abort's can fail. Nevertheless, we want end request handling
try:
notify(PubBeforeAbort(request, exc_info, False))
if transactions_manager:
transactions_manager.abort()
finally:
......
......@@ -41,5 +41,12 @@ class IPubAfterTraversal(IPubEvent):
class IPubBeforeCommit(IPubEvent):
"""notified immediately before the transaction commit (i.e. after the main
request processing is finished.
request processing is finished).
"""
class IPubBeforeAbort(IPubEvent):
"""notified immediately before the transaction abort (i.e. after the main
request processing is finished, and there was an error).
"""
exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''')
retry = Attribute('Whether the request will be retried')
......@@ -10,7 +10,7 @@ for detailed time related analysis, inline request monitoring.
from zope.interface import implements
from interfaces import IPubStart, IPubSuccess, IPubFailure, \
IPubAfterTraversal, IPubBeforeCommit
IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort
class _Base(object):
"""PubEvent base class."""
......@@ -42,3 +42,10 @@ class PubAfterTraversal(_Base):
class PubBeforeCommit(_Base):
"""notified immediately before the commit."""
implements(IPubBeforeCommit)
class PubBeforeAbort(_Base):
"""notified immediately before an abort."""
implements(IPubBeforeAbort)
def __init__(self, request, exc_info, retry):
self.request, self.exc_info, self.retry = request, exc_info, retry
......@@ -8,7 +8,7 @@ from zope.event import subscribers
from ZPublisher.Publish import publish, Retry
from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \
PubAfterTraversal, PubBeforeCommit
PubAfterTraversal, PubBeforeCommit, PubBeforeAbort
from ZPublisher.interfaces import \
IPubStart, IPubEnd, IPubSuccess, IPubFailure, \
IPubAfterTraversal, IPubBeforeCommit
......@@ -74,40 +74,58 @@ class TestPubEvents(TestCase):
r = self.request; r.action = 'fail_return'
publish(r, PUBMODULE, [None])
events = self.reporter.events
self.assertEqual(len(events), 2)
self.assertEqual(len(events), 3)
self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubFailure))
self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, False)
self.assertEqual(len(events[1].exc_info), 3)
self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, False)
self.assertEqual(len(events[2].exc_info), 3)
def testFailureException(self):
r = self.request; r.action = 'fail_exception'
self.assertRaises(Exception, publish, r, PUBMODULE, [None])
events = self.reporter.events
self.assertEqual(len(events), 2)
self.assertEqual(len(events), 3)
self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubFailure))
self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, False)
self.assertEqual(len(events[1].exc_info), 3)
self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, False)
self.assertEqual(len(events[2].exc_info), 3)
def testFailureConflict(self):
r = self.request; r.action = 'conflict'
publish(r, PUBMODULE, [None])
events = self.reporter.events
self.assertEqual(len(events), 6)
self.assertEqual(len(events), 7)
self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubFailure))
self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, True)
self.assertEqual(len(events[1].exc_info), 3)
self.assert_(isinstance(events[1].exc_info[1], ConflictError))
self.assert_(isinstance(events[2], PubStart))
self.assert_(isinstance(events[5], PubSuccess))
self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, True)
self.assertEqual(len(events[2].exc_info), 3)
self.assert_(isinstance(events[2].exc_info[1], ConflictError))
self.assert_(isinstance(events[3], PubStart))
self.assert_(isinstance(events[4], PubAfterTraversal))
self.assert_(isinstance(events[5], PubBeforeCommit))
self.assert_(isinstance(events[6], PubSuccess))
# Auxiliaries
def _succeed():
......
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