typing icon indicating copy to clipboard operation
typing copied to clipboard

ParamSpec: No support for adding keyword-only argument

Open ItsDrike opened this issue 3 years ago • 2 comments

I've been looking through PEP 612 but I just wasn't able to find any way to implement behavior that would allow me to do this:

P = ParamSpec("P")
R = TypeVar("R")

def foo(f: Callable[P, R]) -> Callable[P, List[R]]:  # Here, the parameters are actually P + optional kw-only int x = 3
    def inner(*args: P.args, x: int = 3, **kwargs: P.kwargs) -> List[R]:
        results = []
        for _ in range(x):
          ret = f(*args, **kwargs)
          results.append(ret)
        return results
    return inner

@foo
def bar(p: int):
    return p + 2

bar(5)  # Acceptable
bar(5, x=8)  # Should also be Acceptable

Proposed solution:

Callable[[P.args, x: int = 3, P.kwargs], List[R]]

EDIT: I didn't notice that this was already in rejected alternatives section (I didn't scroll that far), however I still think an implementation like this would be worth adding since I have already encountered the need for this twice and there's simply absolutely no way to handle it.

I'm fine with the proposed syntax suggestion of

Concatenate[("x", int), P]

Though it didn't mention anything about default values, but those could just be handled by a third value in that tuple. It would probably be a lot easier to implement than my proposed suggestion, though to be fair, at least to me, my suggestion looks a bit cleaner.

ItsDrike avatar Jan 06 '22 02:01 ItsDrike

I've encountered this as well. In particular, I'm trying to recreate a decorator for a React-like framework in Python. I have a decorator that, given a user's function, returns a new function that accepts all the arguments of the one give, but with extra arguments that the decorator itself consumes and does not pass on. This would look a bit like this:

P = ParamSpec("P")

def component(f: Callable[P, Any]) -> Callable[Concatenate[(str | None, "key"), P], Any]: ...

One point of note is that I need (str | None, "key") to be a keyword-only arg. The syntax suggested in the PEP seems to concatenate a keyword or positional argument. Perhaps, placing it after the ParamSpec in the concatenation could indicate a keyword-only argument?

Concatenate[P, (str | None, "key")]

rmorshea avatar Sep 21 '22 04:09 rmorshea

This is mentioned here: https://peps.python.org/pep-0612/#concatenating-keyword-parameters

But I think supporting this would open the door for typing a lot of powerful decorators that exist in the wild.

flying-sheep avatar Feb 26 '24 12:02 flying-sheep