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?
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
The print function parameters are: *objects – an object/objects to be printed. sep – the separator between multiple printed objects. end – the character/string printed at the end after the object.
In Python, decorators can be either functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class. We can add functionality to the function by defining methods in the decorating class.
However, Python provides a much easier way for us to apply decorators. We simply use the @ symbol before the function we'd like to decorate.
Here is the updated version for Python 3.6+
import inspect
def dump_args(func):
"""
Decorator to print function call details.
This includes parameters names and effective values.
"""
def wrapper(*args, **kwargs):
func_args = inspect.signature(func).bind(*args, **kwargs).arguments
func_args_str = ", ".join(map("{0[0]} = {0[1]!r}".format, func_args.items()))
print(f"{func.__module__}.{func.__qualname__} ( {func_args_str} )")
return func(*args, **kwargs)
return wrapper
@dump_args
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)
Old version
Working version with default values:
def dumpArgs(func):
'''Decorator to print function call details - parameters names and effective values'''
def wrapper(*func_args, **func_kwargs):
arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
args = func_args[:len(arg_names)]
defaults = func.func_defaults or ()
args = args + defaults[len(defaults) - (func.func_code.co_argcount - len(args)):]
params = zip(arg_names, args)
args = func_args[len(arg_names):]
if args: params.append(('args', args))
if func_kwargs: params.append(('kwargs', func_kwargs))
print func.func_name + ' (' + ', '.join('%s = %r' % p for p in params) + ' )'
return func(*func_args, **func_kwargs)
return wrapper
@dumpArgs
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)
Result:
>>> test ( a = 1, b = 4, c = 'blah-blah' )
test ( a = 1, b = 3, c = 'blah-blah' )
test ( a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} )
test ( a = 1, b = 2, c = 3, args = (4, 5), kwargs = {'d': 6, 'g': 12.9} )
Sorry its a bit messy. I modified some code from Easy Dump of Function Arguments in the PythonDecoratorLibrary.
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})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With