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