
Hi All, I'm transferring a response over from https://github.com/python/peps/pull/1259 to a comment from Andrew Svetlov andrew.svetlov@gmail.com to keep our discussion consolidated.
I still think that modifying the parameters list signature is important. One very common example is `unittest.mock`:
@patch('__main__.SomeClass') def function(normal_argument, mock_class): print(mock_class is SomeClass)
Another one is `click`:
@cli.command() @click.pass_context def sync(ctx): click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
Note, `pass_context` decorator adds a parameter to the function signature. I can imagine the reverse operation easily.
Signature modifying decorators are definitely on our radar, but I'm not sure that this PEP is the appropriate moment to try to address them.
For altering the arguments, we have another PEP coming down the pipe that may fit your needs, ListVariadics.
For addition:
``` from typing import Callable, TypeVar from pyre_extensions.type_variable_operators import Concatenate Ts = pyre_extensions.ListVariadic("Ts")
def prepend_addition_argument(f: Callable[[Ts], int]) -> Callable[[Concatenate[int, Ts]], str]: def inner(x: int, *args: Ts) -> str: return str(x + f( *args)) return inner
@prepend_addition_argument def foo(x: int, y: int) -> int: return x + y
reveal_type(foo) # typing.Callable(foo)[[int, int, int], str] ```
For removal:
``` from typing import Callable, TypeVar, List from pyre_extensions.type_variable_operators import Concatenate Ts = pyre_extensions.ListVariadic("Ts") TReturn = TypeVar("TReturn")
def simple_partial_application( f: Callable[[Concatenate[float, Ts]], TReturn] ) -> Callable[[Ts], TReturn]: def inner( *args: Ts) -> TReturn: return f(42.0, *args) return inner @simple_partial_application def foo(x: float, y: str, z: bool) -> int: return 3
reveal_type(foo) # typing.Callable(foo)[[str, bool], int] ```
For more details on ListVariadics, you can read this presentation from the last typing summit (https://github.com/facebook/pyre-check/blob/master/docs/Variadic_Type_Variab...)
The trade-off here is that by going in and out of this ListVariadic, we lose the names of the arguments, meaning that, for example, `foo(y="A", z=True)` would not be accepted by Pyre in the second example, even though it would work in the runtime. Furthermore, this will not work at all for functions that have keyword-only arguments. However, as far as I'm aware, click and patch work positionally, so ListVariadics should work in that instance.
Supporting mutation in the full-fidelity ParameterSpecifications would require rich handling of name collision, which would get very complex very quickly. In my opinion working out the specification/implementation of that isn't worth blocking the rest of this, since it seems like the combination of these two features can get us a lot of the way there.
Currently, Python supports 5 types of arguments:
- positional-only
- regular arguments that can be used in both positional and named contexts
- keyword-only
- var-args: `*args`
- keyword var-args: `*kwargs`
The first three can have default arguments, it also affects a signature. I think it makes the proposed object much more complex
This complexity is a the reason why we have to make a purpose-built abstraction for parameter specifications, rather than being able to directly use a combination of other type system extensions like Map and List Variadics. However, I believe that the `TParams.args` and `TParams.kwargs` component model encompasses all of these kinds of arguments since they encode the set of positional arguments and keyword arguments of a single invocation of the signature. When the function has defaults, *args, **kwargs, etc, each argument is always either invoked positionally or by name. This is the reason why the `def f(*args, **kwargs): return g(*args, **kwargs)` pattern works in the runtime, and that's all that we need to be able to statically enforce in order to make the signature forwarding sound.