Skip to content

Commit ff4d71b

Browse files
fix: Preserve sentinel identity in error messages
Signed-off-by: Edgar Ramírez Mondragón <[email protected]>
1 parent ee1e2c4 commit ff4d71b

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

‎mypy/messages.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,12 @@ def format_literal_value(typ: LiteralType) -> str:
27122712

27132713
if isinstance(typ, Instance):
27142714
itype = typ
2715+
if itype.last_known_value is not None and itype.last_known_value.is_sentinel_literal():
2716+
# Sentinel values (PEP 661) have no other way to identify themselves
2717+
# than via their literal, so use it instead of the shared fallback
2718+
# class name (unlike other literals, sentinels are always formatted
2719+
# this way, e.g. "MISSING" rather than "Literal[MISSING]").
2720+
return format_literal_value(itype.last_known_value)
27152721
# Get the short name of the type.
27162722
if itype.type.fullname == "types.ModuleType":
27172723
# Make some common error messages simpler and tidier.
@@ -3525,6 +3531,12 @@ def ignore_last_known_values(t: UnionType) -> Type:
35253531
seen_instances = set()
35263532
for item in t.items:
35273533
if isinstance(item, ProperType) and isinstance(item, Instance):
3534+
if item.last_known_value is not None and item.last_known_value.is_sentinel_literal():
3535+
# Sentinel values (PEP 661) have no other way to identify themselves
3536+
# than via their literal, unlike e.g. enum members, so it must be
3537+
# preserved (see mypy/erasetype.py for the same exemption).
3538+
union_items.append(item)
3539+
continue
35283540
erased = item.copy_modified(last_known_value=None)
35293541
if erased in seen_instances:
35303542
continue

‎test-data/unit/check-sentinels.test‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,13 @@ def func(d: dict[str, str]) -> None:
206206
var = d.get("key", Unknown)
207207
assert_type(var, str | Unknown)
208208
[builtins fixtures/dict-full.pyi]
209+
210+
[case testSentinelPreservedInErrorMessages]
211+
from typing_extensions import sentinel
212+
213+
Unknown = sentinel("Unknown")
214+
215+
def func(d: dict[str, str]) -> None:
216+
var = d.get("key", Unknown)
217+
x: int = var # E: Incompatible types in assignment (expression has type "str | Unknown", variable has type "int")
218+
[builtins fixtures/dict-full.pyi]

0 commit comments

Comments
 (0)