diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9dfc0e2a6458..a2b1c4172781 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3242,7 +3242,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/mypy/semanal.py b/mypy/semanal.py index a9445a9c8748..d341d858919d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2883,11 +2883,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: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 111743e9235e..fa866c173b56 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -753,6 +753,29 @@ 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] [case testLiteralStringPep675] # flags: --python-version 3.11 diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index a3f44fff5e33..862a89be2b6d 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1603,3 +1603,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 "..."