Search code examples
pythonflaskmarshallingdecoratorflask-restful

Wrapping a decorator, with arguments


I'm trying to replace the marshal_with decorator from flask-restful with a decorator that does something before calling marshal_with. My approach is to try to implement a new decorator that wraps marshal_with.

My code looks like:

from flask.ext.restful import marshal_with as restful_marshal_with

def marshal_with(fields, envelope=None):
    def wrapper(f):
        print("Do something with fields and envelope")

        @wraps(f)
        def inner(*args, **kwargs):
            restful_marshal_with(f(*args, **kwargs))
        return inner
    return wrapper

Unfortunately this seems to break things... no error messages but my API returns a null response when it shouldn't be. Any insights on what I'm doing wrong?


Solution

  • I was doing a couple things wrong here, first, failing to return the output of restful_marshal_with as jonrsharpe pointed out, secondly, failing to understand a decorator written as a class instead of a function, and how to properly pass values to it. The correct code ended up being:

    def marshal_with(fields, envelope=None):
        def wrapper(f):
            print("Do something with fields and envelope")
    
            @wraps(f)
            def inner(*args, **kwargs):
                rmw = restful_marshal_with(fields, envelope)
                return rmw(f)(*args, **kwargs)
            return inner
        return wrapper
    

    As you can see, in addition to not returning rmw(), I needed to properly initialize the request_marshal_with class before calling it. Finally, it is important to remember that decorators return functions, therefore the arguments of the original function should be passed to the return value of rmw(f), hence the statement return rmw(f)(*args, **kwargs). This is perhaps more apparent if you take a look at the flask_restful.marshal_with code here.