Skip to content

Commit a06873f

Browse files
committed
Use TypeVar defaults to fix instances
1 parent 16cb493 commit a06873f

File tree

3 files changed

+104
-27
lines changed

3 files changed

+104
-27
lines changed

mypy/messages.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2816,12 +2816,15 @@ def for_function(callee: CallableType) -> str:
28162816
return ""
28172817

28182818

2819-
def wrong_type_arg_count(n: int, act: str, name: str) -> str:
2820-
s = f"{n} type arguments"
2821-
if n == 0:
2822-
s = "no type arguments"
2823-
elif n == 1:
2824-
s = "1 type argument"
2819+
def wrong_type_arg_count(low: int, high: int, act: str, name: str) -> str:
2820+
if low == high:
2821+
s = f"{low} type arguments"
2822+
if low == 0:
2823+
s = "no type arguments"
2824+
elif low == 1:
2825+
s = "1 type argument"
2826+
else:
2827+
s = f"between {low} and {high} type arguments"
28252828
if act == "0":
28262829
act = "none"
28272830
return f'"{name}" expects {s}, but {act} given'

mypy/typeanal.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from mypy import errorcodes as codes, message_registry, nodes
1111
from mypy.errorcodes import ErrorCode
12+
from mypy.expandtype import expand_type
1213
from mypy.messages import MessageBuilder, format_type_bare, quote_type_string, wrong_type_arg_count
1314
from mypy.nodes import (
1415
ARG_NAMED,
@@ -75,6 +76,7 @@
7576
TypeOfTypeList,
7677
TypeQuery,
7778
TypeType,
79+
TypeVarId,
7880
TypeVarLikeType,
7981
TypeVarTupleType,
8082
TypeVarType,
@@ -1669,27 +1671,39 @@ def fix_instance(
16691671
16701672
Also emit a suitable error if this is not due to implicit Any's.
16711673
"""
1672-
if len(t.args) == 0:
1673-
if use_generic_error:
1674-
fullname: str | None = None
1675-
else:
1676-
fullname = t.type.fullname
1677-
any_type = get_omitted_any(
1678-
disallow_any, fail, note, t, python_version, fullname, unexpanded_type
1674+
arg_count = len(t.args)
1675+
max_tv_count = len(t.type.type_vars)
1676+
args: list[Type] = [*(t.args[:max_tv_count])]
1677+
any_type: AnyType | None = None
1678+
env: dict[TypeVarId, Type] = {}
1679+
for tv, arg in itertools.zip_longest(t.type.defn.type_vars, t.args, fillvalue=None):
1680+
if tv is None:
1681+
continue
1682+
if arg is None:
1683+
if tv.has_default():
1684+
arg = tv.default
1685+
else:
1686+
if any_type is None:
1687+
fullname = None if use_generic_error else t.type.fullname
1688+
any_type = get_omitted_any(
1689+
disallow_any, fail, note, t, python_version, fullname, unexpanded_type
1690+
)
1691+
arg = any_type
1692+
args.append(arg)
1693+
env[tv.id] = arg
1694+
t.args = tuple(args)
1695+
fixed = expand_type(t, env)
1696+
assert isinstance(fixed, Instance)
1697+
t.args = fixed.args
1698+
1699+
min_tv_count = sum(tv.has_default() is False for tv in t.type.defn.type_vars)
1700+
if arg_count != 0 and not (min_tv_count <= arg_count <= max_tv_count):
1701+
fail(
1702+
wrong_type_arg_count(min_tv_count, max_tv_count, str(arg_count), t.type.name),
1703+
t,
1704+
code=codes.TYPE_ARG,
16791705
)
1680-
t.args = (any_type,) * len(t.type.type_vars)
1681-
return
1682-
# Invalid number of type parameters.
1683-
fail(
1684-
wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name),
1685-
t,
1686-
code=codes.TYPE_ARG,
1687-
)
1688-
# Construct the correct number of type arguments, as
1689-
# otherwise the type checker may crash as it expects
1690-
# things to be right.
1691-
t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars)
1692-
t.invalid = True
1706+
t.invalid = True
16931707

16941708

16951709
def expand_type_alias(
@@ -1746,7 +1760,7 @@ def expand_type_alias(
17461760
if use_standard_error:
17471761
# This is used if type alias is an internal representation of another type,
17481762
# for example a generic TypedDict or NamedTuple.
1749-
msg = wrong_type_arg_count(exp_len, str(act_len), node.name)
1763+
msg = wrong_type_arg_count(exp_len, exp_len, str(act_len), node.name)
17501764
else:
17511765
msg = f"Bad number of arguments for type alias, expected: {exp_len}, given: {act_len}"
17521766
fail(msg, ctx, code=codes.TYPE_ARG)

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,63 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]:
115115
# reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO
116116
# reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO
117117
[builtins fixtures/tuple.pyi]
118+
119+
[case testTypeVarDefaultsClass1]
120+
from typing import Generic, TypeVar
121+
122+
T1 = TypeVar("T1")
123+
T2 = TypeVar("T2", default=int)
124+
T3 = TypeVar("T3", default=str)
125+
126+
class ClassA1(Generic[T2, T3]): ...
127+
128+
def func_a1(
129+
a: ClassA1,
130+
b: ClassA1[float],
131+
c: ClassA1[float, float],
132+
) -> None:
133+
reveal_type(a) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]"
134+
reveal_type(b) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]"
135+
reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]"
136+
137+
class ClassA2(Generic[T1, T2, T3]): ...
138+
139+
def func_a2(
140+
a: ClassA2,
141+
b: ClassA2[float],
142+
c: ClassA2[float, float],
143+
d: ClassA2[float, float, float],
144+
) -> None:
145+
reveal_type(a) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]"
146+
reveal_type(b) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.int, builtins.str]"
147+
reveal_type(c) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.str]"
148+
reveal_type(d) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]"
149+
150+
[case testTypeVarDefaultsClass2]
151+
from typing import Generic, ParamSpec
152+
153+
P1 = ParamSpec("P1")
154+
P2 = ParamSpec("P2", default=(int, str))
155+
P3 = ParamSpec("P3", default=...)
156+
157+
class ClassB1(Generic[P2, P3]): ...
158+
159+
def func_b1(
160+
a: ClassB1,
161+
b: ClassB1[[float]],
162+
c: ClassB1[[float], [float]],
163+
) -> None:
164+
reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]"
165+
reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], ...]"
166+
reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]"
167+
168+
class ClassB2(Generic[P1, P2]): ...
169+
170+
def func_b2(
171+
a: ClassB2,
172+
b: ClassB2[[float]],
173+
c: ClassB2[[float], [float]],
174+
) -> None:
175+
reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
176+
reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]"
177+
reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]"

0 commit comments

Comments
 (0)