0

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?

learningKnight
  • 321
  • 1
  • 3
  • 14
  • You don't `return restful_marshal_with(...)`. – jonrsharpe May 16 '17 at 21:51
  • `restful_marshal_with(f(*args, **kwargs))`. This calls the function f and passes the results to the wrapper. That is different from wrapping it. Try reading : http://stackoverflow.com/questions/5952641/decorating-decorators-try-to-get-my-head-around-understanding-it – RobertB May 16 '17 at 21:59
  • @jonrsharpe can you please elaborate? I was following the pattern for decorators with arugments outlined here: http://www.artima.com/weblogs/viewpost.jsp?thread=240845 but I confess my understanding is incomplete. How can I preserve the `*args` and `**kwargs**` in the approach you're suggesting? – learningKnight May 16 '17 at 21:59
  • Why would adding the `return` *not* preserve them? That's why you're getting a null response, `inner` calls the original function and then calls `restful_marshal_with` with the result, but doesn't return anything. – jonrsharpe May 16 '17 at 22:01
  • @jonrsharpe, thank you, pulling that thread led me to the correct answer. Cheers. – learningKnight May 16 '17 at 23:23

2 Answers2

1

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.

tboz203
  • 379
  • 3
  • 13
  • Thank, you yes, I am familiar with this pattern... the goal is to use a single decorator to accomplish what two could do, since they take the same argument and repeating it is redundant. – learningKnight May 16 '17 at 23:17
0

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.

Community
  • 1
  • 1
learningKnight
  • 321
  • 1
  • 3
  • 14