Stephen Lu
Stephen Lu

Reputation: 314

Python Decorator that maintains signature, allows modification to docstring, and allows optional arguments

So I have been stuck on this for some time and figure I would ask for some advice. I am attempting to create a decorator that can modify a functions docstring, allow for optional arguments or none, and is signature preserving. Separately, these can be done. And even getting two of the three.

The sample below seems to modify the docstring and allow for optional arguments or none. However, this method does not preserve the signature.

from functools import wraps
def thisDecorator(*targs, **tkwargs):
    def internalFunc(func):
        func.__doc__ = "Added this... {0}".format(func.__doc__)
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    if len(targs) == 1 and callable(targs[0]):
        return internalFunc(targs[0])
    else:
        return internalFunc

I have read about the decorator module that does preserve the signature but I can not seem to modify the docstring.

All the help is greatly appreciated

Upvotes: 3

Views: 1334

Answers (2)

Stephen Lu
Stephen Lu

Reputation: 314

By adding a decorator to the internal wrapper, this will preserve the function signature.

from functools import wraps
from decorator import decorator

def thisDecorator(*targs, **tkwargs):
    def internalFunc(func):
        func.__doc__ = "Added this... {0}".format(func.__doc__)
        @wraps(func)
        def wrapper(func, *args, **kwargs):
            return func(*args, **kwargs)
        return decorator(wrapper, func)
    if len(targs) == 1 and callable(targs[0]):
        return internalFunc(targs[0])
    else:
        return internalFunc

Upvotes: 4

Xavier
Xavier

Reputation: 1574

Maybe I don't fully understand your requirements, but this seems to be working for me:

class myDeco:
    def __init__(self, f):
        self.f = f
        if f.__doc__:
            self.__doc__ = 'great doc: ' + f.__doc__
        else:
            self.__doc__ = 'ooohh, no doc'
    def __call__(self, *args, **kwargs):
        print "decorator args and kwargs: {0}, {1}".format(args, kwargs)
        self.f(*args, **kwargs)

@myDeco
def test1():
    """test1 doc"""
    print 'test1'

@myDeco
def test2(a, b):
    print 'test2: {0}, {1}'.format(a,b)

@myDeco
def test3(a, *args):
    print "test3: {0}, {1}".format(a, args)

@myDeco
def test4(a, *args, **kwargs):
    print "test4: {0}, {1}, {2}".format(a, args, kwargs)


print test1.__doc__
print test2.__doc__
print test3.__doc__
print test4.__doc__

test1()
test2(1,2)
test3(1,2,3)
test4(1,2,3, foo=4, bar=5)

The signature is preserved, the doc is modified, optional arguments are allowed. The output is:

great doc: test1 doc
ooohh, no doc
ooohh, no doc
ooohh, no doc
decorator args and kwargs: (), {}
test1
decorator args and kwargs: (1, 2), {}
test2: 1, 2
decorator args and kwargs: (1, 2, 3), {}
test3: 1, (2, 3)
decorator args and kwargs: (1, 2, 3), {'foo': 4, 'bar': 5}
test4: 1, (2, 3), {'foo': 4, 'bar': 5}

Upvotes: 0

Related Questions