A possibly controversial point is whether we should require that sentinels must be assigned to the same variable name as their sentinel name (i.e., should X = sentinel("Y") be a type checker error?). I chose not to put this requirement in my PR, because there may be cases where a user wants to use a name with some special characters (example), and because sentinels defined in classes need to put the class name in the sentinel name for the sentinel to be picklable (PEP 661: Sentinel Values - #344 by tmk). Sentinels that don’t use the right name aren’t picklable, but I don’t see a need for type checkers to enforce that; it’s fine to leave it to linters.
Using pickle support as the motivation for type errors implies that sentinels in a local scope should be a type error too and I’d disagree with that. Sentinels are unique first and optionally pickleable as long as certain rules are followed. Then again there’s not much difference than using object() if those rules aren’t followed.
This would be “customization of repr” which was already rejected. Abusing the name to modify the repr string is a flawed idea that is technically allowed due to the current specification. I’d have preferred accepting a repr= keyword over devs breaking pickle in an attempt to customize the repr string.
Sentinels will be used in many places where there’s no need or expectation of pickle support, but be careful with any sentinel stored in a container or as an attribute.
Sentinel name mismatches like these don’t affect type-safety in any way, so I agree that there’s no need to require type-checkers to enforce matching names.
What would be displayed in the type-checker report if they don’t have matching names?
X = sentinel("Y")
def f(arg: int | X, /) -> None: ...
f("") # Argument 1 to "f" has incompatible type "str"; expected "int | <???>"
If it’s <module>.X, the name parameter to sentinel() would be for purely runtime repr displays and is not used in static typing error reports, which is a mismatch;
If it’s <module>.Y, the error report would reference the qualified name of a symbol that doesn’t exist.
Currently, type-checkers check for matching names in similar expressions as well, e.g.
from enum import Enum
from typing import NamedTuple
E = Enum("Z", (("b", int),)) # Error
T = NamedTuple("K", (("b", int),)) # Error