warvariuc warvariuc - 2 months ago 7
Python Question

Decorator to print function call details - parameters names and effective values

I want to make a function that being a decorator to another function will print that function call details - parameters names and effective values. My current implementation is this.

def describeFuncCall(func):
'''Decorator to print function call details - parameters names and effective values'''
def wrapper(*func_args, **func_kwargs):
print 'func_code.co_varnames =', func.func_code.co_varnames
print 'func_code.co_argcount =', func.func_code.co_argcount
print 'func_args =', func_args
print 'func_kwargs =', func_kwargs
params = []
for argNo in range(func.func_code.co_argcount):
argName = func.func_code.co_varnames[argNo]
argValue = func_args[argNo] if argNo < len(func_args) else func.func_defaults[argNo - func.func_code.co_argcount]
params.append((argName, argValue))
for argName, argValue in func_kwargs.items():
params.append((argName, argValue))
params = [ argName + ' = ' + repr(argValue) for argName, argValue in params]
print(func.__name__ + ' ( ' + ', '.join(params) + ' )')
return func(*func_args, **func_kwargs)
return wrapper


@describeFuncCall
def test(a, b = 4, c = 'blah-blah', *args, **kwargs):
pass


test(1)
#test(1, 3)
#test(1, d = 5)
test(1, 2, 3, 4, 5, d = 6, g = 12.9)


Kinda works, but with some bugs:

For call

test(1, 2, 3, 4, 5, d = 6, g = 12.9)


it prints

test ( a = 1, b = 2, c = 3, d = 6, g = 12.9 )
.

The expected result is

test ( a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} )


I got stuck here. Can you help me to find the right solution?

Answer

Sorry its a bit messy. I modified some code from http://wiki.python.org/moin/PythonDecoratorLibrary#Easy_Dump_of_Function_Arguments

def dump_args(func):
    "This decorator dumps out the arguments passed to a function before calling it"
    argnames = func.func_code.co_varnames[:func.func_code.co_argcount]
    fname = func.func_name
    def echo_func(*args,**kwargs):
        print fname, "(", ', '.join(
            '%s=%r' % entry
            for entry in zip(argnames,args[:len(argnames)])+[("args",list(args[len(argnames):]))]+[("kwargs",kwargs)]) +")"
    return echo_func

@dump_args
def test(a, b = 4, c = 'blah-blah', *args, **kwargs):
    pass

test(1, 2, 3, 4, 5, d = 6, g = 12.9)

output:

test ( a=1, b=2, c=3, args=[4, 5], kwargs={'d': 6, 'g': 12.9})