From 3a570f919b4c0ff8bd349d7761e2ed021effe534 Mon Sep 17 00:00:00 2001 From: Joe D'Andrea Date: Tue, 31 May 2016 16:22:55 +0000 Subject: [PATCH] Pecan "after" hooks are called during internal redirects. PecanBase:__call__() now calls "after" hooks only if an internal redirect is not in progress. --- pecan/core.py | 19 ++++++++++++++----- pecan/tests/test_hooks.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/pecan/core.py b/pecan/core.py index 9514638..ff2f2c4 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -668,6 +668,9 @@ class PecanBase(object): } controller = None + # track internal redirects + internal_redirect = False + # handle the request try: # add context and environment to the request @@ -697,9 +700,12 @@ class PecanBase(object): state.response.content_type = best_match environ['pecan.original_exception'] = e + # note if this is an internal redirect + internal_redirect = isinstance(e, ForwardRequestException) + # if this is not an internal redirect, run error hooks on_error_result = None - if not isinstance(e, ForwardRequestException): + if not internal_redirect: on_error_result = self.handle_hooks( self.determine_hooks(state.controller), 'on_error', @@ -720,10 +726,13 @@ class PecanBase(object): if allowed_methods: state.response.allow = sorted(allowed_methods) finally: - # handle "after" hooks - self.handle_hooks( - self.determine_hooks(state.controller), 'after', state - ) + # if this is not an internal redirect, run "after" hooks + if not internal_redirect: + self.handle_hooks( + self.determine_hooks(state.controller), + 'after', + state + ) self._handle_empty_response_body(state) diff --git a/pecan/tests/test_hooks.py b/pecan/tests/test_hooks.py index a15368e..902074b 100644 --- a/pecan/tests/test_hooks.py +++ b/pecan/tests/test_hooks.py @@ -416,6 +416,28 @@ class TestHooks(PecanTestCase): # for each different instance of the Hook in the two Controllers assert run_hook[3] == 'last - before hook', run_hook[3] + def test_internal_redirect_with_after_hook(self): + run_hook = [] + + class RootController(object): + @expose() + def internal(self): + redirect('/testing', internal=True) + + @expose() + def testing(self): + return 'it worked!' + + class SimpleHook(PecanHook): + def after(self, state): + run_hook.append('after') + + app = TestApp(make_app(RootController(), hooks=[SimpleHook()])) + response = app.get('/internal') + assert response.body == b_('it worked!') + + assert len(run_hook) == 1 + class TestStateAccess(PecanTestCase):