Some way to get arguments of current function for passing along to another function even when not using *args/**kw

Often times I have a function that does not make use of *args/**kw for various reasons but still needs to pass along all arguments to another function. An example would be:

def other_fun(a, b, c=7, d=9):
    ...


def myfun(a, b, c=7, d=9):
    v = other_fun(a, b, c=c, d=d)
    # do some other post processing of v, often times depending on the input arguments
    ...
    

For functions where there are a lot of arguments and/or the api is evolving and the arguments are changing - it would be nice to have some way of getting effectively what *args and **kwargs would be to myfun for use to pass them along to other_fun - even though I’m not using that pattern. This would reduce errors where we I add an argument and forget to update the sub calls. Example:

def other_fun(a, b, c=7, d=9):
    ...


def myfun(a, b, c=7, d=9):
    in_args, in_kwargs = get_this_call_args_kwargs()  # would return (a, b) dict(c=..., d=...)
    v = other_fun(*in_args, **in_kwargs)
    # do some other post processing of v, often times depending on the input arguments
    ...


Is there any way to do something like this already outside of manually enumerating the arguments?

Um…what stops You from doing this:

def myfun(*args, **kwargs):
   v = other_fun(*args, **kwargs)
   if kwargs['arg1']: ...

?

If you have a lot of arguments that go together, your design is probably bad. Group the arguments into a dataclass and pass that around.

4 Likes

Institutional constraint - our org has a policy to prefer writing out args so that the top level call is clearer what arguments it takes. Not saying it’s right but it is what it is

Please do yourself and others who must read your code a favour, and write simple, legible code with *args and **kwargs, and don’t ever use this:

import inspect

def other_fun(a, b, c=7, d=9):
    print(f'From other_fun {a=}, {b=}, {c=}, {d=}')


def myfun(a, b, c=7, d=9):
    this_frame = inspect.currentframe()

    arg_values = inspect.getargvalues(this_frame)
    
    kwargs = {}
    for k in arg_values.args:
        kwargs[k] = arg_values.locals[k]
    
    other_fun(**kwargs)
    
myfun(1, 2, 3, 4)
1 Like

Well, if it is just a requirement for the top level call, then you can gather these arguments into a dataclass, just as @NeilGirdhar said, and pass that object to bottom functions.

The ideal would be if it worked just like in JavaScript: by this.arguments (or similar, I frankly haven’t written anything in JS for a long time)

Unless…you can! Try using locals():

def my_fun(a, b, c=1, d=2):
    args = locals()

:slight_smile:

You could probably write something that did this using the inspect module (Signature and Parameter objects) and locals() to get at the local variables.

If you did write something like this, it’s possible that it would make a reasonable addition to the inspect module. I can’t see a strong use case for it myself (as you say, the reson you need it is more a matter of institutional policy rather than technical requirements) but IMO it fits the remit of the inspect module, and it’s tricky enough to get right that it might get accepted.

Thanks everyone - it seems like @NeilGirdhar 's answer is the right one. Interesting to know the other options

You may also create myfun as a dataclass and make other_fun a method instead:

@dataclass
class myfun:
    a: str
    b: str
    c: int = 7
    d: int = 9

    def __post_init__(self):
        v = self.other_func()
        ... # do some other post processing of v

    def other_func(self):
        ... # do something with self.a, self.b, self.c, self.d

myfun('foo', 'bar', 1, 2)