diff --git a/mypy/constraints.py b/mypy/constraints.py index 5a78cdb94e93..9bc233054e91 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -8,7 +8,8 @@ TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, TypeVarId, TypeQuery, is_named_instance, TypeOfAny, LiteralType, ProperType, ParamSpecType, get_proper_type, TypeAliasType, is_union_with_any, - callable_with_ellipsis + callable_with_ellipsis, + TUPLE_LIKE_INSTANCE_NAMES, ) from mypy.maptype import map_instance_to_supertype import mypy.subtypes @@ -501,11 +502,8 @@ def visit_instance(self, template: Instance) -> List[Constraint]: return res if isinstance(actual, AnyType): return self.infer_against_any(template.args, actual) - if (isinstance(actual, TupleType) and - (is_named_instance(template, 'typing.Iterable') or - is_named_instance(template, 'typing.Container') or - is_named_instance(template, 'typing.Sequence') or - is_named_instance(template, 'typing.Reversible')) + if (isinstance(actual, TupleType) + and is_named_instance(template, TUPLE_LIKE_INSTANCE_NAMES) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) diff --git a/mypy/semanal.py b/mypy/semanal.py index 4938b96c8bca..b25ad1542a04 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -94,6 +94,7 @@ TypeTranslator, TypeOfAny, TypeType, NoneType, PlaceholderType, TPDICT_NAMES, ProperType, get_proper_type, get_proper_types, TypeAliasType, TypeVarLikeType, PROTOCOL_NAMES, TYPE_ALIAS_NAMES, FINAL_TYPE_NAMES, FINAL_DECORATOR_NAMES, + is_named_instance, ) from mypy.typeops import function_type, get_type_vars from mypy.type_visitor import TypeQuery @@ -1038,8 +1039,7 @@ def visit_decorator(self, dec: Decorator) -> None: removed.append(i) dec.func.is_abstract = True self.check_decorated_function_is_method('abstractmethod', dec) - elif (refers_to_fullname(d, 'asyncio.coroutines.coroutine') or - refers_to_fullname(d, 'types.coroutine')): + elif refers_to_fullname(d, ('asyncio.coroutines.coroutine', 'types.coroutine')): removed.append(i) dec.func.is_awaitable_coroutine = True elif refers_to_fullname(d, 'builtins.staticmethod'): @@ -1052,9 +1052,10 @@ def visit_decorator(self, dec: Decorator) -> None: dec.func.is_class = True dec.var.is_classmethod = True self.check_decorated_function_is_method('classmethod', dec) - elif (refers_to_fullname(d, 'builtins.property') or - refers_to_fullname(d, 'abc.abstractproperty') or - refers_to_fullname(d, 'functools.cached_property')): + elif refers_to_fullname(d, ( + 'builtins.property', + 'abc.abstractproperty', + 'functools.cached_property')): removed.append(i) dec.func.is_property = True dec.var.is_property = True @@ -1068,8 +1069,7 @@ def visit_decorator(self, dec: Decorator) -> None: elif refers_to_fullname(d, 'typing.no_type_check'): dec.var.type = AnyType(TypeOfAny.special_form) no_type_check = True - elif (refers_to_fullname(d, 'typing.final') or - refers_to_fullname(d, 'typing_extensions.final')): + elif refers_to_fullname(d, FINAL_DECORATOR_NAMES): if self.is_class_scope(): assert self.type is not None, "No type set at class scope" if self.type.is_protocol: @@ -5315,16 +5315,17 @@ def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike: assert False -def refers_to_fullname(node: Expression, fullname: str) -> bool: +def refers_to_fullname(node: Expression, fullnames: Union[str, Tuple[str, ...]]) -> bool: """Is node a name or member expression with the given full name?""" + if not isinstance(fullnames, tuple): + fullnames = (fullnames,) + if not isinstance(node, RefExpr): return False - if node.fullname == fullname: + if node.fullname in fullnames: return True if isinstance(node.node, TypeAlias): - target = get_proper_type(node.node.target) - if isinstance(target, Instance) and target.type.fullname == fullname: - return True + return is_named_instance(node.node.target, fullnames) return False diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 200106000132..271e950de659 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -7,7 +7,8 @@ Type, AnyType, UnboundType, TypeVisitor, FormalArgument, NoneType, Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, is_named_instance, - FunctionLike, TypeOfAny, LiteralType, get_proper_type, TypeAliasType, ParamSpecType + FunctionLike, TypeOfAny, LiteralType, get_proper_type, TypeAliasType, ParamSpecType, + TUPLE_LIKE_INSTANCE_NAMES, ) import mypy.applytype import mypy.constraints @@ -362,11 +363,7 @@ def visit_tuple_type(self, left: TupleType) -> bool: if isinstance(right, Instance): if is_named_instance(right, 'typing.Sized'): return True - elif (is_named_instance(right, 'builtins.tuple') or - is_named_instance(right, 'typing.Iterable') or - is_named_instance(right, 'typing.Container') or - is_named_instance(right, 'typing.Sequence') or - is_named_instance(right, 'typing.Reversible')): + elif is_named_instance(right, TUPLE_LIKE_INSTANCE_NAMES): if right.args: iter_type = right.args[0] else: @@ -1377,11 +1374,7 @@ def visit_callable_type(self, left: CallableType) -> bool: def visit_tuple_type(self, left: TupleType) -> bool: right = self.right if isinstance(right, Instance): - if (is_named_instance(right, 'builtins.tuple') or - is_named_instance(right, 'typing.Iterable') or - is_named_instance(right, 'typing.Container') or - is_named_instance(right, 'typing.Sequence') or - is_named_instance(right, 'typing.Reversible')): + if is_named_instance(right, TUPLE_LIKE_INSTANCE_NAMES): if not right.args: return False iter_type = get_proper_type(right.args[0]) diff --git a/mypy/types.py b/mypy/types.py index f055d134d582..d3987897995e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -117,6 +117,15 @@ 'typing_extensions.Annotated', ) +# We use this constant in various places when checking `tuple` subtyping: +TUPLE_LIKE_INSTANCE_NAMES: Final = ( + 'builtins.tuple', + 'typing.Iterable', + 'typing.Container', + 'typing.Sequence', + 'typing.Reversible', +) + # A placeholder used for Bogus[...] parameters _dummy: Final[Any] = object() @@ -2482,9 +2491,12 @@ def strip_type(typ: Type) -> ProperType: return typ -def is_named_instance(t: Type, fullname: str) -> bool: +def is_named_instance(t: Type, fullnames: Union[str, Tuple[str, ...]]) -> bool: + if not isinstance(fullnames, tuple): + fullnames = (fullnames,) + t = get_proper_type(t) - return isinstance(t, Instance) and t.type.fullname == fullname + return isinstance(t, Instance) and t.type.fullname in fullnames TP = TypeVar('TP', bound=Type)