diff --git a/pep-0661.rst b/pep-0661.rst index b8b8ca4dc12..24237c3d7c4 100644 --- a/pep-0661.rst +++ b/pep-0661.rst @@ -15,7 +15,7 @@ TL;DR: See the `Specification`_ and `Reference Implementation`_. Abstract ======== -Unique placeholder values, widely known as "sentinel values", are useful in +Unique placeholder values, commonly known as "sentinel values", are useful in Python programs for several things, such as default values for function arguments where ``None`` is a valid input value. These cases are common enough for several idioms for implementing such "sentinels" to have arisen @@ -120,13 +120,27 @@ and a single optional argument, the repr of the object. >>> MISSING mymodule.MISSING +Checking if a value is such a sentinel *should* be done using the ``is`` +operator, as is recommended for ``None``. Equality checks using ``==`` will +also work as expected, returning ``True`` only when the object is compared +with itself. -A third optional argument, the name of the module where the sentinel is -defined, exists to be used to support cases where the name of the module -cannot be found by inspecting the stack frame. This is identical to the -pattern used by ``collections.namedtuple``. (The name of the module is -used to choose a unique name for the class generated for the new sentinel, -which is set as an attribute of the ``sentinels`` module.) +The name should be set to the name of the variable used to reference the +object, as in the examples above. Otherwise, the sentinel object won't be +able to survive copying or pickling+unpickling while retaining the above +described behavior. Note, that when defined in a class scope, the name must +be the fully-qualified name of the variable in the module, for example:: + + class MyClass: + NotGiven = sentinel('MyClass.NotGiven') + +Type annotations for sentinel values will use `typing.Literal`_. +For example:: + + def foo(value: int | Literal[NotGiven]) -> None: + ... + +.. _typing.Literal: https://docs.python.org/3/library/typing.html#typing.Literal Reference Implementation @@ -145,11 +159,11 @@ The reference implementation is found in a dedicated GitHub repo '__repr__': lambda self: repr, } cls = type(class_name, (), class_namespace) - cls.__module__ = __name__ - globals()[class_name] = cls + cls.__module__ = module + _get_parent_frame().f_globals[class_name] = cls sentinel = cls() - cls.__new__ = lambda cls: sentinel + cls.__new__ = lambda cls_: sentinel return sentinel @@ -159,6 +173,15 @@ The reference implementation is found in a dedicated GitHub repo sentinel_qualname.replace('.', '_')]) +Note that a dedicated class is created automatically for each sentinel object. +This class is assigned to the namespace of the module from which the +``sentinel()`` call was made, or to that of the ``sentinels`` module itself as +a fallback. These classes have long names comprised of several parts to +ensure their uniqueness. However, these names usually wouldn't be used, since +type annotations should use ``Literal[]`` as described above, and identity +checks should be preferred over type checks. + + Rejected Ideas ============== @@ -256,10 +279,10 @@ To have a clear repr, one could define ``__repr__``: class NotGiven(metaclass=SentinelMeta): pass However, all such implementations don't have a dedicated type for the -sentinel, which is considered desirable. A dedicated type could be created -by a meta-class or class decorator, but at that point the implementation would -become much more complex and loses its advantages over the chosen -implementation. +sentinel, which is considered desirable for strict typing. A dedicated type +could be created by a meta-class or class decorator, but at that point the +implementation would become much more complex and loses its advantages over +the chosen implementation. Additionally, using classes this way is unusual and could be confusing. @@ -300,6 +323,7 @@ References .. [5] `bpo-44123: Make function parameter sentinel values true singletons `_ .. [6] `The "sentinels" package on PyPI `_ .. [7] `The "sentinel" package on PyPI `_ +.. [8] `Discussion thread about type signatures for these sentinels on the typing-sig mailing list `_ Copyright