Sentinel values in the stdlib

I was also thinking that. I’ve got a reasonable working implementation of MISSING = sentinel("MISSING"), but one which generates a class and an instance, rather than just using a class object as you suggest. AFAICT, the only drawbacks of this are using the same mechanism as Enum, logging and namedtuple of inspecting stack frames to find the module where it is defined, and using a naming hack instead of trying to set the class as a module attribute. I also didn’t like that the type isn’t directly available and would potentially have an awkward name, but being able to use Literal[MISSING] possibly makes that irrelevant.

I’m still not sure what would be preferable. (I originally intended to first think about it some more and get thoughts from a few initial reviewers.)

For those wondering, here is the (simplified) code:

import sys
if hasattr(sys, '_getframe'):
    get_parent_frame = lambda: sys._getframe(2)
else:
    def get_parent_frame():
        """Return the frame object for the caller's parent stack frame."""
        try:
            raise Exception
        except Exception:
            return sys.exc_info()[2].tb_frame.f_back.f_back

def sentinel(name):
    """Create a unique sentinel object."""
    repr_ = f'<{name}>'

    # This is a hack to get copying and unpickling to work without setting the
    # class as a module attribute.
    class_name = f'{name}.__class__'
    class_namespace = {
        '__repr__': lambda self: repr_,
    }
    cls = type(class_name, (), class_namespace)

    # For pickling to work, the __module__ variable needs to be set to the 
    # name of the module where the sentinel is defined.
    try:
        module = get_parent_frame().f_globals.get('__name__', '__main__')
    except (AttributeError, ValueError):
        pass
    else:
        cls.__module__ = module

    sentinel = cls()
    cls.__new__ = lambda self: sentinel

    return sentinel

Ahh, sorry about that, someone made a PR to that branch which triggered my GitHub notifs, and I (wrongly) assumed that I probably must have missed a mention of it in the email topic.

No worries :slight_smile:

Ah, but now I see that typing.Literal currently can’t be used with such sentinels.

FYI, I’ve created a second version of the draft PEP, and put up a GitHub repo with the PEP and a reference implementation.

2 Likes

It’s been nearly two weeks, so I’ve closed the poll.

This is now PEP 661: Sentinel Values.

Further discussion should be conducted here:

1 Like