Skip to content

Commit d88bb33

Browse files
committed
Use TypeVar defaults to fix instances
1 parent 838a73b commit d88bb33

File tree

3 files changed

+105
-26
lines changed

3 files changed

+105
-26
lines changed

mypy/messages.py

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

29122912

2913-
def wrong_type_arg_count(n: int, act: str, name: str) -> str:
2914-
s = f"{n} type arguments"
2915-
if n == 0:
2916-
s = "no type arguments"
2917-
elif n == 1:
2918-
s = "1 type argument"
2913+
def wrong_type_arg_count(low: int, high: int, act: str, name: str) -> str:
2914+
if low == high:
2915+
s = f"{low} type arguments"
2916+
if low == 0:
2917+
s = "no type arguments"
2918+
elif low == 1:
2919+
s = "1 type argument"
2920+
else:
2921+
s = f"between {low} and {high} type arguments"
29192922
if act == "0":
29202923
act = "none"
29212924
return f'"{name}" expects {s}, but {act} given'

mypy/typeanal.py

Lines changed: 36 additions & 20 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,
@@ -74,6 +75,7 @@
7475
TypeOfAny,
7576
TypeQuery,
7677
TypeType,
78+
TypeVarId,
7779
TypeVarLikeType,
7880
TypeVarTupleType,
7981
TypeVarType,
@@ -1676,25 +1678,39 @@ def fix_instance(
16761678
16771679
Also emit a suitable error if this is not due to implicit Any's.
16781680
"""
1679-
if len(t.args) == 0:
1680-
if use_generic_error:
1681-
fullname: str | None = None
1682-
else:
1683-
fullname = t.type.fullname
1684-
any_type = get_omitted_any(disallow_any, fail, note, t, options, fullname, unexpanded_type)
1685-
t.args = (any_type,) * len(t.type.type_vars)
1686-
return
1687-
# Invalid number of type parameters.
1688-
fail(
1689-
wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name),
1690-
t,
1691-
code=codes.TYPE_ARG,
1692-
)
1693-
# Construct the correct number of type arguments, as
1694-
# otherwise the type checker may crash as it expects
1695-
# things to be right.
1696-
t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars)
1697-
t.invalid = True
1681+
arg_count = len(t.args)
1682+
max_tv_count = len(t.type.type_vars)
1683+
args: list[Type] = [*(t.args[:max_tv_count])]
1684+
any_type: AnyType | None = None
1685+
env: dict[TypeVarId, Type] = {}
1686+
for tv, arg in itertools.zip_longest(t.type.defn.type_vars, t.args, fillvalue=None):
1687+
if tv is None:
1688+
continue
1689+
if arg is None:
1690+
if tv.has_default():
1691+
arg = tv.default
1692+
else:
1693+
if any_type is None:
1694+
fullname = None if use_generic_error else t.type.fullname
1695+
any_type = get_omitted_any(
1696+
disallow_any, fail, note, t, options, fullname, unexpanded_type
1697+
)
1698+
arg = any_type
1699+
args.append(arg)
1700+
env[tv.id] = arg
1701+
t.args = tuple(args)
1702+
fixed = expand_type(t, env)
1703+
assert isinstance(fixed, Instance)
1704+
t.args = fixed.args
1705+
1706+
min_tv_count = sum(tv.has_default() is False for tv in t.type.defn.type_vars)
1707+
if arg_count != 0 and not (min_tv_count <= arg_count <= max_tv_count):
1708+
fail(
1709+
wrong_type_arg_count(min_tv_count, max_tv_count, str(arg_count), t.type.name),
1710+
t,
1711+
code=codes.TYPE_ARG,
1712+
)
1713+
t.invalid = True
16981714

16991715

17001716
def expand_type_alias(
@@ -1753,7 +1769,7 @@ def expand_type_alias(
17531769
if use_standard_error:
17541770
# This is used if type alias is an internal representation of another type,
17551771
# for example a generic TypedDict or NamedTuple.
1756-
msg = wrong_type_arg_count(exp_len, str(act_len), node.name)
1772+
msg = wrong_type_arg_count(exp_len, exp_len, str(act_len), node.name)
17571773
else:
17581774
msg = f"Bad number of arguments for type alias, expected: {exp_len}, given: {act_len}"
17591775
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
@@ -116,3 +116,63 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]:
116116
# reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO
117117
# reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO
118118
[builtins fixtures/tuple.pyi]
119+
120+
[case testTypeVarDefaultsClass1]
121+
from typing import Generic, TypeVar
122+
123+
T1 = TypeVar("T1")
124+
T2 = TypeVar("T2", default=int)
125+
T3 = TypeVar("T3", default=str)
126+
127+
class ClassA1(Generic[T2, T3]): ...
128+
129+
def func_a1(
130+
a: ClassA1,
131+
b: ClassA1[float],
132+
c: ClassA1[float, float],
133+
) -> None:
134+
reveal_type(a) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]"
135+
reveal_type(b) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]"
136+
reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]"
137+
138+
class ClassA2(Generic[T1, T2, T3]): ...
139+
140+
def func_a2(
141+
a: ClassA2,
142+
b: ClassA2[float],
143+
c: ClassA2[float, float],
144+
d: ClassA2[float, float, float],
145+
) -> None:
146+
reveal_type(a) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]"
147+
reveal_type(b) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.int, builtins.str]"
148+
reveal_type(c) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.str]"
149+
reveal_type(d) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]"
150+
151+
[case testTypeVarDefaultsClass2]
152+
from typing import Generic, ParamSpec
153+
154+
P1 = ParamSpec("P1")
155+
P2 = ParamSpec("P2", default=(int, str))
156+
P3 = ParamSpec("P3", default=...)
157+
158+
class ClassB1(Generic[P2, P3]): ...
159+
160+
def func_b1(
161+
a: ClassB1,
162+
b: ClassB1[[float]],
163+
c: ClassB1[[float], [float]],
164+
) -> None:
165+
reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]"
166+
reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], ...]"
167+
reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]"
168+
169+
class ClassB2(Generic[P1, P2]): ...
170+
171+
def func_b2(
172+
a: ClassB2,
173+
b: ClassB2[[float]],
174+
c: ClassB2[[float], [float]],
175+
) -> None:
176+
reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]"
177+
reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]"
178+
reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]"

0 commit comments

Comments
 (0)