Skip to content

Commit

Permalink
is_singleton_type: move business logic to type definitions (#13165)
Browse files Browse the repository at this point in the history
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ó <[email protected]>
  • Loading branch information
vlaci and László Vaskó authored Jul 17, 2022
1 parent 6635b47 commit 35d9aef
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 17 deletions.
20 changes: 3 additions & 17 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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},
[])
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 35d9aef

Please sign in to comment.