Here's a variation on @Mark Hildreth's answer that does wrap and return 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 why it does not work quite as simply as I expected and hoped has to something do with the Flask context stack and the order in which callbacks are applied. Here is a timeline of method calls (based on some debug statements since removed):
$ 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 within the wrapped
function. But because that is not called until after the analytics_view
@app.before_request
hook, I had to follow Mark's example and set the _skip_analytics
attr on the endpoint method loaded in what I'm calling the application (as opposed to request) context which gets invoked only at launch.
For more on flask.g
and app context, see this StackOverflow answer.