smarie / python-makefun

Dynamically create python functions with a proper signature.
https://smarie.github.io/python-makefun/
BSD 3-Clause "New" or "Revised" License
119 stars 16 forks source link

Pull/merge signature from other functions #56

Closed beasteers closed 3 years ago

beasteers commented 4 years ago

Basically, I've been making command-line tools a lot lately, and I get annoyed by having to continually pass arguments down the call stack. And when I want to alter a function parameter down the call stack, I need to pass the parameter thru each nested call.

If you only have one function that you want to pass arbitrary command-line arguments to, it's easy because you can just pass **kwargs to that function and you'll get a type error when that nested function is called.

I think it would be useful to be able to build parameter tracing and filtering to functions so that we could pass **kwargs to multiple functions and it would filter out only the relevant arguments to pass to each function.

I keep going back and forth because part of me knows that this could end up being a bad idea and may just end up making things messy, but the other part of me is fed up with having to write a bunch of var1=var1, var2=var2, ... all over the place.

This would also be useful in allowing you to be able to hoist default arguments out to a config file.

Here's a simple illustration of what I mean.

def func1(a, b, c=5):
    return a + b + c

def func2(d=1, e=2):
    return d - e

@makefun.args_to(func2) # get extra arguments from func2
def func3(c=5, **kw):
    return c * func2(**kw)

# signature: func3(c=5, d=1, e=2)

#####

@makefun.args_to(func1) # build signature of varargs and kwargs from func1
def zxcv(x, *a, **kw):
    return x - func1(*a, **kw)

# signature: zxcv(x, a, b, c=5)

# specify multiple signature sources
@makefun.args_to(func1)
@makefun.kwargs_to(func3)
def asdf(x, *a, **kw):
    return x - func1(*a, **kw) + func3(**kw)

# signature: asdf(x, a, b, c=5, d=1, e=2)

zxcv(1, 2, 3, z=10) # TypeError: zxcv() got an unexpected keyword argument 'z'

One issue I see with this is for varargs, if you called it like func1(5, *args, **kw), the *args would be offset by one argument and I'm not sure if you could fix that simply. So maybe only supporting kwargs could be a safer option although that problem still exists to a lesser extend for kwargs.

The next step would be to splice in relevant argument lists from one docstring to another, but that seems like it could be difficult so...

smarie commented 4 years ago

Thanks @beasteers and deeply sorry for only seeing now this message from july! I never saw the corresponding github notification - strange..

I understand what you propose here. I personally went for object-oriented when this happened to me: creating a container class for all the arguments seems a good way to pass an arbitrary number of them and let each nested function decide which part of it it requires. Of course this is not pure functional programming. Libraries such as munch may ease even further the process by providing dual object/dict containers.

As for makefun I think that this is a bit too far from its core target. Nevertherless you can reach your goal by directly concatenating the signatures (extracted using signature(f)) and then passing the result to @with_signature, so I guess that the resulting args_to / kwargs_to decorators will not be too complex to write.

smarie commented 3 years ago

@beasteers does the above answer your question ? I'll close the ticket now but feel free to reopen if this answer does not suit you