El matraz golpeó al decorador antes de que se produzca una señal de petición.

Estoy usando Flask y el decorador before_request para enviar información sobre las solicitudes a un sistema de análisis. Ahora estoy tratando de crear un decorador que impida enviar estos eventos en algunas rutas.

El problema con el que me estoy topando es hacer que llamen a mi decorador antes de que se dispare la señal before_request.

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 

Puede usar el decorador para simplemente poner un atributo en la función (en mi ejemplo a continuación, estoy usando _exclude_from_analytics como el atributo). Encuentro la función de vista usando una combinación de request.endpoint y app.view_functions .

Si el atributo no se encuentra en el punto final, puede ignorar los análisis.

 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) 

La salida (ignorando archivos estáticos …)

 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 - 

No he probado para ver si esto funciona con planos. Además, un decorador que ajusta y devuelve una función NUEVA puede hacer que esto no funcione ya que el atributo puede estar oculto.

Aquí hay una variación en la respuesta de @Mark Hildreth que envuelve y devuelve una función:

 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) 

La razón por la que no funciona es tan simple como lo esperaba y esperaba que tuviera que ver con la stack de contexto de Flask y el orden en que se aplican las devoluciones de llamada. Aquí hay una línea de tiempo de llamadas a métodos (en base a algunas declaraciones de depuración desde que se eliminaron):

 $ 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 - 

Preferiría establecer g.skip_analytics desde dentro de la función wrapped . Pero debido a que no se llama hasta después del @app.before_request analytics_view @app.before_request , tuve que seguir el ejemplo de Mark y configurar _skip_analytics attr en el método de punto final cargado en lo que llamo el contexto de la aplicación (en oposición a la solicitud ) que se invoca sólo en el lanzamiento.

Para obtener más información sobre flask.g y el contexto de la aplicación, consulte esta respuesta de StackOverflow .