From 15812b9405dd063ebe42c68c359062c9e5737f98 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 2 Jul 2021 16:35:34 +0100 Subject: [PATCH 1/4] Allow tuple[int, ...] as type alias and in type application --- mypy/semanal.py | 8 ++++++-- test-data/unit/check-generic-alias.test | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index ae3d176aa154..248a6ed667a1 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -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 @@ -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 @@ -3883,6 +3884,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 items and isinstance(items[-1], EllipsisExpr): + items = items[:-1] else: items = [index] for item in items: diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 5cfe77b9c0fc..4c2a106b5543 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -239,3 +239,18 @@ 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] From 4f0ae0498df2ad33b30a2d58ca3f1d69dcfe8341 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 2 Jul 2021 16:55:36 +0100 Subject: [PATCH 2/4] Also fix stubs --- mypy/semanal.py | 3 ++- mypy/typeanal.py | 3 ++- test-data/unit/check-generic-alias.test | 28 ++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 248a6ed667a1..b96cf740d453 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3868,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]]: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index bcec18bd925f..503a9bb99cf5 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -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') diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 4c2a106b5543..0d2669b89820 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -252,5 +252,31 @@ 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*]" +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 +[builtins fixtures/list.pyi] From 31971868262cbaef1116f6ffe88c8946ce7f1aef Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 5 Jul 2021 13:50:02 +0100 Subject: [PATCH 3/4] Minor tweak --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index b96cf740d453..96ef7401bf42 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3886,7 +3886,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> Optional[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 items and isinstance(items[-1], EllipsisExpr): + if is_tuple and len(items) == 2 and isinstance(items[-1], EllipsisExpr): items = items[:-1] else: items = [index] From 7172206632a66984b0c468beda3ef11b6588c31a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 5 Jul 2021 14:03:11 +0100 Subject: [PATCH 4/4] Update test case --- test-data/unit/check-generic-alias.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 0d2669b89820..b6060f3922fe 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -279,4 +279,6 @@ A = list[int] a: A B = list[list[int]] b: B +class C(list[int]): + pass [builtins fixtures/list.pyi]