Skip to content

Commit

Permalink
Fixes to generic builtin types (#10766)
Browse files Browse the repository at this point in the history
This fixes these issues:

* Allow variable-length tuple (tuple[int, ...]) in type aliases
* Allow generic built-in types (e.g. list[int]) in stubs in all Python versions

Fixes #9980. Fixes #10303. Fixes #10731.
  • Loading branch information
JukkaL committed Jul 5, 2021
1 parent a5a9e15 commit 6eafc5e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 4 deletions.
11 changes: 8 additions & 3 deletions mypy/semanal.py
Expand Up @@ -76,7 +76,7 @@
get_nongen_builtins, get_member_expr_fullname, REVEAL_TYPE,
REVEAL_LOCALS, is_final_node, TypedDictExpr, type_aliases_source_versions,
EnumCallExpr, RUNTIME_PROTOCOL_DECOS, FakeExpression, Statement, AssignmentExpr,
ParamSpecExpr
ParamSpecExpr, EllipsisExpr
)
from mypy.tvar_scope import TypeVarLikeScope
from mypy.typevars import fill_typevars
Expand All @@ -91,7 +91,8 @@
FunctionLike, UnboundType, TypeVarDef, TupleType, UnionType, StarType,
CallableType, Overloaded, Instance, Type, AnyType, LiteralType, LiteralValue,
TypeTranslator, TypeOfAny, TypeType, NoneType, PlaceholderType, TPDICT_NAMES, ProperType,
get_proper_type, get_proper_types, TypeAliasType)
get_proper_type, get_proper_types, TypeAliasType
)
from mypy.typeops import function_type
from mypy.type_visitor import TypeQuery
from mypy.nodes import implicit_module_attrs
Expand Down Expand Up @@ -3867,7 +3868,8 @@ def analyze_type_application(self, expr: IndexExpr) -> None:
# ...or directly.
else:
n = self.lookup_type_node(base)
if n and n.fullname in get_nongen_builtins(self.options.python_version):
if (n and n.fullname in get_nongen_builtins(self.options.python_version) and
not self.is_stub_file):
self.fail(no_subscript_builtin_alias(n.fullname, propose_alt=False), expr)

def analyze_type_application_args(self, expr: IndexExpr) -> Optional[List[Type]]:
Expand All @@ -3883,6 +3885,9 @@ def analyze_type_application_args(self, expr: IndexExpr) -> Optional[List[Type]]
types: List[Type] = []
if isinstance(index, TupleExpr):
items = index.items
is_tuple = isinstance(expr.base, RefExpr) and expr.base.fullname == 'builtins.tuple'
if is_tuple and len(items) == 2 and isinstance(items[-1], EllipsisExpr):
items = items[:-1]
else:
items = [index]
for item in items:
Expand Down
3 changes: 2 additions & 1 deletion mypy/typeanal.py
Expand Up @@ -281,7 +281,8 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt
return AnyType(TypeOfAny.from_error)
elif (fullname == 'typing.Tuple' or
(fullname == 'builtins.tuple' and (self.options.python_version >= (3, 9) or
self.api.is_future_flag_set('annotations')))):
self.api.is_future_flag_set('annotations') or
self.allow_unnormalized))):
# Tuple is special because it is involved in builtin import cycle
# and may be not ready when used.
sym = self.api.lookup_fully_qualified_or_none('builtins.tuple')
Expand Down
43 changes: 43 additions & 0 deletions test-data/unit/check-generic-alias.test
Expand Up @@ -239,3 +239,46 @@ t09: tuple[int, ...] = (1, 2, 3)
from typing import Tuple
t10: Tuple[int, ...] = t09
[builtins fixtures/tuple.pyi]

[case testTypeAliasWithBuiltinTuple]
# flags: --python-version 3.9

A = tuple[int, ...]
a: A = ()
b: A = (1, 2, 3)
c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[int, ...]")

B = tuple[int, str]
x: B = (1, 'x')
y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "Tuple[int, str]")

reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.int*]"
[builtins fixtures/tuple.pyi]

[case testTypeAliasWithBuiltinTupleInStub]
# flags: --python-version 3.6
import m
reveal_type(m.a) # N: Revealed type is "builtins.tuple[builtins.int]"
reveal_type(m.b) # N: Revealed type is "Tuple[builtins.int, builtins.str]"

[file m.pyi]
A = tuple[int, ...]
a: A
B = tuple[int, str]
b: B
[builtins fixtures/tuple.pyi]

[case testTypeAliasWithBuiltinListInStub]
# flags: --python-version 3.6
import m
reveal_type(m.a) # N: Revealed type is "builtins.list[builtins.int]"
reveal_type(m.b) # N: Revealed type is "builtins.list[builtins.list[builtins.int]]"

[file m.pyi]
A = list[int]
a: A
B = list[list[int]]
b: B
class C(list[int]):
pass
[builtins fixtures/list.pyi]

0 comments on commit 6eafc5e

Please sign in to comment.