summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2013-04-17 17:28:39 -0400
committerRyan Petrello <lists@ryanpetrello.com>2013-04-17 17:28:39 -0400
commit2813782d1699e84096018ce01177af1c24cf2890 (patch)
treef51096ee21f63d38bff289dfe8ded52203a992ed
parentb5d69b5624f3e359c3c0c898e2b2dd23f85af01b (diff)
downloadpecan-2813782d1699e84096018ce01177af1c24cf2890.tar.gz
Resolve a bug in TransactionHook.
This commit makes TransactionHook more robust so that it isn't as susceptible to failure when exceptions occur in *other* PecanHooks within a request.
-rw-r--r--pecan/hooks.py5
-rw-r--r--pecan/tests/test_hooks.py43
2 files changed, 46 insertions, 2 deletions
diff --git a/pecan/hooks.py b/pecan/hooks.py
index 97dc416..f7422b8 100644
--- a/pecan/hooks.py
+++ b/pecan/hooks.py
@@ -144,7 +144,8 @@ class TransactionHook(PecanHook):
self.start_ro()
def before(self, state):
- if self.is_transactional(state) and not state.request.transactional:
+ if self.is_transactional(state) \
+ and not getattr(state.request, 'transactional', False):
self.clear()
state.request.transactional = True
self.start()
@@ -170,7 +171,7 @@ class TransactionHook(PecanHook):
state.request.error = True
def after(self, state):
- if state.request.transactional:
+ if getattr(state.request, 'transactional', False):
action_name = None
if state.request.error:
action_name = 'after_rollback'
diff --git a/pecan/tests/test_hooks.py b/pecan/tests/test_hooks.py
index 5ad4d46..8f6c55a 100644
--- a/pecan/tests/test_hooks.py
+++ b/pecan/tests/test_hooks.py
@@ -992,6 +992,49 @@ class TestTransactionHook(PecanTestCase):
assert run_hook[2] == 'commit'
assert run_hook[3] == 'clear'
+ def test_transaction_hook_with_broken_hook(self):
+ """
+ In a scenario where a preceding hook throws an exception,
+ ensure that TransactionHook still rolls back properly.
+ """
+ run_hook = []
+
+ class RootController(object):
+ @expose()
+ def index(self):
+ return 'Hello, World!'
+
+ def gen(event):
+ return lambda: run_hook.append(event)
+
+ class MyCustomException(Exception):
+ pass
+
+ class MyHook(PecanHook):
+
+ def on_route(self, state):
+ raise MyCustomException('BROKEN!')
+
+ app = TestApp(make_app(RootController(), hooks=[
+ MyHook(),
+ TransactionHook(
+ start=gen('start'),
+ start_ro=gen('start_ro'),
+ commit=gen('commit'),
+ rollback=gen('rollback'),
+ clear=gen('clear')
+ )
+ ]))
+
+ self.assertRaises(
+ MyCustomException,
+ app.get,
+ '/'
+ )
+
+ assert len(run_hook) == 1
+ assert run_hook[0] == 'clear'
+
class TestRequestViewerHook(PecanTestCase):