Skip to content
Open
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
11 changes: 10 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6567,6 +6567,7 @@ def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMa
and not (
isinstance(p_expr := get_proper_type(expr_type), CallableType)
and p_expr.is_type_obj()
and not p_expr.type_object().is_final
)
):
h = literal_hash(expr)
Expand Down Expand Up @@ -6803,7 +6804,15 @@ def narrow_type_by_equality(
operands[i], *conditional_types(expr_type, [target])
)
if if_map:
else_map = {} # this is the big difference compared to the above
# For final classes, we can narrow in the else branch too since
# no subclasses can exist. Otherwise, clear the else_map.
target_type = get_proper_type(target.item)
if not (
isinstance(target_type, CallableType)
and target_type.is_type_obj()
and target_type.type_object().is_final
):
else_map = {}
partial_type_maps.append((if_map, else_map))

# We will not have duplicate entries in our type maps if we only have two operands,
Expand Down
10 changes: 10 additions & 0 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,16 @@ def is_singleton_identity_type(typ: ProperType) -> bool:
)
if isinstance(typ, LiteralType):
return typ.is_enum_literal() or isinstance(typ.value, bool)
# Check if this is a type object of a final class
if isinstance(typ, TypeType):
item = typ.item
if isinstance(item, Instance) and item.type.is_final:
return True
# Check if this is a callable representing a final class constructor
if isinstance(typ, CallableType) and typ.is_type_obj():
type_obj = typ.type_object()
if type_obj.is_final:
return True
return False


Expand Down