Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is only one Flask teardown_request function being called when view raises Exception?

This simple application, has two teardown_request handlers, and I'd expect both of them to be called for every request, no matter what happens in the view implementation, as per the documentation

import flask
import werkzeug.exceptions

app = flask.Flask(__name__)

@app.teardown_request
def teardown1(response):
    print "Teardown 1"
    return response

@app.teardown_request
def teardown2(response):
    print "Teardown 2"
    return response

@app.route("/")
def index():
    return "chunky bacon"

@app.route("/httpexception")
def httpexception():
    raise werkzeug.exceptions.BadRequest("no bacon?")

@app.route("/exception")
def exception():
    raise Exception("bacoff")

if __name__ == "__main__":
    app.run(port=5000)

However, when I run it and make requests to the three views in turn, I get the following output:

Teardown 2
Teardown 1
127.0.0.1 - - [15/Nov/2011 18:53:16] "GET / HTTP/1.1" 200 -

Teardown 2
Teardown 1
127.0.0.1 - - [15/Nov/2011 18:53:27] "GET /httpexception HTTP/1.1" 400 -

Teardown 2
127.0.0.1 - - [15/Nov/2011 18:53:33] "GET /exception HTTP/1.1" 500 -

Only one of the teardown_request functions is being called when an exception that is not derived from werkzeug.exceptions.HTTPException is raised by the last view. Any ideas why, or is this a bug in flask?

like image 506
Day Avatar asked Nov 15 '11 19:11

Day


1 Answers

Just discovered the answer myself.

The teardown_request functions are not supposed to take a response and return a response, the way after_request does. They apparently take an argument which is usually None unless an Exception which does not derive from HttpException is raised by the view, in which case they are passed that.

Apparently they also must not return said exception or you'll get the broken behaviour that I demonstrated.

To fix, the app's teardown_request functions should look like this:

@app.teardown_request
def teardown1(exc):
    print "Teardown 1 {0!r}".format(exc)

@app.teardown_request
def teardown2(exc):
    print "Teardown 2 {0!r}".format(exc)

Which then gives the expected output for all three views:

Teardown 2 None
Teardown 1 None
127.0.0.1 - - [15/Nov/2011 19:20:03] "GET / HTTP/1.1" 200 -

Teardown 2 None
Teardown 1 None
127.0.0.1 - - [15/Nov/2011 19:20:10] "GET /httpexception HTTP/1.1" 400 -

Teardown 2 Exception('bacoff',)
Teardown 1 Exception('bacoff',)
127.0.0.1 - - [15/Nov/2011 19:20:18] "GET /exception HTTP/1.1" 500 -

(with the addition of some extra debug to print what gets passed to the teardown_request handlers)

like image 108
Day Avatar answered Dec 04 '22 21:12

Day