Reputation: 8070
I have the following code:
import datetime
from flask.app import Flask
app = Flask(__name__)
app.config.from_object(__name__)
app.debug = True
def track_time_spent(name):
def decorator(f):
def wrapped(*args, **kwargs):
start = datetime.datetime.now()
ret = f(*args, **kwargs)
delta = datetime.datetime.now() - start
print name, "took", delta.total_seconds(), "seconds"
return ret
return wrapped
return decorator
@app.route('/foo')
@track_time_spent('foo')
def foo():
print "foo"
return "foo"
@app.route('/bar')
@track_time_spent('bar')
def bar():
print "bar"
return "bar"
I am unable to get foo to return 'foo':
$ curl localhost:8888/foo
bar
(flask window)
bar
bar took 8.2e-05 seconds
127.0.0.1 - - [18/Apr/2013 19:21:31] "GET /foo HTTP/1.1" 200 -
$ curl localhost:8888/bar
bar
(flask window)
bar
bar took 3.5e-05 seconds
127.0.0.1 - - [18/Apr/2013 19:21:35] "GET /bar HTTP/1.1" 200 -
What's going on? Why isn't my decorator working?
EDIT
I don't think you guys seem to be able to see the problem.
When I have @app.route
before @track_time_spent
, both methods return bar. The error here is that calling localhost:8888/foo results in bar
in both the http response as well as the print function.
Upvotes: 8
Views: 11495
Reputation: 4996
The other answers seem to be missing that you're getting "bar" as a response from "/foo" when you switch the order of the decorators. You must use @wraps
here unless you update the __name__
, __module__
, and such manually. Flask uses your methods kinda like singletons, and sees your decorator just as the wrapped()
method, instead of the methods you actually wrapped. Hence, your routes will keep getting overwritten by the last method to use your decorator.
import datetime
from functools import wraps
from flask.app import Flask
app = Flask(__name__)
app.config.from_object(__name__)
app.debug = True
def track_time_spent(name):
def decorator(f):
@wraps(f)
def wrapped(*args, **kwargs):
start = datetime.datetime.now()
ret = f(*args, **kwargs)
delta = datetime.datetime.now() - start
print name, "took", delta.total_seconds(), "seconds"
return ret
return wrapped
return decorator
@app.route('/foo')
@track_time_spent('foo')
def foo():
print "foo"
return "foo"
@app.route('/bar')
@track_time_spent('bar')
def bar():
print "bar"
return "bar"
app.run(host='0.0.0.0', port=8888)
Upvotes: 21
Reputation: 142
Flask's route
function is a registering function in the sense you call it for its side effect - it'll register your view function for an endpoint. However, you're registering just the view function, not the decorated view function. Simply switch the order of decorators to register the "time-tracked" function.
Also, you can use @app.before_request and @app.teardown_request-registered functions to track time more reliably (taking into account the time it took to render the template and such).
Upvotes: 3
Reputation: 55303
Why are you saying your decorator isn't working?
In your second example, the decorator is running, and it's realistic that such a simple function would execute in 0.035 ms (3.5e-05 is a notation that means 3.5 times 10 to the power of -5).
For reference, the reason why you needed to reverse the order of the two decorator is because app.route
registers the function that it is being passed.
Hence you need to pass it the decorated function, hence the order.
Upvotes: 0