Type hints for kwarg overrides

If we ignore hybrid (kw-or-positional) parameters¹, a decorator/higher-kinded-function can do one of the following:

  1. prepend/remove-from-front positional arg(s) (already supported)
  2. append/remove-from-end positional arg(s) when there’s no varargs
  3. add/delete keyword-only arg(s)

The supported use supports excluding functions from being used, e.g. def decor(f: Callable[Concatenate[int, P], R]) -> ... expresses “f needs to have at least one positional argument, and its first positional argument needs to be of type int”.

For keyword-only parameters, we just use name instead of position. So I don’t see how removing positionals from the end or, adding/removing keyword-only args are in any way more complex.

# pop first pos argument of type int.
# Needs≥1 arg, first arg to be int
def pop0_pos(f: Callable[Concatenate[int, P], R]) -> Callable[P, R]: ...

# pop last pos argument of type int.
# Needs≥1 arg, no varargs, last arg to be int
def pop_pos(f: Callable[Concatenate[P, int], R]) -> Callable[P, R]: ...

# pop keyword arg “size” of type int. (placeholder syntax, don’t worry about it)
# Needs a kwarg named “size” of type int
def pop_kw(f: Callable[Concatenate[P, "size" : int], R]) -> Callable[P, R]: ...

The only case where prepending is simpler than the rest is when Concatenate appears in a return-type-only position, but again, we already see it having requirements for the function’s signature in all other cases, so I don’t see why that would be special:

# prepend pos argument of type int.
# No requirements
def prepend_pos(f: Callable[P, R]) -> Callable[Concatenate[int, P], R]: ...

# append pos argument of type int.
# Needs no varargs
def append_pos(f: Callable[P, R]) -> Callable[Concatenate[P, int], R]: ...

# add kwarg “size” of type int.
# Needs `f` to not have an argument named “size”.
def add_kw(f: Callable[P, R]) -> Callable[Concatenate[P, "size" : int], R]: ...

Things like replacing the type of a keyword argument works the same way as replacing the type of the first positional argument, e.g.:

def replace_arg0(f: Callable[Concatenate[int, P], R])
                 -> Callable[Concatenate[str, P], R]:

def replace_kwarg(f: Callable[Concatenate[P, "size" : int], R])
                  -> Callable[Concatenate[P, "size" : str], R]:

¹I don’t think it’s valuable to expand the use of keyword-or-positional arguments, so I think it’s fine to reduce complexity by only having syntax for adding/removing positional-only and keyword-only arguments

1 Like