Skip to content
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
4 changes: 0 additions & 4 deletions mypy/erasetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,6 @@ class LastKnownValueEraser(TypeTranslator):
def visit_instance(self, t: Instance) -> Type:
if not t.last_known_value and not t.args:
return t
if t.last_known_value is not None and t.last_known_value.is_sentinel_literal():
# Sentinel values (PEP 661) have no other way to identify themselves than
# via their literal, unlike e.g. enum members, so it must be preserved.
return t
return t.copy_modified(args=[a.accept(self) for a in t.args], last_known_value=None)

def visit_type_alias_type(self, t: TypeAliasType) -> Type:
Expand Down
4 changes: 0 additions & 4 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,6 @@ def visit_type_var(self, t: TypeVarType) -> Type:
t = t.copy_modified(upper_bound=t.upper_bound.accept(self))
repl = self.variables.get(t.id, t)
if isinstance(repl, ProperType) and isinstance(repl, Instance):
if repl.last_known_value is not None and repl.last_known_value.is_sentinel_literal():
# Sentinel values (PEP 661) have no other way to identify themselves than
# via their literal, unlike e.g. enum members, so it must survive expansion.
return repl
# TODO: do we really need to do this?
# If I try to remove this special-casing ~40 tests fail on reveal_type().
return repl.copy_modified(last_known_value=None)
Expand Down
12 changes: 0 additions & 12 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2712,12 +2712,6 @@ def format_literal_value(typ: LiteralType) -> str:

if isinstance(typ, Instance):
itype = typ
if itype.last_known_value is not None and itype.last_known_value.is_sentinel_literal():
# Sentinel values (PEP 661) have no other way to identify themselves
# than via their literal, so use it instead of the shared fallback
# class name (unlike other literals, sentinels are always formatted
# this way, e.g. "MISSING" rather than "Literal[MISSING]").
return format_literal_value(itype.last_known_value)
# Get the short name of the type.
if itype.type.fullname == "types.ModuleType":
# Make some common error messages simpler and tidier.
Expand Down Expand Up @@ -3531,12 +3525,6 @@ def ignore_last_known_values(t: UnionType) -> Type:
seen_instances = set()
for item in t.items:
if isinstance(item, ProperType) and isinstance(item, Instance):
if item.last_known_value is not None and item.last_known_value.is_sentinel_literal():
# Sentinel values (PEP 661) have no other way to identify themselves
# than via their literal, unlike e.g. enum members, so it must be
# preserved (see mypy/erasetype.py for the same exemption).
union_items.append(item)
continue
erased = item.copy_modified(last_known_value=None)
if erased in seen_instances:
continue
Expand Down
12 changes: 4 additions & 8 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3422,27 +3422,23 @@ def setup_sentinel_var(self, s: AssignmentStmt) -> None:
assert isinstance(lvalue, NameExpr)
if not isinstance(lvalue.node, Var):
return
lvalue.is_special_form = True
var = lvalue.node
var.is_sentinel = True
typ = self.sentinel_type_for_var(var, s.rvalue)
if typ is not None:
s.type = typ

def sentinel_type_for_var(self, var: Var, rvalue: Expression) -> Instance | None:
def sentinel_type_for_var(self, var: Var, rvalue: Expression) -> LiteralType | None:
assert isinstance(rvalue, CallExpr)
callee = rvalue.callee
assert isinstance(callee, RefExpr)
typ = self.named_type_or_none(callee.fullname)
if typ is None:
return None
name = f"{self.type.name}.{var.name}" if self.type is not None else var.name
return typ.copy_modified(
last_known_value=LiteralType(
SentinelValue(var.fullname, name),
fallback=typ,
line=rvalue.line,
column=rvalue.column,
)
return LiteralType(
SentinelValue(var.fullname, name), fallback=typ, line=rvalue.line, column=rvalue.column
)

def analyze_identity_global_assignment(self, s: AssignmentStmt) -> bool:
Expand Down
11 changes: 4 additions & 7 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,13 +1060,10 @@ def analyze_unbound_type_without_type_info(

if isinstance(sym.node, Var) and sym.node.is_sentinel:
typ = get_proper_type(sym.node.type)
if isinstance(typ, Instance) and typ.last_known_value is not None:
return LiteralType(
value=typ.last_known_value.value,
fallback=typ.last_known_value.fallback,
line=t.line,
column=t.column,
)
assert isinstance(typ, LiteralType)
return LiteralType(
value=typ.value, fallback=typ.fallback, line=t.line, column=t.column
)

# None of the above options worked. We parse the args (if there are any)
# to make sure there are no remaining semanal-only types, then give up.
Expand Down
3 changes: 1 addition & 2 deletions test-data/unit/check-sentinels.test
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,7 @@ X = sentinel("X")

def inspect_sentinel(arg: sentinel) -> None: ...

reveal_type(X) # N: Revealed type is "X?"
reveal_type(X) # N: Revealed type is "X"
reveal_type(inspect_sentinel) # N: Revealed type is "def (arg: typing_extensions.Sentinel)"
inspect_sentinel(arg=X)

[builtins fixtures/tuple.pyi]
2 changes: 1 addition & 1 deletion test-data/unit/deps-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -1036,4 +1036,4 @@ def f(x: MISSING) -> None:
pass
[builtins fixtures/tuple.pyi]
[out]
<m.MISSING> -> <m.MISSING>, <m.f>, m, m.f
<m.MISSING> -> <m.f>, m, m.f
Loading