Skip to content

is_singleton_type: move business logic to type definitions #13165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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