Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom decorator in flask not working?

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.

like image 845
disappearedng Avatar asked Apr 19 '13 02:04

disappearedng


2 Answers

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)
like image 178
TkTech Avatar answered Oct 19 '22 03:10

TkTech


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).

like image 24
tehasdf Avatar answered Oct 19 '22 04:10

tehasdf