From b6879057c295991b856e0c2f7ec348e1b68d4c16 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 15:18:23 -0700 Subject: [PATCH 1/2] PEP 661: Amend to allow two customizations --- peps/pep-0661.rst | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/peps/pep-0661.rst b/peps/pep-0661.rst index 8da85bf9789..c3fd97aa661 100644 --- a/peps/pep-0661.rst +++ b/peps/pep-0661.rst @@ -141,6 +141,18 @@ different possible implementations, the design below was chosen to meet these criteria while keeping the API and implementation small (see `Reference Implementation`_). +To support common use cases, we add two customization points: + +- The ``repr`` argument can be used to customize the ``repr()`` of sentinels. Many + pre-existing sentinels in the standard library use a custom repr like ````, + and this argument allows us to convert these sentinels to the new API while + preserving this behavior. +- The ``__module__`` attribute of sentinels is writable, allowing users to + control the module used for pickling in some unusual cases, such as sentinels + created through ``exec()``. This aligns with the behavior of other built-in + types with a ``__module__`` attribute, including classes, functions, ``TypeVar`` + objects, and (as of Python 3.15) type aliases. + Specification ============= @@ -151,14 +163,17 @@ A new built-in callable named ``sentinel`` will be added. >>> MISSING MISSING -``sentinel()`` takes a single positional-only argument, ``name``, which must -be a ``str``. Passing a non-string raises ``TypeError``. The name is used as -the sentinel's name and repr. +``sentinel()`` takes a single required positional-only argument, ``name``, which must +be a ``str``, and an optional keyword-only argument, ``repr``. +Passing a non-string as the ``name`` raises ``TypeError``. The name is used as +the sentinel's name. The value of the ``repr`` argument is used for the ``str()`` and ``repr()`` +of the sentinel object, if given. If not given, the name is used instead. Sentinel objects have two public attributes: * ``__name__`` is the sentinel's name. * ``__module__`` is the name of the module where ``sentinel()`` was called. + This attribute is writable. ``sentinel`` may not be subclassed. @@ -303,23 +318,20 @@ A sketch of the intended behavior follows:: class sentinel: """Unique sentinel values.""" - __slots__ = ("__name__", "_module_name") + __slots__ = ("__name__", "__module__", "_repr") def __init_subclass__(cls): raise TypeError("type 'sentinel' is not an acceptable base type") - def __init__(self, name, /): + def __init__(self, name, /, repr=None): if not isinstance(name, str): raise TypeError("sentinel name must be a string") self.__name__ = name - self._module_name = sys._getframemodulename(1) - - @property - def __module__(self): - return self._module_name + self.__module__ = sys._getframemodulename(1) + self._repr = repr if repr is not None else name def __repr__(self): - return self.__name__ + return self._repr def __reduce__(self): return self.__name__ @@ -492,14 +504,6 @@ module or class name, they can include it in the single ``name`` argument explicitly, e.g. ``sentinel("mymodule.MISSING")``. -Allowing customization of repr ------------------------------- - -This was desirable to allow using this for existing sentinel values without -changing their repr. However, this was eventually dropped as it wasn't -considered worth the added complexity. - - Allowing customization of boolean evaluation -------------------------------------------- From 7dd698fb2cabf1b7d88e559ad3afd6eef2684127 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 16:12:06 -0700 Subject: [PATCH 2/2] C --- peps/pep-0661.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/peps/pep-0661.rst b/peps/pep-0661.rst index c3fd97aa661..adc15929572 100644 --- a/peps/pep-0661.rst +++ b/peps/pep-0661.rst @@ -277,7 +277,9 @@ C API Sentinels can also be useful in C extensions. We propose two new C API functions: -* ``PyObject *PySentinel_New(const char *name, const char *module_name)`` creates a new sentinel object. +* ``PyObject *PySentinel_New(const char *name, const char *module_name, const char *repr)`` + creates a new sentinel object. ``repr`` may be ``NULL``, in which case the sentinel's + repr will be the same as its name. * ``bool PySentinel_Check(PyObject *obj)`` checks if an object is a sentinel. C code can use the ``==`` operator to check if an object is a