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')
Wrapper g
is the function that will be returned by the decorator, so it can invoke not exactly f
function but rather any function with any arguments list, so this is important to provide to function f
**kwargs
.
After defining g
function, I would like to return g
function, but with arguments list copied from f
function and with the name of f
function. I tried this one below:
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 g
function I got the segmentation fault.
I googled how to solve this problem in a higher-level way, but no solutions were satisfied for me. I tried @functools.wraps
, but it doesn't change arguments list, and I tried @decorator.decorator
but it didn't call decorator function at all. Maybe you know better solutions for this problem?
UPDATE
Basically there is more extended 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 one fails with error below:
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 exact case I would like to implement.
Attempt 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)
Produces 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