Skip to content

Commit 55247c4

Browse files
authored
Apply TypeVar defaults to callables (PEP 696) (#16842)
Implement type application for callables with TypeVar defaults. Similar to previous PRs, support for TypeVarTuples is still TODO. Ref: #14851
1 parent e40935e commit 55247c4

File tree

4 files changed

+122
-19
lines changed

4 files changed

+122
-19
lines changed

mypy/applytype.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ def apply_generic_arguments(
9393
bound or constraints, instead of giving an error.
9494
"""
9595
tvars = callable.variables
96-
assert len(tvars) == len(orig_types)
96+
min_arg_count = sum(not tv.has_default() for tv in tvars)
97+
assert min_arg_count <= len(orig_types) <= len(tvars)
9798
# Check that inferred type variable values are compatible with allowed
9899
# values and bounds. Also, promote subtype values to allowed values.
99100
# Create a map from type variable id to target type.

mypy/checkexpr.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4809,21 +4809,29 @@ def apply_type_arguments_to_callable(
48094809
tp = get_proper_type(tp)
48104810

48114811
if isinstance(tp, CallableType):
4812-
if len(tp.variables) != len(args) and not any(
4813-
isinstance(v, TypeVarTupleType) for v in tp.variables
4814-
):
4812+
min_arg_count = sum(not v.has_default() for v in tp.variables)
4813+
has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in tp.variables)
4814+
if (
4815+
len(args) < min_arg_count or len(args) > len(tp.variables)
4816+
) and not has_type_var_tuple:
48154817
if tp.is_type_obj() and tp.type_object().fullname == "builtins.tuple":
48164818
# TODO: Specialize the callable for the type arguments
48174819
return tp
4818-
self.msg.incompatible_type_application(len(tp.variables), len(args), ctx)
4820+
self.msg.incompatible_type_application(
4821+
min_arg_count, len(tp.variables), len(args), ctx
4822+
)
48194823
return AnyType(TypeOfAny.from_error)
48204824
return self.apply_generic_arguments(tp, self.split_for_callable(tp, args, ctx), ctx)
48214825
if isinstance(tp, Overloaded):
48224826
for it in tp.items:
4823-
if len(it.variables) != len(args) and not any(
4824-
isinstance(v, TypeVarTupleType) for v in it.variables
4825-
):
4826-
self.msg.incompatible_type_application(len(it.variables), len(args), ctx)
4827+
min_arg_count = sum(not v.has_default() for v in it.variables)
4828+
has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in it.variables)
4829+
if (
4830+
len(args) < min_arg_count or len(args) > len(it.variables)
4831+
) and not has_type_var_tuple:
4832+
self.msg.incompatible_type_application(
4833+
min_arg_count, len(it.variables), len(args), ctx
4834+
)
48274835
return AnyType(TypeOfAny.from_error)
48284836
return Overloaded(
48294837
[

mypy/messages.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,18 +1347,21 @@ def override_target(self, name: str, name_in_super: str, supertype: str) -> str:
13471347
return target
13481348

13491349
def incompatible_type_application(
1350-
self, expected_arg_count: int, actual_arg_count: int, context: Context
1350+
self, min_arg_count: int, max_arg_count: int, actual_arg_count: int, context: Context
13511351
) -> None:
1352-
if expected_arg_count == 0:
1352+
if max_arg_count == 0:
13531353
self.fail("Type application targets a non-generic function or class", context)
1354-
elif actual_arg_count > expected_arg_count:
1355-
self.fail(
1356-
f"Type application has too many types ({expected_arg_count} expected)", context
1357-
)
1354+
return
1355+
1356+
if min_arg_count == max_arg_count:
1357+
s = f"{max_arg_count} expected"
13581358
else:
1359-
self.fail(
1360-
f"Type application has too few types ({expected_arg_count} expected)", context
1361-
)
1359+
s = f"expected between {min_arg_count} and {max_arg_count}"
1360+
1361+
if actual_arg_count > max_arg_count:
1362+
self.fail(f"Type application has too many types ({s})", context)
1363+
else:
1364+
self.fail(f"Type application has too few types ({s})", context)
13621365

13631366
def could_not_infer_type_arguments(
13641367
self, callee_type: CallableType, n: int, context: Context

test-data/unit/check-typevar-defaults.test

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]:
118118
[builtins fixtures/tuple.pyi]
119119

120120
[case testTypeVarDefaultsClass1]
121-
from typing import Generic, TypeVar
121+
from typing import Generic, TypeVar, Union, overload
122122

123123
T1 = TypeVar("T1")
124124
T2 = TypeVar("T2", default=int)
@@ -137,6 +137,15 @@ def func_a1(
137137
reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]"
138138
reveal_type(d) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]"
139139

140+
k = ClassA1()
141+
reveal_type(k) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]"
142+
l = ClassA1[float]()
143+
reveal_type(l) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]"
144+
m = ClassA1[float, float]()
145+
reveal_type(m) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]"
146+
n = ClassA1[float, float, float]() # E: Type application has too many types (expected between 0 and 2)
147+
reveal_type(n) # N: Revealed type is "Any"
148+
140149
class ClassA2(Generic[T1, T2, T3]): ...
141150

142151
def func_a2(
@@ -152,6 +161,44 @@ def func_a2(
152161
reveal_type(d) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]"
153162
reveal_type(e) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]"
154163

164+
k = ClassA2() # E: Need type annotation for "k"
165+
reveal_type(k) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]"
166+
l = ClassA2[float]()
167+
reveal_type(l) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.int, builtins.str]"
168+
m = ClassA2[float, float]()
169+
reveal_type(m) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.str]"
170+
n = ClassA2[float, float, float]()
171+
reveal_type(n) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]"
172+
o = ClassA2[float, float, float, float]() # E: Type application has too many types (expected between 1 and 3)
173+
reveal_type(o) # N: Revealed type is "Any"
174+
175+
class ClassA3(Generic[T1, T2]):
176+
@overload
177+
def __init__(self) -> None: ...
178+
@overload
179+
def __init__(self, var: int) -> None: ...
180+
def __init__(self, var: Union[int, None] = None) -> None: ...
181+
182+
def func_a3(
183+
a: ClassA3,
184+
b: ClassA3[float],
185+
c: ClassA3[float, float],
186+
d: ClassA3[float, float, float], # E: "ClassA3" expects between 1 and 2 type arguments, but 3 given
187+
) -> None:
188+
reveal_type(a) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]"
189+
reveal_type(b) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.int]"
190+
reveal_type(c) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.float]"
191+
reveal_type(d) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]"
192+
193+
k = ClassA3() # E: Need type annotation for "k"
194+
reveal_type(k) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]"
195+
l = ClassA3[float]()
196+
reveal_type(l) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.int]"
197+
m = ClassA3[float, float]()
198+
reveal_type(m) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.float]"
199+
n = ClassA3[float, float, float]() # E: Type application has too many types (expected between 1 and 2)
200+
reveal_type(n) # N: Revealed type is "Any"
201+
155202
[case testTypeVarDefaultsClass2]
156203
from typing import Generic, ParamSpec
157204

@@ -172,6 +219,15 @@ def func_b1(
172219
reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]"
173220
reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]"
174221

222+
k = ClassB1()
223+
reveal_type(k) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]"
224+
l = ClassB1[[float]]()
225+
reveal_type(l) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]"
226+
m = ClassB1[[float], [float]]()
227+
reveal_type(m) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]"
228+
n = ClassB1[[float], [float], [float]]() # E: Type application has too many types (expected between 0 and 2)
229+
reveal_type(n) # N: Revealed type is "Any"
230+
175231
class ClassB2(Generic[P1, P2]): ...
176232

177233
def func_b2(
@@ -185,6 +241,15 @@ def func_b2(
185241
reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]"
186242
reveal_type(d) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
187243

244+
k = ClassB2() # E: Need type annotation for "k"
245+
reveal_type(k) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
246+
l = ClassB2[[float]]()
247+
reveal_type(l) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]"
248+
m = ClassB2[[float], [float]]()
249+
reveal_type(m) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]"
250+
n = ClassB2[[float], [float], [float]]() # E: Type application has too many types (expected between 1 and 2)
251+
reveal_type(n) # N: Revealed type is "Any"
252+
188253
[case testTypeVarDefaultsClass3]
189254
from typing import Generic, Tuple, TypeVar
190255
from typing_extensions import TypeVarTuple, Unpack
@@ -206,6 +271,11 @@ def func_c1(
206271
# reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO
207272
reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]"
208273

274+
# k = ClassC1() # TODO
275+
# reveal_type(k) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO
276+
l = ClassC1[float]()
277+
reveal_type(l) # N: Revealed type is "__main__.ClassC1[builtins.float]"
278+
209279
class ClassC2(Generic[T3, Unpack[Ts3]]): ...
210280

211281
def func_c2(
@@ -217,6 +287,13 @@ def func_c2(
217287
# reveal_type(b) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO
218288
reveal_type(c) # N: Revealed type is "__main__.ClassC2[builtins.int]"
219289

290+
# k = ClassC2() # TODO
291+
# reveal_type(k) # Revealed type is "__main__.ClassC2[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO
292+
l = ClassC2[int]()
293+
# reveal_type(l) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO
294+
m = ClassC2[int, Unpack[Tuple[()]]]()
295+
reveal_type(m) # N: Revealed type is "__main__.ClassC2[builtins.int]"
296+
220297
class ClassC3(Generic[T3, Unpack[Ts4]]): ...
221298

222299
def func_c3(
@@ -228,6 +305,13 @@ def func_c3(
228305
reveal_type(b) # N: Revealed type is "__main__.ClassC3[builtins.int]"
229306
reveal_type(c) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]"
230307

308+
# k = ClassC3() # TODO
309+
# reveal_type(k) # Revealed type is "__main__.ClassC3[builtins.str]" # TODO
310+
l = ClassC3[int]()
311+
reveal_type(l) # N: Revealed type is "__main__.ClassC3[builtins.int]"
312+
m = ClassC3[int, Unpack[Tuple[float]]]()
313+
reveal_type(m) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]"
314+
231315
class ClassC4(Generic[T1, Unpack[Ts1], T3]): ...
232316

233317
def func_c4(
@@ -238,6 +322,13 @@ def func_c4(
238322
reveal_type(a) # N: Revealed type is "__main__.ClassC4[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]"
239323
# reveal_type(b) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO
240324
reveal_type(c) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]"
325+
326+
k = ClassC4() # E: Need type annotation for "k"
327+
reveal_type(k) # N: Revealed type is "__main__.ClassC4[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]"
328+
l = ClassC4[int]()
329+
# reveal_type(l) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO
330+
m = ClassC4[int, float]()
331+
reveal_type(m) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]"
241332
[builtins fixtures/tuple.pyi]
242333

243334
[case testTypeVarDefaultsTypeAlias1]

0 commit comments

Comments
 (0)