I’m confused about the type checking motivation – isn’t the natural solution to do typing.Literal[MySentinel]
? Doesn’t your example code end up giving the type an obscure name that would look weird in type signatures?
BTW, here’s a kind of “stupid python trick”, but it is convenient in some ways. You can make a sentinel whose type is itself:
class _SentinelBase(type):
def __new__(self):
raise TypeError(f"{self!r} is not callable")
def __repr__(self):
return self.__name__
def sentinel(name):
cls = type.__new__(_SentinelBase, name, (_SentinelBase,), {})
cls.__class__ = cls
return cls
Example:
>>> MISSING = sentinel("MISSING")
>>> MISSING
MISSING
# You can dispatch by type, e.g. using functools.singledispatch:
>>> isinstance(MISSING, MISSING)
True
And you get to use MISSING
and Literal[MISSING]
interchangeably in type annotations!
I blame Dave for inspiring this idea. This code is provided AS IS, WITHOUT WARRANTY OF ANY KIND, INCLUDING THE IMPLIED WARRANTY OF NOT GIVING YOU A HEADACHE WHILE READING IT.