Flange strikes decorator before fire alarm

I use Flask and using the before_request decorator to send request information to the analytic system. I'm trying to create a decorator that will prevent these events from being sent to multiple routes.

The problem I am facing is that my decorator will be called before the before_request signal fires.

def exclude_from_analytics(func): @wraps(func) def wrapped(*args, **kwargs): print "Before decorated function" return func(*args, exclude_from_analytics=True, **kwargs) return wrapped # ------------------------ @exclude_from_analytics @app.route('/') def index(): return make_response('..') # ------------------------ @app.before_request def analytics_view(*args, **kwargs): if 'exclude_from_analytics' in kwargs and kwargs['exclude_from_analytics'] is True: return 
+7
python flask decorator
source share
2 answers

You can use a decorator to simply put an attribute in a function (in my example below I use _exclude_from_analytics as an attribute). I find the view function using a combination of request.endpoint and app.view_functions .

If the attribute is not found at the endpoint, you can ignore analytics.

 from flask import Flask, request app = Flask(__name__) def exclude_from_analytics(func): func._exclude_from_analytics = True return func @app.route('/a') @exclude_from_analytics def a(): return 'a' @app.route('/b') def b(): return 'b' @app.before_request def analytics_view(*args, **kwargs): # Default this to whatever you'd like. run_analytics = True # You can handle 404s differently here if you'd like. if request.endpoint in app.view_functions: view_func = app.view_functions[request.endpoint] run_analytics = not hasattr(view_func, '_exclude_from_analytics') print 'Should run analytics on {0}: {1}'.format(request.path, run_analytics) app.run(debug=True) 

Exit (ignoring static files ...)

 Should run analytics on /a: False 127.0.0.1 - - [24/Oct/2013 15:55:15] "GET /a HTTP/1.1" 200 - Should run analytics on /b: True 127.0.0.1 - - [24/Oct/2013 15:55:18] "GET /b HTTP/1.1" 200 - 

I did not check if this works with drawings. In addition, a decorator that wraps and returns a NEW function may cause this to not work, since the attribute may be hidden.

+10
source share

Here's a @Mark Hildreth answer option that completes and returns a function:

 from functools import wraps from flask import Flask, request, g app = Flask(__name__) def exclude_from_analytics(*args, **kw): def wrapper(endpoint_method): endpoint_method._skip_analytics = True @wraps(endpoint_method) def wrapped(*endpoint_args, **endpoint_kw): # This is what I want I want to do. Will not work. #g.skip_analytics = getattr(endpoint_method, '_skip_analytics', False) return endpoint_method(*endpoint_args, **endpoint_kw) return wrapped return wrapper @app.route('/') def no_skip(): return 'Skip analytics? %s' % (g.skip_analytics) @app.route('/skip') @exclude_from_analytics() def skip(): return 'Skip analytics? %s' % (g.skip_analytics) @app.before_request def analytics_view(*args, **kwargs): if request.endpoint in app.view_functions: view_func = app.view_functions[request.endpoint] g.skip_analytics = hasattr(view_func, '_skip_analytics') print 'Should skip analytics on {0}: {1}'.format(request.path, g.skip_analytics) app.run(debug=True) 

The reason this works is not as simple as I expected, and was hoping to do something with the Flask context stack and the order in which callbacks apply. The following is a graph of method calls (based on some debug instructions from the moment of deletion):

 $ python test-flask-app.py # Application Launched DECORATOR exclude_from_analytics DECORATOR wrapper * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) # REQUEST: / DECORATOR app.before_request: analytics_view > Should skip analytics on /: False ENDPOINT no_skip 127.0.0.1 - - [14/May/2016 16:10:39] "GET / HTTP/1.1" 200 - # REQUEST: /skip DECORATOR app.before_request: analytics_view > Should skip analytics on /skip: True DECORATOR wrapped ENDPOINT skip 127.0.0.1 - - [14/May/2016 16:12:46] "GET /skip HTTP/1.1" 200 - 

I would prefer to set g.skip_analytics from the wrapped function. But since this is not called until there is an analytics_view @app.before_request , I had to follow Mark's example and set _skip_analytics attr in the endpoint method loaded in what I call the application (as opposed to the request ), which is called only at startup.

For more information about flask.g and the application context, see https://stackoverflow.com/a/312618/

+1
source share

All Articles