PEP 661: Sentinel Values

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.

12 Likes