Expanded typing spec chapter for callables

One thing that jumps out to me from reading the mypy issue is that for a while there was no way at all to express a Callable ... in a def statement, first because it was inexpressible, then because mypy’s support for ParamSpec was incomplete.

Now it sounds like the following works? (Copying from Subtyping rules for `...` in callable types)

class Concrete:
    def __call__(self, x: int, *, y: int) -> None:
        pass

class Proto5[**P](Protocol):
    def __call__(self, x: int, *args: P.args, **kwargs: P.kwargs) -> None: ...

f: Proto5[...] = Concrete()  # OK (mypy and pyright)

I wonder if we could get rid of the special case and suggest using ParamSpec instead.

I don’t love the (*args: ..., **kwargs: ...) idea because I think it’s rather non-obvious what ... means in this context. In this issue, there’s some support for making a ... parameter annotation mean “inferred type.” I’m not advocating for this, just using it as an example of an also-plausible and entirely different interpretation.

2 Likes