On Nov 15, 2019, at 16:07, Danilo J. S. Bellini <[email protected]>
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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/FIIDZ4OOL23ZVQFKQRBRP64GZGLJGKOK/
Code of Conduct: http://python.org/psf/codeofconduct/