Change function argument list
I have the following function:
def f(x, y):
print('x: %s' % x)
print('y: %s' % y)
And I would like to have a wrapper like:
def g(**kwargs):
print('hello')
f(**kwargs)
print('world')
The wrapper g
is a function that will be returned by the decorator, so it might not call exactly a function f
, but rather any function with any list of arguments, so this is important to provide a function f
**kwargs
.
After defining the function, g
I would like to return the function g
, but with the list of arguments copied from the function f
and with the name of the function f
. I tried the following:
import types f_code = f.__code__ g_code = g.__code__ g.__code__ = types.CodeType( f_code.co_argcount, g_code.co_nlocals, g_code.co_stacksize, g_code.co_flags, g_code.co_code, g_code.co_consts, g_code.co_names, f_code.co_varnames, g_code.co_filename, f_code.co_name, g_code.co_firstlineno, g_code.co_lnotab, g_code.co_freevars, g_code.co_cellvars )
But after calling the function, g
I got a segmentation fault.
I googled how to solve this problem at a higher level, but the solutions for me were not satisfied. I tried @functools.wraps
it but it doesn't change the argument list and I tried @decorator.decorator
it but it didn't call the decorator function at all. Maybe you know the best solutions for this problem?
UPDATE
There is basically a more advanced example below:
funcs = {}
def h(fi, *args, **kwargs):
def g(*args, **kwargs):
print('hello')
result = fi(**kwargs)
print('world')
return result
funcs[fi.func_code.co_name] = g
return g
@h
def f(x, y):
print('%s %s' % (x, y))
return 5
# z index is unneeded, so should be filtered
extended_arguments = {'x': 1, 'y': 2, 'z': 3}
func = funcs['f']
func_code = func.func_code
arguments = {
i: x for i, x in extended_arguments.items()
if x in func_code.co_varnames[:func_code.co_argcount]
}
func(**arguments)
Running this error fails with below error:
hello
Traceback (most recent call last):
File "x.py", line 26, in <module>
func(**arguments)
File "x.py", line 6, in g
result = fi(**kwargs)
TypeError: f() takes exactly 2 arguments (0 given)
This is the exact case that I would like to implement.
Trying with exec:
funcs = {}
def h(fi, *args, **kwargs):
fi_code = fi.func_code
exec(
'''
def g(%s*args, **kwargs):
print('hello')
result = fi(**kwargs)
print('world')
return result
''' % ''.join(
'%s, ' % varname
for varname in fi_code.co_varnames[:fi_code.co_argcount]
)
)
funcs[fi_code.co_name] = g
return g
@h
def f(x, y):
print('%s %s' % (x, y))
return 5
# z index is unneeded, so should be filtered
extended_arguments = {'x': 1, 'y': 2, 'z': 3}
func = funcs['f']
func_code = func.func_code
arguments = {
i: x for i, x in extended_arguments.items()
if i in func_code.co_varnames[:func_code.co_argcount]
}
func(**arguments)
Throws the following error:
hello
Traceback (most recent call last):
File "y.py", line 34, in <module>
func(**arguments)
File "<string>", line 4, in g
NameError: global name 'fi' is not defined
source to share
No one has answered this question yet
See similar questions:
or similar: