From 7311b74bb13d6f4649e6a8f76e4abad4f5d555aa Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 6 Feb 2022 10:43:53 +0300 Subject: [PATCH 1/2] Allow better `tuple` type aliases, fix "Type application" error --- mypy/checkexpr.py | 6 +++++- test-data/unit/check-type-aliases.test | 24 ++++++++++++++++++++++++ test-data/unit/pythoneval.test | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 36f92898edb7..6f6ad3475365 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3270,7 +3270,11 @@ def apply_type_arguments_to_callable( tp = get_proper_type(tp) if isinstance(tp, CallableType): - if len(tp.variables) != len(args): + if tp.is_type_obj() and tp.type_object().fullname == 'builtins.tuple': + # We special case `tuple` callable, because it can accept + # a different amount of args: `tuple[int]`, `tuple[int, str, str]` + return tp + elif len(tp.variables) != len(args): self.msg.incompatible_type_application(len(tp.variables), len(args), ctx) return AnyType(TypeOfAny.from_error) diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index cb023fc0ad3f..027bfad05525 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -728,3 +728,27 @@ from typing_extensions import TypeAlias A: TypeAlias = str [builtins fixtures/bool.pyi] [out] + +[case testTupleWithDifferentArgsPy38] +# flags: --python-version 3.8 +NotYet1 = tuple[float] # E: "tuple" is not subscriptable +NotYet2 = tuple[float, float] # E: "tuple" is not subscriptable +[builtins fixtures/tuple.pyi] + +[case testTupleWithDifferentArgsStub] +# https://github.com/python/mypy/issues/11098 +import tup +[file tup.pyi] +Correct1 = str | tuple[float, float, str] +Correct2 = tuple[float] | str +Correct3 = tuple[float, ...] | str + +RHSAlias1: type = tuple[int, int] +RHSAlias2: type = tuple[int] +RHSAlias3: type = tuple[int, ...] + +# Wrong: + +WrongTypeElement = str | tuple[float, 1] # E: Invalid type: try using Literal[1] instead? +WrongEllipsis = str | tuple[float, float, ...] # E: Unexpected "..." +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 993af4ced61e..75d634303513 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1586,3 +1586,21 @@ class Foo(Enum): _testEnumValueWithPlaceholderNodeType.py:5: error: Incompatible types in assignment (expression has type "object", variable has type "Foo") _testEnumValueWithPlaceholderNodeType.py:6: error: Incompatible types in assignment (expression has type "object", variable has type "Foo") _testEnumValueWithPlaceholderNodeType.py:6: error: Name "Missing" is not defined + +[case testTupleWithDifferentArgsPy310] +# https://github.com/python/mypy/issues/11098 +# flags: --python-version 3.10 +Correct1 = str | tuple[float, float, str] +Correct2 = tuple[float] | str +Correct3 = tuple[float, ...] | str + +RHSAlias1: type = tuple[int, int] +RHSAlias2: type = tuple[int] +RHSAlias3: type = tuple[int, ...] + +# Wrong: +WrongTypeElement = str | tuple[float, 1] +WrongEllipsis = tuple[float, float, ...] | str +[out] +_testTupleWithDifferentArgsPy310.py:12: error: Invalid type: try using Literal[1] instead? +_testTupleWithDifferentArgsPy310.py:13: error: Unexpected "..." From 70ecb89d78a1daea54e747f0db89a96d827cb9b9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 6 Feb 2022 20:20:23 +0300 Subject: [PATCH 2/2] Fix `Any` problem --- mypy/semanal.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 876866963e6b..ca5b878465e4 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2872,11 +2872,18 @@ def analyze_name_lvalue(self, lvalue.fullname = var._fullname else: lvalue.fullname = lvalue.name - if self.is_func_scope(): - if unmangle(name) == '_': - # Special case for assignment to local named '_': always infer 'Any'. - typ = AnyType(TypeOfAny.special_form) - self.store_declared_types(lvalue, typ) + + # Special case for assignment to local named '_': always infer 'Any'. + is_special_unused = self.is_func_scope() and unmangle(name) == '_' + + # Special case, `Any` is defined as `= object()` in typeshed, + # so we need to make it a real type variable. + is_typing_any = var.fullname == 'typing.Any' + + if is_special_unused or is_typing_any: + typ = AnyType(TypeOfAny.special_form) + self.store_declared_types(lvalue, typ) + if is_final and self.is_final_redefinition(kind, name): self.fail("Cannot redefine an existing name as final", lvalue) else: