Bug Report
When implementing a protocol generic wrt a ParamSpec type inference ends up inferring Any for the param spec in certain cases. Writing code around the way the type checker works we can force it to unify against a properly inferred param spec, but this limits our ability to write nice APIs.
To Reproduce
from typing import Protocol, reveal_type
class Context: pass
class Namer[**P](Protocol):
def name_for(self, *args: P.args, **kwargs: P.kwargs) -> str: ...
def execute_on(
self, ctx: Context, *args: P.args, **kwargs: P.kwargs
) -> None: ...
class Impl0:
@staticmethod
def name_for(x: int, y: str) -> str:
return 'Test'
@staticmethod
def execute_on(ctx: Context, x: int, y: str):
pass
class Impl1:
@staticmethod
def name_for(y: str) -> str:
return 'Test'
@staticmethod
def execute_on(ctx: Context, x: int, y: str):
pass
class UseImplFirst[**P, T]:
def __init__(self, impl: T, *args: P.args, **kwargs: P.kwargs) -> None:
self.impl = impl
self.args = args
self.kwargs = kwargs
def __call__(self: UseImplFirst[P, Namer[P]]) -> None:
pass
def useImplSecond[**P](impl: Namer[P], *args: P.args, **kwargs: P.kwargs):
pass
def useImplThird[**P](wrapper: UseImplFirst[P, Namer[P]]) -> None:
pass
__testImpl0: Namer[[int, str]] = Impl0
UseImplFirst(Impl0, 0, '0')()
useImplSecond(Impl0, 0, '0')
useImplThird(UseImplFirst(Impl0, 0, '0'))
__testImpl1: Namer[[int, str]] = Impl1 # Should fail (Fails)
UseImplFirst(Impl1, 1, '1')() # Should fail (Succeeds)
useImplSecond(Impl1, 1, '1') # Should fail (Succeeds)
useImplThird(UseImplFirst(Impl1, 1, '1')) # Should fail (Succeeds)
Expected Behavior / Actual Behavior
See the snippet above.
Woraround
The following forces mypy to infer the ParamSpec first and unify against the protocol implementation later:
class UseImpl[**P]:
def __init__(self, *args: P.args, **kwargs: P.kwargs):
self.args = args
self.kwargs = kwargs
def __call__(self, impl: Namer[P]) -> None:
pass
__testImpl0: Namer[[int, str]] = Impl0
UseImpl(0, '0')(Impl0)
__testImpl1: Namer[[int, str]] = Impl1 # Should fail (Fails)
UseImpl(1, '1')(Impl1) # Should fail (fails)
Bug Report
When implementing a protocol generic wrt a
ParamSpectype inference ends up inferringAnyfor the param spec in certain cases. Writing code around the way the type checker works we can force it to unify against a properly inferred param spec, but this limits our ability to write nice APIs.To Reproduce
Expected Behavior / Actual Behavior
See the snippet above.
Woraround
The following forces mypy to infer the
ParamSpecfirst and unify against the protocol implementation later: