extending the functionality of the functools.wraps decorator

Go To StackoverFlow.com

2

I'd like to create a new decorator to use in place of @wraps(f) that does whatever magic @wraps(f) would do as well as something else. How would I do that?

Specifically, I've got several decorators that are of the form:

def decorator(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        # does some stuff in here
        return f(*args, **kwargs)
    if not hasattr(wrapper, '_first_line'):
        wrapper._first_line = inspect.getsourcelines(f)[1]
    return wrapper

It seems like I should be able to create a decorator like @wraps_with_first_line(f) which will do all that @wraps(f) is doing as well as if not hasattr(wrapper, '_first_line'): wrapper._first_line = inspect.getsourcelines(f)[1].

2012-04-05 16:04
by Isaac


1

If what you want to add isn't already an attribute of the wrapped object, then you can use this:

def wraps_with_first_line(f):
    def wrap(wrapper):
        wrapper = wraps(f)(wrapper)
        if not hasattr(wrapper, '_first_line'):
            wrapper._first_line = inspect.getsourcelines(f)[1] 
        return wrapper
    return wrap

If it is already an attribute of the wrapped object, use Sven's method.

2012-04-05 16:12
by agf
I ended up using a mix of your answer and Sven's, but it felt more like yours - Isaac 2012-04-06 03:02


3

You should rather follow the good practice of adding a __wrapped__ attribute pointing to the wrapped function than adding single attributes of that wrapped function. New versions of functools.wraps() do this automatically, but if you are using an older version of Python than 3.2, you can als easily extend wraps() to add __wrapped__:

def my_wraps(wrapped, **kwargs):
    def decorator(wrapper):
        functools.update_wrapper(wrapper, wrapped, **kwargs)
        wrapper.__wrapped__ = wrapped
    return decorator

Edit: Here's a function the extracts the original function from a possibly multiply decorated function:

def orig_function(f):
    try:
        while True:
            f = f.__wrapped__
    except AttributeError:
        return f
2012-04-05 16:08
by Sven Marnach
In my particular use case, I have some functions that are decorated/wrapped more than once—in those cases, looking at some_func.__wrapped__ would point me at the intermediate wrapper, right? Could I safely do something like the if not hasattr... in my original code to set _original_wrapped or some such? (Is this effectively what's done in agf's answer? - Isaac 2012-04-05 16:13
@Isaac: You could also write a utility function that extracts the original function by following the chain of __wrapped__ arguments. Of course you can do inside decorator() above whatever you want - Sven Marnach 2012-04-05 16:17
Ads