CINXE.COM

PEP 661 – Sentinel Values | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 661 – Sentinel Values | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0661/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 661 – Sentinel Values | peps.python.org'> <meta property="og:description" content="Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:"> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0661/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:"> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 661</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 661 – Sentinel Values</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Tal Einat &lt;tal&#32;&#97;t&#32;python.org&gt;</dd> <dt class="field-even">Discussions-To<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-661-sentinel-values/9126">Discourse thread</a></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Proposal under active discussion and revision">Draft</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">06-Jun-2021</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/sentinel-values-in-the-stdlib/8810" title="Discourse thread">20-May-2021</a>, <a class="reference external" href="https://discuss.python.org/t/pep-661-sentinel-values/9126" title="Discourse thread">06-Jun-2021</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#typing">Typing</a></li> </ul> </li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#use-notgiven-object">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></li> <li><a class="reference internal" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></li> <li><a class="reference internal" href="#use-the-existing-ellipsis-sentinel-value">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></li> <li><a class="reference internal" href="#use-a-single-valued-enum">Use a single-valued enum</a></li> <li><a class="reference internal" href="#a-sentinel-class-decorator">A sentinel class decorator</a></li> <li><a class="reference internal" href="#using-class-objects">Using class objects</a></li> <li><a class="reference internal" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation">Define a recommended “standard” idiom, without supplying an implementation</a></li> <li><a class="reference internal" href="#allowing-customization-of-repr">Allowing customization of repr</a></li> <li><a class="reference internal" href="#using-typing-literal-in-type-annotations">Using <code class="docutils literal notranslate"><span class="pre">typing.Literal</span></code> in type annotations</a></li> </ul> </li> <li><a class="reference internal" href="#additional-notes">Additional Notes</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a></li> <li><a class="reference internal" href="#footnotes">Footnotes</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <p>TL;DR: See the <a class="reference internal" href="#specification">Specification</a> and <a class="reference internal" href="#reference-implementation">Reference Implementation</a>.</p> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:</p> <ul> <li>Default values for function arguments, for when a value was not given:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="o">...</span> </pre></div> </div> </li> <li>Return values from functions when something is not found or unavailable:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="s2">&quot;abc&quot;</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&quot;d&quot;</span><span class="p">)</span> <span class="go">-1</span> </pre></div> </div> </li> <li>Missing data, such as NULL in relational databases or “N/A” (“not available”) in spreadsheets</li> </ul> <p>Python has the special value <code class="docutils literal notranslate"><span class="pre">None</span></code>, which is intended to be used as such a sentinel value in most cases. However, sometimes an alternative sentinel value is needed, usually when it needs to be distinct from <code class="docutils literal notranslate"><span class="pre">None</span></code> since <code class="docutils literal notranslate"><span class="pre">None</span></code> is a valid value in that context. Such cases are common enough that several idioms for implementing such sentinels have arisen over the years, but uncommon enough that there hasn’t been a clear need for standardization. However, the common implementations, including some in the stdlib, suffer from several significant drawbacks.</p> <p>This PEP proposes adding a utility for defining sentinel values, to be used in the stdlib and made publicly available as part of the stdlib.</p> <p>Note: Changing all existing sentinels in the stdlib to be implemented this way is not deemed necessary, and whether to do so is left to the discretion of the maintainers.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>In May 2021, a question was brought up on the python-dev mailing list <a class="footnote-reference brackets" href="#id15" id="id1">[1]</a> about how to better implement a sentinel value for <code class="docutils literal notranslate"><span class="pre">traceback.print_exception</span></code>. The existing implementation used the following common idiom:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_sentinel</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span> </pre></div> </div> <p>However, this object has an uninformative and overly verbose repr, causing the function’s signature to be overly long and hard to read:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">traceback</span><span class="o">.</span><span class="n">print_exception</span><span class="p">)</span> <span class="go">Help on function print_exception in module traceback:</span> <span class="go">print_exception(exc, /, value=&lt;object object at</span> <span class="go">0x000002825DF09650&gt;, tb=&lt;object object at 0x000002825DF09650&gt;,</span> <span class="go">limit=None, file=None, chain=True)</span> </pre></div> </div> <p>Additionally, two other drawbacks of many existing sentinels were brought up in the discussion:</p> <ol class="arabic simple"> <li>Some do not have a distinct type, hence it is impossible to define clear type signatures for functions with such sentinels as default values.</li> <li>They behave unexpectedly after being copied or unpickled, due to a separate instance being created and thus comparisons using <code class="docutils literal notranslate"><span class="pre">is</span></code> failing.</li> </ol> <p>In the ensuing discussion, Victor Stinner supplied a list of currently used sentinel values in the Python standard library <a class="footnote-reference brackets" href="#id16" id="id2">[2]</a>. This showed that the need for sentinels is fairly common, that there are various implementation methods used even within the stdlib, and that many of these suffer from at least one of the three above drawbacks.</p> <p>The discussion did not lead to any clear consensus on whether a standard implementation method is needed or desirable, whether the drawbacks mentioned are significant, nor which kind of implementation would be good. The author of this PEP created an issue on bugs.python.org (now a GitHub issue <a class="footnote-reference brackets" href="#id17" id="id3">[3]</a>) suggesting options for improvement, but that focused on only a single problematic aspect of a few cases, and failed to gather any support.</p> <p>A poll <a class="footnote-reference brackets" href="#id18" id="id4">[4]</a> was created on discuss.python.org to get a clearer sense of the community’s opinions. After nearly two weeks, significant further, discussion, and 39 votes, the poll’s results were not conclusive. 40% had voted for “The status-quo is fine / there’s no need for consistency in this”, but most voters had voted for one or more standardized solutions. Specifically, 37% of the voters chose “Consistent use of a new, dedicated sentinel factory / class / meta-class, also made publicly available in the stdlib”.</p> <p>With such mixed opinions, this PEP was created to facilitate making a decision on the subject.</p> <p>While working on this PEP, iterating on various options and implementations and continuing discussions, the author has come to the opinion that a simple, good implementation available in the standard library would be worth having, both for use in the standard library itself and elsewhere.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>The criteria guiding the chosen implementation were:</p> <ol class="arabic simple"> <li>The sentinel objects should behave as expected by a sentinel object: When compared using the <code class="docutils literal notranslate"><span class="pre">is</span></code> operator, it should always be considered identical to itself but never to any other object.</li> <li>Creating a sentinel object should be a simple, straightforward one-liner.</li> <li>It should be simple to define as many distinct sentinel values as needed.</li> <li>The sentinel objects should have a clear and short repr.</li> <li>It should be possible to use clear type signatures for sentinels.</li> <li>The sentinel objects should behave correctly after copying and/or unpickling.</li> <li>Such sentinels should work when using CPython 3.x and PyPy3, and ideally also with other implementations of Python.</li> <li>As simple and straightforward as possible, in implementation and especially in use. Avoid this becoming one more special thing to learn when learning Python. It should be easy to find and use when needed, and obvious enough when reading code that one would normally not feel a need to look up its documentation.</li> </ol> <p>With so many uses in the Python standard library <a class="footnote-reference brackets" href="#id16" id="id5">[2]</a>, it would be useful to have an implementation in the standard library, since the stdlib cannot use implementations of sentinel objects available elsewhere (such as the <code class="docutils literal notranslate"><span class="pre">sentinels</span></code> <a class="footnote-reference brackets" href="#id19" id="id6">[5]</a> or <code class="docutils literal notranslate"><span class="pre">sentinel</span></code> <a class="footnote-reference brackets" href="#id20" id="id7">[6]</a> PyPI packages).</p> <p>After researching existing idioms and implementations, and going through many different possible implementations, an implementation was written which meets all of these criteria (see <a class="reference internal" href="#reference-implementation">Reference Implementation</a>).</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>A new <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> class will be added to a new <code class="docutils literal notranslate"><span class="pre">sentinellib</span></code> module.</p> <div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span><span class="w"> </span><span class="nn">sentinellib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Sentinel</span> <span class="gp">&gt;&gt;&gt; </span><span class="n">MISSING</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">&#39;MISSING&#39;</span><span class="p">)</span> <span class="gp">&gt;&gt;&gt; </span><span class="n">MISSING</span> <span class="go">MISSING</span> </pre></div> </div> <p>Checking if a value is such a sentinel <em>should</em> be done using the <code class="docutils literal notranslate"><span class="pre">is</span></code> operator, as is recommended for <code class="docutils literal notranslate"><span class="pre">None</span></code>. Equality checks using <code class="docutils literal notranslate"><span class="pre">==</span></code> will also work as expected, returning <code class="docutils literal notranslate"><span class="pre">True</span></code> only when the object is compared with itself. Identity checks such as <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">value</span> <span class="pre">is</span> <span class="pre">MISSING:</span></code> should usually be used rather than boolean checks such as <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">value:</span></code> or <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">not</span> <span class="pre">value:</span></code>.</p> <p>Sentinel instances are “truthy”, i.e. boolean evaluation will result in <code class="docutils literal notranslate"><span class="pre">True</span></code>. This parallels the default for arbitrary classes, as well as the boolean value of <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code>. This is unlike <code class="docutils literal notranslate"><span class="pre">None</span></code>, which is “falsy”.</p> <p>The names of sentinels are unique within each module. When calling <code class="docutils literal notranslate"><span class="pre">Sentinel()</span></code> in a module where a sentinel with that name was already defined, the existing sentinel with that name will be returned. Sentinels with the same name defined in different modules will be distinct from each other.</p> <p>Creating a copy of a sentinel object, such as by using <code class="docutils literal notranslate"><span class="pre">copy.copy()</span></code> or by pickling and unpickling, will return the same object.</p> <p><code class="docutils literal notranslate"><span class="pre">Sentinel()</span></code> will also accept a single optional argument, <code class="docutils literal notranslate"><span class="pre">module_name</span></code>. This should normally not need to be supplied, as <code class="docutils literal notranslate"><span class="pre">Sentinel()</span></code> will usually be able to recognize the module in which it was called. <code class="docutils literal notranslate"><span class="pre">module_name</span></code> should be supplied only in unusual cases when this automatic recognition does not work as intended, such as perhaps when using Jython or IronPython. This parallels the designs of <code class="docutils literal notranslate"><span class="pre">Enum</span></code> and <code class="docutils literal notranslate"><span class="pre">namedtuple</span></code>. For more details, see <a class="pep reference internal" href="../pep-0435/" title="PEP 435 – Adding an Enum type to the Python standard library">PEP 435</a>.</p> <p>The <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> class may not be sub-classed, to avoid the greater complexity of supporting subclassing.</p> <p>Ordering comparisons are undefined for sentinel objects.</p> <section id="typing"> <h3><a class="toc-backref" href="#typing" role="doc-backlink">Typing</a></h3> <p>To make usage of sentinels clear and simple in typed Python code, we propose to amend the type system with a special case for sentinel objects.</p> <p>Sentinel objects may be used in <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-type-expression" title="(in typing)"><span class="xref std std-term">type expressions</span></a>, representing themselves. This is similar to how <code class="docutils literal notranslate"><span class="pre">None</span></code> is handled in the existing type system. For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">sentinels</span><span class="w"> </span><span class="kn">import</span> <span class="n">Sentinel</span> <span class="n">MISSING</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">&#39;MISSING&#39;</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="n">MISSING</span> <span class="o">=</span> <span class="n">MISSING</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span> </pre></div> </div> <p>More formally, type checkers should recognize sentinel creations of the form <code class="docutils literal notranslate"><span class="pre">NAME</span> <span class="pre">=</span> <span class="pre">Sentinel('NAME')</span></code> as creating a new sentinel object. If the name passed to the <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> constructor does not match the name the object is assigned to, type checkers should emit an error.</p> <p>Sentinels defined using this syntax may be used in <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-type-expression" title="(in typing)"><span class="xref std std-term">type expressions</span></a>. They represent a <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-fully-static-type" title="(in typing)"><span class="xref std std-term">fully static type</span></a> that has a single member, the sentinel object itself.</p> <p>Type checkers should support narrowing union types involving sentinels using the <code class="docutils literal notranslate"><span class="pre">is</span></code> and <code class="docutils literal notranslate"><span class="pre">is</span> <span class="pre">not</span></code> operators:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">sentinels</span><span class="w"> </span><span class="kn">import</span> <span class="n">Sentinel</span> <span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">assert_type</span> <span class="n">MISSING</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">&#39;MISSING&#39;</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="n">MISSING</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">MISSING</span><span class="p">:</span> <span class="n">assert_type</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">MISSING</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="n">assert_type</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span> </pre></div> </div> <p>To support usage in type expressions, the runtime implementation of the <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> class should have the <code class="docutils literal notranslate"><span class="pre">__or__</span></code> and <code class="docutils literal notranslate"><span class="pre">__ror__</span></code> methods, returning <a class="reference external" href="https://docs.python.org/3.14/library/typing.html#typing.Union" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">typing.Union</span></code></a> objects.</p> </section> </section> <section id="backwards-compatibility"> <h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2> <p>This proposal should have no backwards compatibility implications.</p> </section> <section id="how-to-teach-this"> <h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2> <p>The normal types of documentation of new stdlib modules and features, namely doc-strings, module docs and a section in “What’s New”, should suffice.</p> </section> <section id="security-implications"> <h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2> <p>This proposal should have no security implications.</p> </section> <section id="reference-implementation"> <h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2> <p>The reference implementation is found in a dedicated GitHub repo <a class="footnote-reference brackets" href="#id21" id="id8">[7]</a>. A simplified version follows:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_registry</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">class</span><span class="w"> </span><span class="nc">Sentinel</span><span class="p">:</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Unique sentinel values.&quot;&quot;&quot;</span> <span class="k">def</span><span class="w"> </span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">module_name</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="n">name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="k">if</span> <span class="n">module_name</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="n">module_name</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">_getframemodulename</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">module_name</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="n">module_name</span> <span class="o">=</span> <span class="vm">__name__</span> <span class="n">registry_key</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="n">module_name</span><span class="si">}</span><span class="s1">-</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">&#39;</span> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">_registry</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">registry_key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="k">if</span> <span class="n">sentinel</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> <span class="k">return</span> <span class="n">sentinel</span> <span class="n">sentinel</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span> <span class="n">sentinel</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="n">sentinel</span><span class="o">.</span><span class="n">_module_name</span> <span class="o">=</span> <span class="n">module_name</span> <span class="k">return</span> <span class="n">_registry</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">registry_key</span><span class="p">,</span> <span class="n">sentinel</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="k">def</span><span class="w"> </span><span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">,</span> <span class="p">(</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_module_name</span><span class="p">,</span> <span class="p">),</span> <span class="p">)</span> </pre></div> </div> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <section id="use-notgiven-object"> <h3><a class="toc-backref" href="#use-notgiven-object" role="doc-backlink">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></h3> <p>This suffers from all of the drawbacks mentioned in the <a class="reference internal" href="#rationale">Rationale</a> section.</p> </section> <section id="add-a-single-new-sentinel-value-such-as-missing-or-sentinel"> <h3><a class="toc-backref" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel" role="doc-backlink">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></h3> <p>Since such a value could be used for various things in various places, one could not always be confident that it would never be a valid value in some use cases. On the other hand, a dedicated and distinct sentinel value can be used with confidence without needing to consider potential edge-cases.</p> <p>Additionally, it is useful to be able to provide a meaningful name and repr for a sentinel value, specific to the context where it is used.</p> <p>Finally, this was a very unpopular option in the poll <a class="footnote-reference brackets" href="#id18" id="id9">[4]</a>, with only 12% of the votes voting for it.</p> </section> <section id="use-the-existing-ellipsis-sentinel-value"> <h3><a class="toc-backref" href="#use-the-existing-ellipsis-sentinel-value" role="doc-backlink">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></h3> <p>This is not the original intended use of Ellipsis, though it has become increasingly common to use it to define empty class or function blocks instead of using <code class="docutils literal notranslate"><span class="pre">pass</span></code>.</p> <p>Also, similar to a potential new single sentinel value, <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> can’t be as confidently used in all cases, unlike a dedicated, distinct value.</p> </section> <section id="use-a-single-valued-enum"> <h3><a class="toc-backref" href="#use-a-single-valued-enum" role="doc-backlink">Use a single-valued enum</a></h3> <p>The suggested idiom is:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">NotGivenType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span> <span class="n">NotGiven</span> <span class="o">=</span> <span class="s1">&#39;NotGiven&#39;</span> <span class="n">NotGiven</span> <span class="o">=</span> <span class="n">NotGivenType</span><span class="o">.</span><span class="n">NotGiven</span> </pre></div> </div> <p>Besides the excessive repetition, the repr is overly long: <code class="docutils literal notranslate"><span class="pre">&lt;NotGivenType.NotGiven:</span> <span class="pre">'NotGiven'&gt;</span></code>. A shorter repr can be defined, at the expense of a bit more code and yet more repetition.</p> <p>Finally, this option was the least popular among the nine options in the poll <a class="footnote-reference brackets" href="#id18" id="id10">[4]</a>, being the only option to receive no votes.</p> </section> <section id="a-sentinel-class-decorator"> <h3><a class="toc-backref" href="#a-sentinel-class-decorator" role="doc-backlink">A sentinel class decorator</a></h3> <p>The suggested idiom is:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@sentinel</span> <span class="k">class</span><span class="w"> </span><span class="nc">NotGivenType</span><span class="p">:</span> <span class="k">pass</span> <span class="n">NotGiven</span> <span class="o">=</span> <span class="n">NotGivenType</span><span class="p">()</span> </pre></div> </div> <p>While this allows for a very simple and clear implementation of the decorator, the idiom is too verbose, repetitive, and difficult to remember.</p> </section> <section id="using-class-objects"> <h3><a class="toc-backref" href="#using-class-objects" role="doc-backlink">Using class objects</a></h3> <p>Since classes are inherently singletons, using a class as a sentinel value makes sense and allows for a simple implementation.</p> <p>The simplest version of this is:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">NotGiven</span><span class="p">:</span> <span class="k">pass</span> </pre></div> </div> <p>To have a clear repr, one would need to use a meta-class:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">NotGiven</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">SentinelMeta</span><span class="p">):</span> <span class="k">pass</span> </pre></div> </div> <p>… or a class decorator:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@Sentinel</span> <span class="k">class</span><span class="w"> </span><span class="nc">NotGiven</span><span class="p">:</span> <span class="k">pass</span> </pre></div> </div> <p>Using classes this way is unusual and could be confusing. The intention of code would be hard to understand without comments. It would also cause such sentinels to have some unexpected and undesirable behavior, such as being callable.</p> </section> <section id="define-a-recommended-standard-idiom-without-supplying-an-implementation"> <h3><a class="toc-backref" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation" role="doc-backlink">Define a recommended “standard” idiom, without supplying an implementation</a></h3> <p>Most common existing idioms have significant drawbacks. So far, no idiom has been found that is clear and concise while avoiding these drawbacks.</p> <p>Also, in the poll <a class="footnote-reference brackets" href="#id18" id="id11">[4]</a> on this subject, the options for recommending an idiom were unpopular, with the highest-voted option being voted for by only 25% of the voters.</p> </section> <section id="allowing-customization-of-repr"> <h3><a class="toc-backref" href="#allowing-customization-of-repr" role="doc-backlink">Allowing customization of repr</a></h3> <p>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.</p> </section> <section id="using-typing-literal-in-type-annotations"> <h3><a class="toc-backref" href="#using-typing-literal-in-type-annotations" role="doc-backlink">Using <code class="docutils literal notranslate"><span class="pre">typing.Literal</span></code> in type annotations</a></h3> <p>This was suggested by several people in discussions and is what this PEP first went with. However, it was pointed out that this would cause potential confusion, due to e.g. <code class="docutils literal notranslate"><span class="pre">Literal[&quot;MISSING&quot;]</span></code> referring to the string value <code class="docutils literal notranslate"><span class="pre">&quot;MISSING&quot;</span></code> rather than being a forward-reference to a sentinel value <code class="docutils literal notranslate"><span class="pre">MISSING</span></code>. Using the bare name was also suggested often in discussions. This follows the precedent and well-known pattern set by <code class="docutils literal notranslate"><span class="pre">None</span></code>, and has the advantages of not requiring an import and being much shorter.</p> </section> </section> <section id="additional-notes"> <h2><a class="toc-backref" href="#additional-notes" role="doc-backlink">Additional Notes</a></h2> <ul> <li>This PEP and the initial implementation are drafted in a dedicated GitHub repo <a class="footnote-reference brackets" href="#id21" id="id12">[7]</a>.</li> <li>For sentinels defined in a class scope, to avoid potential name clashes, one should use the fully-qualified name of the variable in the module. The full name will be used as the repr. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">class</span><span class="w"> </span><span class="nc">MyClass</span><span class="p">:</span> <span class="gp">... </span> <span class="n">NotGiven</span> <span class="o">=</span> <span class="n">sentinel</span><span class="p">(</span><span class="s1">&#39;MyClass.NotGiven&#39;</span><span class="p">)</span> <span class="gp">&gt;&gt;&gt; </span><span class="n">MyClass</span><span class="o">.</span><span class="n">NotGiven</span> <span class="go">MyClass.NotGiven</span> </pre></div> </div> </li> <li>One should be careful when creating sentinels in a function or method, since sentinels with the same name created by code in the same module will be identical. If distinct sentinel objects are needed, make sure to use distinct names.</li> <li>There is no single desirable value for the “truthiness” of sentinels, i.e. their boolean value. It is sometimes useful for the boolean value to be <code class="docutils literal notranslate"><span class="pre">True</span></code>, and sometimes <code class="docutils literal notranslate"><span class="pre">False</span></code>. Of the built-in sentinels in Python, <code class="docutils literal notranslate"><span class="pre">None</span></code> evaluates to <code class="docutils literal notranslate"><span class="pre">False</span></code>, while <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> (a.k.a. <code class="docutils literal notranslate"><span class="pre">...</span></code>) evaluates to <code class="docutils literal notranslate"><span class="pre">True</span></code>. The desire for this to be set as needed came up in discussions as well.</li> <li>The boolean value of <code class="docutils literal notranslate"><span class="pre">NotImplemented</span></code> is <code class="docutils literal notranslate"><span class="pre">True</span></code>, but using this is deprecated since Python 3.9 (doing so generates a deprecation warning.) This deprecation is due to issues specific to <code class="docutils literal notranslate"><span class="pre">NotImplemented</span></code>, as described in bpo-35712 <a class="footnote-reference brackets" href="#id22" id="id13">[8]</a>.</li> <li>To define multiple, related sentinel values, possibly with a defined ordering among them, one should instead use <code class="docutils literal notranslate"><span class="pre">Enum</span></code> or something similar.</li> <li>There was a discussion on the typing-sig mailing list <a class="footnote-reference brackets" href="#id23" id="id14">[9]</a> about the typing for these sentinels, where different options were discussed.</li> </ul> </section> <section id="open-issues"> <h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2> <ul class="simple"> <li><strong>Is adding a new stdlib module the right way to go?</strong> I could not find any existing module which seems like a logical place for this. However, adding new stdlib modules should be done judiciously, so perhaps choosing an existing module would be preferable even if it is not a perfect fit?</li> </ul> </section> <section id="footnotes"> <h2><a class="toc-backref" href="#footnotes" role="doc-backlink">Footnotes</a></h2> <aside class="footnote-list brackets"> <aside class="footnote brackets" id="id15" role="doc-footnote"> <dt class="label" id="id15">[<a href="#id1">1</a>]</dt> <dd>Python-Dev mailing list: <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/ZLVPD2OISI7M4POMTR2FCQTE6TPMPTO3/">The repr of a sentinel</a></aside> <aside class="footnote brackets" id="id16" role="doc-footnote"> <dt class="label" id="id16">[2]<em> (<a href='#id2'>1</a>, <a href='#id5'>2</a>) </em></dt> <dd>Python-Dev mailing list: <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/">“The stdlib contains tons of sentinels”</a></aside> <aside class="footnote brackets" id="id17" role="doc-footnote"> <dt class="label" id="id17">[<a href="#id3">3</a>]</dt> <dd><a class="reference external" href="https://github.com/python/cpython/issues/88289">bpo-44123: Make function parameter sentinel values true singletons</a></aside> <aside class="footnote brackets" id="id18" role="doc-footnote"> <dt class="label" id="id18">[4]<em> (<a href='#id4'>1</a>, <a href='#id9'>2</a>, <a href='#id10'>3</a>, <a href='#id11'>4</a>) </em></dt> <dd>discuss.python.org Poll: <a class="reference external" href="https://discuss.python.org/t/sentinel-values-in-the-stdlib/8810/">Sentinel Values in the Stdlib</a></aside> <aside class="footnote brackets" id="id19" role="doc-footnote"> <dt class="label" id="id19">[<a href="#id6">5</a>]</dt> <dd><a class="reference external" href="https://pypi.org/project/sentinels/">The “sentinels” package on PyPI</a></aside> <aside class="footnote brackets" id="id20" role="doc-footnote"> <dt class="label" id="id20">[<a href="#id7">6</a>]</dt> <dd><a class="reference external" href="https://pypi.org/project/sentinel/">The “sentinel” package on PyPI</a></aside> <aside class="footnote brackets" id="id21" role="doc-footnote"> <dt class="label" id="id21">[7]<em> (<a href='#id8'>1</a>, <a href='#id12'>2</a>) </em></dt> <dd><a class="reference external" href="https://github.com/taleinat/python-stdlib-sentinels">Reference implementation at the taleinat/python-stdlib-sentinels GitHub repo</a></aside> <aside class="footnote brackets" id="id22" role="doc-footnote"> <dt class="label" id="id22">[<a href="#id13">8</a>]</dt> <dd><a class="reference external" href="https://github.com/python/cpython/issues/79893">bpo-35712: Make NotImplemented unusable in boolean context</a></aside> <aside class="footnote brackets" id="id23" role="doc-footnote"> <dt class="label" id="id23">[<a href="#id14">9</a>]</dt> <dd><a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/thread/NDEJ7UCDPINP634GXWDARVMTGDVSNBKV/#LVCPTY26JQJW7NKGKGAZXHQKWVW7GOGL">Discussion thread about type signatures for these sentinels on the typing-sig mailing list</a></aside> </aside> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0661.rst">https://github.com/python/peps/blob/main/peps/pep-0661.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0661.rst">2025-02-01 08:55:40 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#typing">Typing</a></li> </ul> </li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#use-notgiven-object">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></li> <li><a class="reference internal" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></li> <li><a class="reference internal" href="#use-the-existing-ellipsis-sentinel-value">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></li> <li><a class="reference internal" href="#use-a-single-valued-enum">Use a single-valued enum</a></li> <li><a class="reference internal" href="#a-sentinel-class-decorator">A sentinel class decorator</a></li> <li><a class="reference internal" href="#using-class-objects">Using class objects</a></li> <li><a class="reference internal" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation">Define a recommended “standard” idiom, without supplying an implementation</a></li> <li><a class="reference internal" href="#allowing-customization-of-repr">Allowing customization of repr</a></li> <li><a class="reference internal" href="#using-typing-literal-in-type-annotations">Using <code class="docutils literal notranslate"><span class="pre">typing.Literal</span></code> in type annotations</a></li> </ul> </li> <li><a class="reference internal" href="#additional-notes">Additional Notes</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a></li> <li><a class="reference internal" href="#footnotes">Footnotes</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0661.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10