From 35d9aef5b4a6bbbe1c67798505f2197386bc4649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Vask=C3=B3?= <1771332+vlaci@users.noreply.github.com> Date: Sun, 17 Jul 2022 12:30:27 +0100 Subject: [PATCH] `is_singleton_type`: move business logic to type definitions (#13165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This greatly reduces complexity as each type can determine for themselves if they are representing a singleton. `get_enum_values` has to be moved as well, as it is used by `get_singleton_type`. Relates-to: #13117 Co-authored-by: László Vaskó --- mypy/typeops.py | 20 +++----------------- mypy/types.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index ccb7ac55980c..b47c3c246fd2 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -719,11 +719,6 @@ def is_literal_type_like(t: Optional[Type]) -> bool: return False -def get_enum_values(typ: Instance) -> List[str]: - """Return the list of values for an Enum.""" - return [name for name, sym in typ.type.names.items() if isinstance(sym.node, Var)] - - def is_singleton_type(typ: Type) -> bool: """Returns 'true' if this type is a "singleton type" -- if there exists exactly only one runtime value associated with this type. @@ -741,16 +736,7 @@ def is_singleton_type(typ: Type) -> bool: constructing two distinct instances of 100001. """ typ = get_proper_type(typ) - # TODO: - # Also make this return True if the type corresponds to NotImplemented? - return ( - isinstance(typ, NoneType) - or (isinstance(typ, LiteralType) - and (typ.is_enum_literal() or isinstance(typ.value, bool))) - or (isinstance(typ, Instance) and ( - typ.type.is_enum and len(get_enum_values(typ)) == 1 - or typ.type.fullname == 'builtins.ellipsis')) - ) + return typ.is_singleton_type() def try_expanding_sum_type_to_union(typ: Type, target_fullname: str) -> ProperType: @@ -827,7 +813,7 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> List[ProperType] fullname = typ.fallback.type.fullname if typ.fallback.type.is_enum or isinstance(typ.value, bool): if fullname not in sum_types: - sum_types[fullname] = (set(get_enum_values(typ.fallback)) + sum_types[fullname] = (set(typ.fallback.get_enum_values()) if typ.fallback.type.is_enum else {True, False}, []) @@ -855,7 +841,7 @@ def coerce_to_literal(typ: Type) -> Type: if typ.last_known_value: return typ.last_known_value elif typ.type.is_enum: - enum_values = get_enum_values(typ) + enum_values = typ.get_enum_values() if len(enum_values) == 1: return LiteralType(value=enum_values[0], fallback=typ) return original_type diff --git a/mypy/types.py b/mypy/types.py index 4c595d9105a1..5abd9fcad03b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -240,6 +240,9 @@ def serialize(self) -> Union[JsonDict, str]: def deserialize(cls, data: JsonDict) -> 'Type': raise NotImplementedError(f'Cannot deserialize {cls.__name__} instance') + def is_singleton_type(self) -> bool: + return False + class TypeAliasType(Type): """A type alias to another type. @@ -1027,6 +1030,9 @@ def deserialize(cls, data: JsonDict) -> 'NoneType': assert data['.class'] == 'NoneType' return NoneType() + def is_singleton_type(self) -> bool: + return True + # NoneType used to be called NoneTyp so to avoid needlessly breaking # external plugins we keep that alias here. @@ -1231,6 +1237,21 @@ def copy_modified(self, *, def has_readable_member(self, name: str) -> bool: return self.type.has_readable_member(name) + def is_singleton_type(self) -> bool: + # TODO: + # Also make this return True if the type corresponds to NotImplemented? + return ( + self.type.is_enum and len(self.get_enum_values()) == 1 + or self.type.fullname == 'builtins.ellipsis' + ) + + def get_enum_values(self) -> List[str]: + """Return the list of values for an Enum.""" + return [ + name for name, sym in self.type.names.items() + if isinstance(sym.node, mypy.nodes.Var) + ] + class FunctionLike(ProperType): """Abstract base class for function types.""" @@ -2268,6 +2289,9 @@ def deserialize(cls, data: JsonDict) -> 'LiteralType': fallback=Instance.deserialize(data['fallback']), ) + def is_singleton_type(self) -> bool: + return self.is_enum_literal() or isinstance(self.value, bool) + class StarType(ProperType): """The star type *type_parameter.