diff --git a/flask.py b/flask.py index 71c30d84..00d23287 100644 --- a/flask.py +++ b/flask.py @@ -1362,6 +1362,12 @@ class Flask(_PackageBoundObject): Then you still have the original application object around and can continue to call methods on it. + .. versionchanged:: 0.4 + The :meth:`after_request` functions are now called even if an + error handler took over request processing. This ensures that + even if an exception happens database have the chance to + properly close the connection. + :param environ: a WSGI environment :param start_response: a callable accepting a status code, a list of headers and an optional @@ -1376,6 +1382,13 @@ class Flask(_PackageBoundObject): response = self.process_response(response) except Exception, e: response = self.make_response(self.handle_exception(e)) + try: + response = self.process_response(response) + except Exception, e: + self.logger.exception('after_request handler failed ' + 'to postprocess error response. ' + 'Depending on uncertain state?') + return response(environ, start_response) def request_context(self, environ): diff --git a/tests/flask_tests.py b/tests/flask_tests.py index 9d1dba6e..3f9588bc 100644 --- a/tests/flask_tests.py +++ b/tests/flask_tests.py @@ -16,6 +16,7 @@ import sys import flask import unittest import tempfile +from logging import StreamHandler from contextlib import contextmanager from datetime import datetime from werkzeug import parse_date, parse_options_header @@ -240,6 +241,37 @@ class BasicFunctionalityTestCase(unittest.TestCase): assert 'after' in evts assert rv == 'request|after' + def test_after_request_errors(self): + app = flask.Flask(__name__) + called = [] + @app.after_request + def after_request(response): + called.append(True) + return response + @app.route('/') + def fails(): + 1/0 + rv = app.test_client().get('/') + assert rv.status_code == 500 + assert 'Internal Server Error' in rv.data + assert len(called) == 1 + + def test_after_request_handler_error(self): + error_out = StringIO() + app = flask.Flask(__name__) + app.logger.addHandler(StreamHandler(error_out)) + @app.after_request + def after_request(response): + 1/0 + return response + @app.route('/') + def fails(): + 1/0 + rv = app.test_client().get('/') + assert rv.status_code == 500 + assert 'Internal Server Error' in rv.data + assert 'after_request handler failed' in error_out.getvalue() + def test_error_handling(self): app = flask.Flask(__name__) @app.errorhandler(404)