diff options
author | Ryan Petrello <lists@ryanpetrello.com> | 2013-04-17 17:28:39 -0400 |
---|---|---|
committer | Ryan Petrello <lists@ryanpetrello.com> | 2013-04-17 17:28:39 -0400 |
commit | 2813782d1699e84096018ce01177af1c24cf2890 (patch) | |
tree | f51096ee21f63d38bff289dfe8ded52203a992ed | |
parent | b5d69b5624f3e359c3c0c898e2b2dd23f85af01b (diff) | |
download | pecan-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.py | 5 | ||||
-rw-r--r-- | pecan/tests/test_hooks.py | 43 |
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): |