-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Feature Request
It would be awesome if it were possible to create a partial type spec for Callable
. For example, I might want to be able to specify that the first argument of a function must be an integer, but that any other parameters are allowed. This is useful if parameters are being curried to a function as in the decorator cast_first_arg
decorator below where the only requirement of func
is that it's first argument is an int
:
@cast_first_arg(int)
def func(x: int, y: str) -> Any:
# First positional argument is always cast to an integer.
# All other parameters are passed through without modification.
...
At the moment, the only alternative to a "partial callable type spec" is to use VarArg
and KwArg
, however this is problematic since, in the case of cast_first_arg
, the function doesn't actually need to accept variadic positional or keyword arguments.
Proposed Syntax
To provide a partial argument specification one should use ...
before, between, or after any extended callable types. Using this syntax the type spec for cast_first_arg
might look like this:
T = TypeVar("T", bound=Type)
F = Callable[[Var(int), ...], Any]
def cast_first_arg(cls: T) -> Callable[F, Callable[..., Any]]:
def decorator(func: F) -> Callable[..., Any]:
def wrapper(first: Any, *args: Any, **kwargs: Any): Any:
return func(cls(first), *args, **kwargs)
return wrapper
return decorator
Ellipsis After
When ...
follows some number of argument specifications should indicate that any other function parameters not explicitly indicated therein is allowed.
FirstArgIsInt = Callable[[Arg(int), ...], Any]
def function(a: int, b: str) -> Any: ... # 'b' is an unspecified, but allowe
f: FirstArgIsInt = function
Ellipsis Before
Similarly if ...
is placed before some number of argument specifications then only the last argument will have been specifically defined:
LastArgIsInt = Callable[[..., Arg(int)], Any]
def function(b: str, a: int) -> Any: ... # 'b' is an unspecified, but allowed
f: LastArgIsInt = function
Ellipsis Between
Lastly ...
placed between argument specifications should indicate that any parameters between the two explicitly defined parameters are allowed:
FirstAndLastAreInt = Callable[[Arg(int), ..., Arg(int)], Any]
def function(a: int, b: str, c: int) -> Any: ... # 'b' is an unspecified, but allowed
f: FirstAndLastAreInt = function
Multiple Ellipsis
This could add complications and edge cases to the feature implementation so perhaps this could be left for a later iteration of partial callable type specifications, however this syntax enables you to describe an even wider variety of functions:
HasArgNamedC = Callable[[..., Arg(Any, "c"), ...], Any]