Open vxgmichel opened 4 months ago
To add a bit more information, the same example works fine if the I
type is included within the P
param spec (see that in the playground).
However, being able to extract a type from the param spec as seen in the first code sample is really useful. In particular, the type of the first argument might appear in other methods of the protocol, for instance:
# An object that can convert I to O with parameters P
class ConvertorProtocol(Protocol[I, P, O]):
def convert(self, __source: I, *args: P.args, **kwargs: P.kwargs) -> O:
...
def prepare(self, *args: P.args, **kwargs: P.kwargs) -> Callable[[I], O]:
...
I just came up with a simpler example that can reproduce the regression, without Protocol
nor decorators:
from typing import (
Generic,
TypeVar,
Callable,
Concatenate,
ParamSpec,
reveal_type,
)
from dataclasses import dataclass
X = TypeVar("X")
P = ParamSpec("P")
I = TypeVar("I", contravariant=True)
O = TypeVar("O", covariant=True)
@dataclass
class Convertor(Generic[I, P, O]):
convert: Callable[Concatenate[I, P], O]
def as_list(source: X, repeat: int = 1) -> list[X]:
return [source] * repeat
as_list_convertor = Convertor(convert=as_list)
if __name__ == "__main__":
reveal_type(as_list_convertor.convert)
result = as_list_convertor.convert(1, repeat=3)
reveal_type(result)
assert result == [1, 1, 1]
You can see in the playground that it passes with mypy 1.6 but starts failing with mypy 1.7.
I am having the same issue, mypy
infers Never
for the type-variable. Interestingly, binding the expression first causes it to infer Any
instead. Maybe this could have similar causes as https://github.com/microsoft/pyright/issues/8165?
from typing import Protocol, Sequence, reveal_type, Any
class ClassDecorator[T, **P](Protocol):
def __call__(self, cls: type[T], /, *args: P.args, **kwargs: P.kwargs) -> type[T]: ...
def as_class_decorator[T, **P](x: ClassDecorator[T, P]) -> ClassDecorator[T, P]:
return x
def pprint_sequence[S: Sequence](cls: type[S], /, **kwds: Any) -> type[S]:
return cls
reveal_type(as_class_decorator(pprint_sequence))
# >>> mypy: ClassDecorator[Never, [**kwds: Any]]
# >>> pyright: ClassDecorator[S@pprint_sequence, (**kwds: Any)]
# if we assign to a variable first, we at least don't get `Never`
fn = as_class_decorator(pprint_sequence)
reveal_type(fn)
# >>> mypy: ClassDecorator[Any, [**kwds: Any]]
# >>> pyright: ClassDecorator[S@pprint_sequence, (**kwds: Any)]
EDIT: I found a simpler example reproducing the regression, see below
The following code used to pass with mypy 1.6.1
However, it no longer passes with mypy 1.7.0 and later:
Try it in the playground.
Note that this sample works in the pyright playground.