Browse files

Added after_this_request decorator.

  • Loading branch information...
1 parent dbfd406 commit 086348e2f2874fb701048d5e1390cfe674de1f70 @mitsuhiko mitsuhiko committed May 8, 2012
Showing with 51 additions and 2 deletions.
  1. +1 −0 CHANGES
  2. +2 −0 docs/api.rst
  3. +2 −1 flask/__init__.py
  4. +1 −1 flask/app.py
  5. +30 −0 flask/ctx.py
  6. +15 −0 flask/testsuite/basic.py
View
1 CHANGES
@@ -70,6 +70,7 @@ Relase date to be decided, codename to be chosen.
be used on creation of the :data:`~flask.g` instance of each request.
- Added `required_methods` attribute to view functions to force-add methods
on registration.
+- Added :func:`flask.after_this_request`.
Version 0.8.1
-------------
View
2 docs/api.rst
@@ -289,6 +289,8 @@ Useful Functions and Classes
.. autofunction:: make_response
+.. autofunction:: after_this_request
+
.. autofunction:: send_file
.. autofunction:: send_from_directory
View
3 flask/__init__.py
@@ -25,7 +25,8 @@
get_template_attribute, make_response, safe_join
from .globals import current_app, g, request, session, _request_ctx_stack, \
_app_ctx_stack
-from .ctx import has_request_context, has_app_context
+from .ctx import has_request_context, has_app_context, \
+ after_this_request
from .module import Module
from .blueprints import Blueprint
from .templating import render_template, render_template_string
View
2 flask/app.py
@@ -1555,7 +1555,7 @@ def process_response(self, response):
"""
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
- funcs = ()
+ funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = reversed(self.after_request_funcs[bp])
if None in self.after_request_funcs:
View
30 flask/ctx.py
@@ -11,6 +11,7 @@
import sys
+from functools import partial
from werkzeug.exceptions import HTTPException
from .globals import _request_ctx_stack, _app_ctx_stack
@@ -30,6 +31,31 @@ def _push_app_if_necessary(app):
return ctx
+def after_this_request(f):
+ """Executes a function after this request. This is useful to modify
+ response objects. The function is passed the response object and has
+ to return the same or a new one.
+
+ Example::
+
+ @app.route('/')
+ def index():
+ @after_this_request
+ def add_header():
+ response.headers['X-Foo'] = 'Parachute'
+ return response
+ return 'Hello World!'
+
+ This is more useful if a function other than the view function wants to
+ modify a response. For instance think of a decorator that wants to add
+ some headers without converting the return value into a response object.
+
+ .. versionadded:: 0.9
+ """
+ _request_ctx_stack.top._after_request_functions.append(f)
+ return f
+
+
def has_request_context():
"""If you have code that wants to test if a request context is there or
not this function can be used. For instance, you may want to take advantage
@@ -153,6 +179,10 @@ def __init__(self, app, environ):
# context, it will be stored there
self._pushed_application_context = None
+ # Functions that should be executed after the request on the response
+ # object. These will even be called in case of an error.
+ self._after_request_functions = []
+
self.match_request()
# XXX: Support for deprecated functionality. This is going away with
View
15 flask/testsuite/basic.py
@@ -411,6 +411,21 @@ def index():
self.assert_('after' in evts)
self.assert_equal(rv, 'request|after')
+ def test_after_request_processing(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/')
+ def index():
+ @flask.after_this_request
+ def foo(response):
+ response.headers['X-Foo'] = 'a header'
+ return response
+ return 'Test'
+ c = app.test_client()
+ resp = c.get('/')
+ self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.headers['X-Foo'], 'a header')
+
def test_teardown_request_handler(self):
called = []
app = flask.Flask(__name__)

0 comments on commit 086348e

Please sign in to comment.