Skip to content

Commit 09490c8

Browse files
authored
Use TypeVar defaults instead of Any when fixing TypeAlias types (PEP 696) (#16825)
This PR applies the TypeVar defaults to `TypeAlias` types instead of using `Any` exclusively, similar to #16812. Again `TypeVarTuple` defaults aren't handled correctly yet. Ref: #14851
1 parent 717a263 commit 09490c8

File tree

3 files changed

+232
-34
lines changed

3 files changed

+232
-34
lines changed

mypy/checkexpr.py

+1
Original file line numberDiff line numberDiff line change
@@ -4711,6 +4711,7 @@ class LongName(Generic[T]): ...
47114711
item = get_proper_type(
47124712
set_any_tvars(
47134713
alias,
4714+
[],
47144715
ctx.line,
47154716
ctx.column,
47164717
self.chk.options,

mypy/typeanal.py

+70-34
Original file line numberDiff line numberDiff line change
@@ -1927,34 +1927,35 @@ def instantiate_type_alias(
19271927
if any(unknown_unpack(a) for a in args):
19281928
# This type is not ready to be validated, because of unknown total count.
19291929
# Note that we keep the kind of Any for consistency.
1930-
return set_any_tvars(node, ctx.line, ctx.column, options, special_form=True)
1930+
return set_any_tvars(node, [], ctx.line, ctx.column, options, special_form=True)
19311931

1932-
exp_len = len(node.alias_tvars)
1932+
max_tv_count = len(node.alias_tvars)
19331933
act_len = len(args)
19341934
if (
1935-
exp_len > 0
1935+
max_tv_count > 0
19361936
and act_len == 0
19371937
and not (empty_tuple_index and node.tvar_tuple_index is not None)
19381938
):
19391939
# Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...]
19401940
return set_any_tvars(
19411941
node,
1942+
args,
19421943
ctx.line,
19431944
ctx.column,
19441945
options,
19451946
disallow_any=disallow_any,
19461947
fail=fail,
19471948
unexpanded_type=unexpanded_type,
19481949
)
1949-
if exp_len == 0 and act_len == 0:
1950+
if max_tv_count == 0 and act_len == 0:
19501951
if no_args:
19511952
assert isinstance(node.target, Instance) # type: ignore[misc]
19521953
# Note: this is the only case where we use an eager expansion. See more info about
19531954
# no_args aliases like L = List in the docstring for TypeAlias class.
19541955
return Instance(node.target.type, [], line=ctx.line, column=ctx.column)
19551956
return TypeAliasType(node, [], line=ctx.line, column=ctx.column)
19561957
if (
1957-
exp_len == 0
1958+
max_tv_count == 0
19581959
and act_len > 0
19591960
and isinstance(node.target, Instance) # type: ignore[misc]
19601961
and no_args
@@ -1967,32 +1968,48 @@ def instantiate_type_alias(
19671968
if any(isinstance(a, UnpackType) for a in args):
19681969
# A variadic unpack in fixed size alias (fixed unpacks must be flattened by the caller)
19691970
fail(message_registry.INVALID_UNPACK_POSITION, ctx, code=codes.VALID_TYPE)
1970-
return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True)
1971-
correct = act_len == exp_len
1971+
return set_any_tvars(node, [], ctx.line, ctx.column, options, from_error=True)
1972+
min_tv_count = sum(not tv.has_default() for tv in node.alias_tvars)
1973+
fill_typevars = act_len != max_tv_count
1974+
correct = min_tv_count <= act_len <= max_tv_count
19721975
else:
1973-
correct = act_len >= exp_len - 1
1976+
min_tv_count = sum(
1977+
not tv.has_default() and not isinstance(tv, TypeVarTupleType)
1978+
for tv in node.alias_tvars
1979+
)
1980+
correct = act_len >= min_tv_count
19741981
for a in args:
19751982
if isinstance(a, UnpackType):
19761983
unpacked = get_proper_type(a.type)
19771984
if isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple":
19781985
# Variadic tuple is always correct.
19791986
correct = True
1980-
if not correct:
1981-
if use_standard_error:
1982-
# This is used if type alias is an internal representation of another type,
1983-
# for example a generic TypedDict or NamedTuple.
1984-
msg = wrong_type_arg_count(exp_len, exp_len, str(act_len), node.name)
1985-
else:
1986-
if node.tvar_tuple_index is not None:
1987-
exp_len_str = f"at least {exp_len - 1}"
1987+
fill_typevars = not correct
1988+
if fill_typevars:
1989+
if not correct:
1990+
if use_standard_error:
1991+
# This is used if type alias is an internal representation of another type,
1992+
# for example a generic TypedDict or NamedTuple.
1993+
msg = wrong_type_arg_count(max_tv_count, max_tv_count, str(act_len), node.name)
19881994
else:
1989-
exp_len_str = str(exp_len)
1990-
msg = (
1991-
"Bad number of arguments for type alias,"
1992-
f" expected: {exp_len_str}, given: {act_len}"
1993-
)
1994-
fail(msg, ctx, code=codes.TYPE_ARG)
1995-
return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True)
1995+
if node.tvar_tuple_index is not None:
1996+
msg = (
1997+
"Bad number of arguments for type alias,"
1998+
f" expected: at least {min_tv_count}, given: {act_len}"
1999+
)
2000+
elif min_tv_count != max_tv_count:
2001+
msg = (
2002+
"Bad number of arguments for type alias,"
2003+
f" expected between {min_tv_count} and {max_tv_count}, given: {act_len}"
2004+
)
2005+
else:
2006+
msg = (
2007+
"Bad number of arguments for type alias,"
2008+
f" expected: {min_tv_count}, given: {act_len}"
2009+
)
2010+
fail(msg, ctx, code=codes.TYPE_ARG)
2011+
args = []
2012+
return set_any_tvars(node, args, ctx.line, ctx.column, options, from_error=True)
19962013
elif node.tvar_tuple_index is not None:
19972014
# We also need to check if we are not performing a type variable tuple split.
19982015
unpack = find_unpack_in_list(args)
@@ -2006,7 +2023,7 @@ def instantiate_type_alias(
20062023
act_suffix = len(args) - unpack - 1
20072024
if act_prefix < exp_prefix or act_suffix < exp_suffix:
20082025
fail("TypeVarTuple cannot be split", ctx, code=codes.TYPE_ARG)
2009-
return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True)
2026+
return set_any_tvars(node, [], ctx.line, ctx.column, options, from_error=True)
20102027
# TODO: we need to check args validity w.r.t alias.alias_tvars.
20112028
# Otherwise invalid instantiations will be allowed in runtime context.
20122029
# Note: in type context, these will be still caught by semanal_typeargs.
@@ -2025,6 +2042,7 @@ def instantiate_type_alias(
20252042

20262043
def set_any_tvars(
20272044
node: TypeAlias,
2045+
args: list[Type],
20282046
newline: int,
20292047
newcolumn: int,
20302048
options: Options,
@@ -2041,7 +2059,33 @@ def set_any_tvars(
20412059
type_of_any = TypeOfAny.special_form
20422060
else:
20432061
type_of_any = TypeOfAny.from_omitted_generics
2044-
if disallow_any and node.alias_tvars:
2062+
any_type = AnyType(type_of_any, line=newline, column=newcolumn)
2063+
2064+
env: dict[TypeVarId, Type] = {}
2065+
used_any_type = False
2066+
has_type_var_tuple_type = False
2067+
for tv, arg in itertools.zip_longest(node.alias_tvars, args, fillvalue=None):
2068+
if tv is None:
2069+
continue
2070+
if arg is None:
2071+
if tv.has_default():
2072+
arg = tv.default
2073+
else:
2074+
arg = any_type
2075+
used_any_type = True
2076+
if isinstance(tv, TypeVarTupleType):
2077+
# TODO Handle TypeVarTuple defaults
2078+
has_type_var_tuple_type = True
2079+
arg = UnpackType(Instance(tv.tuple_fallback.type, [any_type]))
2080+
args.append(arg)
2081+
env[tv.id] = arg
2082+
t = TypeAliasType(node, args, newline, newcolumn)
2083+
if not has_type_var_tuple_type:
2084+
fixed = expand_type(t, env)
2085+
assert isinstance(fixed, TypeAliasType)
2086+
t.args = fixed.args
2087+
2088+
if used_any_type and disallow_any and node.alias_tvars:
20452089
assert fail is not None
20462090
if unexpanded_type:
20472091
type_str = (
@@ -2057,15 +2101,7 @@ def set_any_tvars(
20572101
Context(newline, newcolumn),
20582102
code=codes.TYPE_ARG,
20592103
)
2060-
any_type = AnyType(type_of_any, line=newline, column=newcolumn)
2061-
2062-
args: list[Type] = []
2063-
for tv in node.alias_tvars:
2064-
if isinstance(tv, TypeVarTupleType):
2065-
args.append(UnpackType(Instance(tv.tuple_fallback.type, [any_type])))
2066-
else:
2067-
args.append(any_type)
2068-
return TypeAliasType(node, args, newline, newcolumn)
2104+
return t
20692105

20702106

20712107
def flatten_tvars(lists: list[list[T]]) -> list[T]:

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

+161
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,164 @@ def func_c4(
239239
# reveal_type(b) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO
240240
reveal_type(c) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]"
241241
[builtins fixtures/tuple.pyi]
242+
243+
[case testTypeVarDefaultsTypeAlias1]
244+
# flags: --disallow-any-generics
245+
from typing import Any, Dict, List, Tuple, TypeVar, Union
246+
247+
T1 = TypeVar("T1")
248+
T2 = TypeVar("T2", default=int)
249+
T3 = TypeVar("T3", default=str)
250+
T4 = TypeVar("T4")
251+
252+
TA1 = Dict[T2, T3]
253+
254+
def func_a1(
255+
a: TA1,
256+
b: TA1[float],
257+
c: TA1[float, float],
258+
d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3
259+
) -> None:
260+
reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
261+
reveal_type(b) # N: Revealed type is "builtins.dict[builtins.float, builtins.str]"
262+
reveal_type(c) # N: Revealed type is "builtins.dict[builtins.float, builtins.float]"
263+
reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
264+
265+
TA2 = Tuple[T1, T2, T3]
266+
267+
def func_a2(
268+
a: TA2, # E: Missing type parameters for generic type "TA2"
269+
b: TA2[float],
270+
c: TA2[float, float],
271+
d: TA2[float, float, float],
272+
e: TA2[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4
273+
) -> None:
274+
reveal_type(a) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]"
275+
reveal_type(b) # N: Revealed type is "Tuple[builtins.float, builtins.int, builtins.str]"
276+
reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.str]"
277+
reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]"
278+
reveal_type(e) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]"
279+
280+
TA3 = Union[Dict[T1, T2], List[T3]]
281+
282+
def func_a3(
283+
a: TA3, # E: Missing type parameters for generic type "TA3"
284+
b: TA3[float],
285+
c: TA3[float, float],
286+
d: TA3[float, float, float],
287+
e: TA3[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4
288+
) -> None:
289+
reveal_type(a) # N: Revealed type is "Union[builtins.dict[Any, builtins.int], builtins.list[builtins.str]]"
290+
reveal_type(b) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.int], builtins.list[builtins.str]]"
291+
reveal_type(c) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.float], builtins.list[builtins.str]]"
292+
reveal_type(d) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.float], builtins.list[builtins.float]]"
293+
reveal_type(e) # N: Revealed type is "Union[builtins.dict[Any, builtins.int], builtins.list[builtins.str]]"
294+
295+
TA4 = Tuple[T1, T4, T2]
296+
297+
def func_a4(
298+
a: TA4, # E: Missing type parameters for generic type "TA4"
299+
b: TA4[float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 1
300+
c: TA4[float, float],
301+
d: TA4[float, float, float],
302+
e: TA4[float, float, float, float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 4
303+
) -> None:
304+
reveal_type(a) # N: Revealed type is "Tuple[Any, Any, builtins.int]"
305+
reveal_type(b) # N: Revealed type is "Tuple[Any, Any, builtins.int]"
306+
reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.int]"
307+
reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]"
308+
reveal_type(e) # N: Revealed type is "Tuple[Any, Any, builtins.int]"
309+
[builtins fixtures/dict.pyi]
310+
311+
[case testTypeVarDefaultsTypeAlias2]
312+
# flags: --disallow-any-generics
313+
from typing import Any, Generic, ParamSpec
314+
315+
P1 = ParamSpec("P1")
316+
P2 = ParamSpec("P2", default=[int, str])
317+
P3 = ParamSpec("P3", default=...)
318+
319+
class ClassB1(Generic[P2, P3]): ...
320+
TB1 = ClassB1[P2, P3]
321+
322+
def func_b1(
323+
a: TB1,
324+
b: TB1[[float]],
325+
c: TB1[[float], [float]],
326+
d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3
327+
) -> None:
328+
reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]"
329+
reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]"
330+
reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]"
331+
reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]"
332+
333+
class ClassB2(Generic[P1, P2]): ...
334+
TB2 = ClassB2[P1, P2]
335+
336+
def func_b2(
337+
a: TB2, # E: Missing type parameters for generic type "TB2"
338+
b: TB2[[float]],
339+
c: TB2[[float], [float]],
340+
d: TB2[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 1 and 2, given: 3
341+
) -> None:
342+
reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
343+
reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]"
344+
reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]"
345+
reveal_type(d) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
346+
[builtins fixtures/tuple.pyi]
347+
348+
[case testTypeVarDefaultsTypeAlias3]
349+
# flags: --disallow-any-generics
350+
from typing import Tuple, TypeVar
351+
from typing_extensions import TypeVarTuple, Unpack
352+
353+
T1 = TypeVar("T1")
354+
T3 = TypeVar("T3", default=str)
355+
356+
Ts1 = TypeVarTuple("Ts1")
357+
Ts2 = TypeVarTuple("Ts2", default=Unpack[Tuple[int, str]])
358+
Ts3 = TypeVarTuple("Ts3", default=Unpack[Tuple[float, ...]])
359+
Ts4 = TypeVarTuple("Ts4", default=Unpack[Tuple[()]])
360+
361+
TC1 = Tuple[Unpack[Ts2]]
362+
363+
def func_c1(
364+
a: TC1,
365+
b: TC1[float],
366+
) -> None:
367+
# reveal_type(a) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO
368+
reveal_type(b) # N: Revealed type is "Tuple[builtins.float]"
369+
370+
TC2 = Tuple[T3, Unpack[Ts3]]
371+
372+
def func_c2(
373+
a: TC2,
374+
b: TC2[int],
375+
c: TC2[int, Unpack[Tuple[()]]],
376+
) -> None:
377+
# reveal_type(a) # Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO
378+
# reveal_type(b) # Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO
379+
reveal_type(c) # N: Revealed type is "Tuple[builtins.int]"
380+
381+
TC3 = Tuple[T3, Unpack[Ts4]]
382+
383+
def func_c3(
384+
a: TC3,
385+
b: TC3[int],
386+
c: TC3[int, Unpack[Tuple[float]]],
387+
) -> None:
388+
# reveal_type(a) # Revealed type is "Tuple[builtins.str]" # TODO
389+
reveal_type(b) # N: Revealed type is "Tuple[builtins.int]"
390+
reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]"
391+
392+
TC4 = Tuple[T1, Unpack[Ts1], T3]
393+
394+
def func_c4(
395+
a: TC4, # E: Missing type parameters for generic type "TC4"
396+
b: TC4[int],
397+
c: TC4[int, float],
398+
) -> None:
399+
reveal_type(a) # N: Revealed type is "Tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]"
400+
# reveal_type(b) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO
401+
reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]"
402+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)