You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incompatible ParamSpecs are not reported as issue #17766
I think mypy should error out on the foo() call since it's not possible to resolve both Callables to a common ParamSpec P.
When I put the same code into VSCode, pylance reports the following error:
Argument of type "(x: str) -> None" cannot be assigned to parameter "cb2" of type "(**P@foo) -> None" in function "foo"
Type "(x: str) -> None" is not assignable to type "(x: int) -> None"
Parameter 1: type "int" is incompatible with type "str"
"int" is not assignable to "str"
Pylance[reportArgumentType](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportArgumentType)
Actual Behavior
mypy does not report any issues:
Success: no issues found in 1 source file
Your Environment
See the playground link above, but when I run into this locally, I have:
Mypy version used: mypy 1.11.2 (compiled: yes)
Mypy command-line flags: --strict
Mypy configuration options from mypy.ini (and other config files): None
I'm not sure what the right course of action is here.
First of all, P in the snippet can be satisfied by def _(x: Never) -> None, and that's exactly how mypy solves these constraints. Function with such signature can exist, so formally there is no bug.
mypy does detect truly non-overlapping signatures (e.g. def f(x: int) -> None and def g(*, y: str) -> None are rejected in foo(f, g)). It doesn't:(
However, such functions are of rather limited use (assert_never is one of them), and the requested behaviour is more intuitive and useful. This behaviour certainly needs to be formalized first: should that be "all arguments inferred to Never"? "at least one argument inferred to Never"? Something else?
Current spec does not explicitly define incompatible ParamSpec caused by one or more args with constraints satisfied only by bottom type:
Just as with traditional TypeVars, a user may include the same ParamSpec multiple times in the arguments of the same function, to indicate a dependency between multiple arguments. In these cases a type checker may choose to solve to a common behavioral supertype (i.e. a set of parameters for which all of the valid calls are valid in both of the subtypes), but is not obligated to do so.
# Quoted from typing specP=ParamSpec("P")
deffoo(x: Callable[P, int], y: Callable[P, int]) ->Callable[P, bool]: ...
defx_y(x: int, y: str) ->int: ...
defy_x(y: int, x: str) ->int: ...
foo(x_y, x_y) # Should return (x: int, y: str) -> bool# (a callable with two positional-or-keyword parameters)foo(x_y, y_x) # Could return (a: int, b: str, /) -> bool# (a callable with two positional-only parameters)# This works because both callables have types that are# behavioral subtypes of Callable[[int, str], int]defkeyword_only_x(*, x: int) ->int: ...
defkeyword_only_y(*, y: int) ->int: ...
foo(keyword_only_x, keyword_only_y) # Rejected
There's nothing technically challenging in changing this behaviour:
diff --git a/mypy/applytype.py b/mypy/applytype.py
index e87bf939c..e443f98db 100644
--- a/mypy/applytype.py+++ b/mypy/applytype.py@@ -43,6 +43,10 @@ def get_target_type(
if isinstance(p_type, UninhabitedType) and tvar.has_default():
return tvar.default
if isinstance(tvar, ParamSpecType):
+ if isinstance(p_type, Parameters) and any(isinstance(get_proper_type(p), UninhabitedType) for p in p_type.arg_types):+ if skip_unsatisfied:+ return None+ report_incompatible_typevar_value(callable, type, tvar.name, context)
return type
if isinstance(tvar, TypeVarTupleType):
return type
Activity
[-]Incompatible ParamSpecs are not causing any issue[/-][+]Incompatible ParamSpecs are not reported as issue[/+]sterliakov commentedon Feb 5, 2025
I'm not sure what the right course of action is here.
First of all, P in the snippet can be satisfied by
def _(x: Never) -> None, and that's exactly how mypy solves these constraints. Function with such signature can exist, so formally there is no bug.It doesn't:(mypydoes detect truly non-overlapping signatures (e.g.def f(x: int) -> Noneanddef g(*, y: str) -> Noneare rejected infoo(f, g)).However, such functions are of rather limited use (
assert_neveris one of them), and the requested behaviour is more intuitive and useful. This behaviour certainly needs to be formalized first: should that be "all arguments inferred to Never"? "at least one argument inferred to Never"? Something else?Current spec does not explicitly define incompatible ParamSpec caused by one or more args with constraints satisfied only by bottom type:
There's nothing technically challenging in changing this behaviour: