Reputation: 3978
I have a function
def func(a,b,c,d):
...
and I am trying to write a decorator that understands the arguments and logs some of them to a different system.
def decorator(func):
def new_func(*args, **kwargs):
if (func.__name__ == 'func'):
a = ?
b = ?
c = ?
d = ?
else:
a = ?
b = ?
c = ?
d = ?
log_to_system(a, b, c, d)
return func(*args, **kwargs)
return new_func
The problem is that the decorator doesn't have an easy way to extract the a,b,c,d values from both the args and the kwargs since the user can pass these in using either positional or keyword arguments. I would also like to keep this generic since this decorator could be used on various different functions.
Is there a library or a utility that can extract the values of the parameters from args and kwargs easily?
Upvotes: 3
Views: 141
Reputation: 4622
You can use this general decorator to dump the parameter values:
def dump_args(func):
# Decorator to print function call details - parameters names and effective values
def wrapper(*func_args, **func_kwargs):
arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
args = func_args[:len(arg_names)]
defaults = func.__defaults__ or ()
args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
params = list(zip(arg_names, args))
args = func_args[len(arg_names):]
if args: params.append(('args', args))
if func_kwargs: params.append(('kwargs', func_kwargs))
return func(*func_args, **func_kwargs)
return wrapper
Credits belong to @aliteralmind, he suggested this decorator in this StackOverflow post.
Upvotes: 0
Reputation: 106488
A simple approach is to make your log_to_system
function accept variable parameters and variable keyword parameters in addition to the known parameters that it will actually log, so that you can simply pass on the variable arguments and variable keyword arguments from the decorated function to log_to_system
and let the interpreter extract the parameters a
, b
, c
and d
for you:
def log_to_system(a, b, c, d, *args, **kwargs):
print(a, b, c, d)
def decorator(func):
def new_func(*args, **kwargs):
log_to_system(*args, **kwargs)
return func(*args, **kwargs)
return new_func
@decorator
def func(a, b, c, d, e):
pass
func(1, 2, c=3, d=4, e=5)
This outputs:
1 2 3 4
Alternatively, you can use inspect.signature
to obtain a dict of arguments after binding the given variable arguments and keyword arguments to the decorated function's signature, so that you can call log_to_system
with just the parameters it needs:
import inspect
def log_to_system(a, b, c, d):
print(a, b, c, d)
def decorator(func):
sig = inspect.signature(func)
def new_func(*args, **kwargs):
arguments = sig.bind(*args, **kwargs).arguments
log_to_system(**{k: arguments[k] for k in log_to_system.__code__.co_varnames})
return func(*args, **kwargs)
return new_func
@decorator
def func(a, b, c, d, e):
pass
func(1, 2, c=3, d=4, e=5)
This outputs:
1 2 3 4
Upvotes: 2
Reputation: 8500
You can use inspect
, working example:
import inspect
def deco(func):
signature = inspect.signature(func)
def _deco(*args, **kwargs):
binded = signature.bind(*args, **kwargs)
arguments = binded.arguments # OrderedDict
print(arguments.items())
return func(*args, **kwargs)
return _deco
@deco
def foo(a, b, c, d):
pass
@deco
def bar(d, c, b, a):
pass
foo(1, 2, c=3, d=4) # odict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
bar(1, a=2, b=3, c=4) # odict_items([('d', 1), ('c', 4), ('b', 3), ('a', 2)])
Upvotes: 1