0

I made a logging decorator for all HTTP calls:

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}"

It's working this way, but if I reverse the order of the decorators to e.g.:

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

It's no longer working.

Since other consumers of my code may place these decorators in different order, I want to make sure that they work either way.

How can I make it work in any order?

davidism
  • 121,510
  • 29
  • 395
  • 339
Tar
  • 8,529
  • 9
  • 56
  • 127

3 Answers3

2

The Flask docs specifically state the route decorator must be the outermost. There is no way around this except modifying the Flask library yourself (which seems like a daunting task given that the developers probably would have supported this already if it was easy).

Much like Flask put that comment in their documentation, you can put a similar comment in your documentation (or just reference the Flask docs).

noslenkwah
  • 1,702
  • 1
  • 17
  • 26
0

You cannot: a decorator wraps a function, having multiple decorators wraps a function in a function. To be more specific: Decorator execution order

sampers
  • 463
  • 4
  • 11
0

When you do the this:

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

What you're actually doing is taking the inner function, and passing it as an argument to the wrapping decorator function.

Essentially, your asking for an f(x) and g(x) such that f(g(x)) == g(f(x)). This is called a commutative function composition, and the only instance in which f(g(x)) == g(f(x)) is when f(x) is the inverse of g(x). In other words, the order of your decorators is almost 100% likely to change the outcome.

Hope this helps!

Greg
  • 1,845
  • 2
  • 16
  • 26