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

      

0


source to share





All Articles