On Nov 15, 2019, at 16:07, Danilo J. S. Bellini <danilo.bell...@gmail.com> 
wrote:
> 
> Hi!
> 
> # First simple idea:
> 
> TL;DR: Let's show a inspect.Signature instance to the help messages instead 
> of just writing its actual "vague" signature [usually (*args, **kwargs)].

But that’s what already happens:

    >>> import functools
    >>> def f(a, b=2, **kw):
    ...   print(a,b,kw)
    ...   
    >>> @functools.wraps(f)
    ... def g(*args, **kw):
    ...   return f(*args, **kw)
    ...   
    >>> import inspect
    >>> inspect.signature(f)
    <Signature (a, b=2, **kw)>
    >>> inspect.signature(g)
    <Signature (a, b=2, **kw)>
    >>> help(f)
    Help on function f in module __main__:
    
    f(a, b=2, **kw)

    >>> help(g)
    Help on function f in module __main__:
    
    f(a, b=2, **kw)

The signature is there, including with functions.wraps. So that’s your main 
idea, and your first idea.

> # Second idea:
> 
> The partial objects might include an automatic propagation of the 
> __signature__, if there's one already set. That can be lazy (the partial 
> object __signature__ is created only when requested).

>>> h = functools.partial(f, 3)
>>> inspect.signature(h)
<Signature (b=2, **kw)>

So that’s already there too.

> # Third idea:
> 
> Setting the function signature itself from an inspect.Signature object.

    >>> f.__signature__ = inspect.signature(h)
    >>> inspect.signature(f)
    <Signature (b=2, **kw)>

> That might be a new functools.wraps keyword argument, like:
> 
> @functools.wraps(wrapped_func, signature=wrapped_func.__signature__)
> def wrapper_func(*args, **kwargs):
>     ... # some code

You can already add __signature__ to assigned if you want, which will copy it 
from wrapped_func, and I don’t know why you’d want to set any signature besides 
that one. But normally you don’t need to do that.

> Or perhaps a new syntax, like:
> 
> def wrapper_func(*args, **kwargs) with wrapped_func.__signature__:
>     ... # some code

Why would you want to elevate __signature__ that way? What’s wrong with 
treating it the same as the other mutable attributes and just assigning it 
normally (or with a decorator)?

> Externally, this would behave like a function with the given (more restrict) 
> signature (e.g. raising TypeError if it doesn't match the given 
> wrapped_func.__signature__ in the example).

But that’s not what signatures are for in Python. If you want to force the 
function to check a certain parameter signature, just make that the actual 
parameter list. Why make the parameter list *args, **kw instead, just to try to 
duplicate what you’d have gotten if you didn’t do that?

And for the case of wrapper functions, you usually don’t have to do anything. 
If the arguments don’t match the intended signature, you’ll get a TypeError 
from the wrapped function, without having to do anything in the wrapper.

> Internally, the function would just use the explicit args and kwargs (or 
> whatever the explicit signature happen to be).
> 
> As an example, suppose the wrapped_func has the (a, b, c=3, *, d=4) 
> signature, the wrapper_func of the proposed syntax would behave like this for 
> this example:
> 
> def wrapper_func(a, b, c=3, *, d=4):
>     def internal_wrapper_func(*args, **kwargs):
>         ... # some code
>     return internal_wrapper_func(a, b, c=3, *, d=4)

You can’t call a function that way. And if you could, you’d be ignoring any 
values the user passed in for c and d to force the defaults instead, which 
doesn’t sound like a good idea. What were you actually trying to do? Maybe (a, 
b, c, d=d)?

> The most obvious use cases are the creation of simple function signature 
> validators (to avoid losing a parameter left untouched in the kwargs dict, to 
> "fail fast" and fix the argument name in code)

If you really want this, you can write a decorator that takes a signature and a 
function, and returns a function that binds the signature before passing the 
result on to the function. This is about 5 lines of code, and not that 
complicated.

But you rarely want it. Again, you can just define your function with the right 
signature in the first place and you get this for free, or for a wrapper do 
nothing and also get it for free. Binding a signature is a lot slower, doesn’t 
work on uncooperative functions (like non-argclinic C functions), and doesn’t 
give you any benefits except in special cases (s.g., it could be useful for an 
auto-curry decorator that accumulates args into a partial until the entire 
signature is bound, and then calls the function).

Maybe those special cases are common enough to put a helper in functools or 
something. But i don’t think they’re common enough for a whole new protocol, 
much less new syntax. 
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/FIIDZ4OOL23ZVQFKQRBRP64GZGLJGKOK/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to