更改Flask和自定义装饰器的顺序会破坏自定义装饰器

时间:2020-02-03 16:09:35

标签: python flask python-decorators

我为所有HTTP调用做了一个日志装饰器:

def log_http_call(flask_request_getter: typing.Callable[[], flask.Request]):
    def arg_wrapper(wrapped_function):
        @wraps(wrapped_function)
        def wrapper(*args, **kwargs):
            flask_request: flask.Request = flask_request_getter()
            print(f'HTTP Method {flask_request.method}: {flask_request.path}')
            return wrapped_function(*args, **kwargs)    
        return wrapper    
    return arg_wrapper


@app.route('/api/all', methods=['GET'])
@log_http_call(lambda: flask.request)
def get_all():
    return "Here you go"


@app.route('/api/<int:_id>/<string:name>', methods=['GET'])
@log_http_call(lambda: flask.request)
def get_one(_id, name):
    return f"{name}'s ID is {_id}"

以这种方式工作,但是如果我将装饰器的顺序颠倒为例如:

@log_http_call(lambda: flask.request)
@app.route('/api/all', methods=['GET'])
def get_all():
    return "Here you go"

它不再工作。

由于我代码的其他使用者可能以不同的顺序放置这些装饰器,因此我想确保它们以任何一种方式工作。

如何使它以任何顺序工作?

3 个答案:

答案 0 :(得分:2)

烧瓶docs特别声明route装饰器必须位于最外面。除了自己修改Flask库,没有其他方法可以解决这个问题(鉴于开发人员可能很容易做到这一点,这似乎是一项艰巨的任务)。

就像Flask在他们的文档中添加注释一样,您可以在文档中添加类似的注释(或仅引用Flask文档)。

答案 1 :(得分:0)

您不能:装饰器包装一个函数,让多个装饰器包装一个函数中的一个函数。具体来说:than what you'd expect

答案 2 :(得分:0)

执行此操作时:

@app.route('/api/all', methods=['GET'])
@log_http_call(lambda: flask.request)
def get_all():
    return "Here you go"

您实际上正在做的是获取内部函数,并将其作为参数传递给包装装饰函数。

本质上,您要求一个f(x)和g(x)使得f(g(x))== g(f(x))。这被称为交换函数合成,并且唯一的情况是f(g(x))== g(f(x))是f(x)是g(x)的倒数。换句话说,装饰器的顺序几乎会100%改变结果。

希望这会有所帮助!