learningKnight
learningKnight

Reputation: 329

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?

Upvotes: 0

Views: 1623

Answers (2)

learningKnight
learningKnight

Reputation: 329

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.

Upvotes: 0

tboz203
tboz203

Reputation: 474

I don't know the specifics of marshal_with, but it's entirely possible to use multiple decorators on a single function. For instance:

def decorator_one(func):
    def inner(*args, **kwargs):
        print("I'm decorator one")
        func(*args, **kwargs)
    return inner

def decorator_two(text):
    def wrapper(func):
        def inner(*args, **kwargs):
            print(text)
            func(*args, **kwargs)
        return inner
    return wrapper

@decorator_one
@decorator_two("I'm decorator two")
def some_function(a, b):
    print(a, b, a+b)


some_function(4, 7)

The output this gives is:

I'm decorator one
I'm decorator two
4 7 11

You can modify this little script by adding print statements after each inner function call to see the exact flow control between each decorator as well.

Upvotes: 1

Related Questions