peps/pep-0532/index.html

998 lines
105 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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 532 A circuit breaking protocol and binary operators | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0532/">
<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 532 A circuit breaking protocol and binary operators | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0532/">
<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="Python Enhancement Proposals (PEPs)">
<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 532</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 532 A circuit breaking protocol and binary operators</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Alyssa Coghlan &lt;ncoghlan&#32;&#97;t&#32;gmail.com&gt;,
Mark E. Haase &lt;mehaase&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Inactive draft that may be taken up again at a later time">Deferred</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><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-even">Created<span class="colon">:</span></dt>
<dd class="field-even">30-Oct-2016</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.8</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even">05-Nov-2016</dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#pep-deferral">PEP Deferral</a></li>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#relationship-with-other-peps">Relationship with other PEPs</a><ul>
<li><a class="reference internal" href="#pep-531-existence-checking-protocol">PEP 531: Existence checking protocol</a></li>
<li><a class="reference internal" href="#pep-505-none-aware-operators">PEP 505: None-aware operators</a></li>
<li><a class="reference internal" href="#pep-335-overloadable-boolean-operators">PEP 335: Overloadable Boolean operators</a></li>
<li><a class="reference internal" href="#pep-535-rich-comparison-chaining">PEP 535: Rich comparison chaining</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#the-circuit-breaking-protocol-if-else">The circuit breaking protocol (<code class="docutils literal notranslate"><span class="pre">if-else</span></code>)</a></li>
<li><a class="reference internal" href="#circuit-breaking-operators-binary-if-and-binary-else">Circuit breaking operators (binary <code class="docutils literal notranslate"><span class="pre">if</span></code> and binary <code class="docutils literal notranslate"><span class="pre">else</span></code>)</a></li>
<li><a class="reference internal" href="#overloading-logical-inversion-not">Overloading logical inversion (<code class="docutils literal notranslate"><span class="pre">not</span></code>)</a></li>
<li><a class="reference internal" href="#forcing-short-circuiting-behaviour">Forcing short-circuiting behaviour</a></li>
<li><a class="reference internal" href="#circuit-breaking-identity-comparisons-is-and-is-not">Circuit breaking identity comparisons (<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>)</a></li>
<li><a class="reference internal" href="#truth-checking-comparisons">Truth checking comparisons</a></li>
<li><a class="reference internal" href="#none-aware-operators">None-aware operators</a></li>
<li><a class="reference internal" href="#rich-chained-comparisons">Rich chained comparisons</a></li>
<li><a class="reference internal" href="#other-conditional-constructs">Other conditional constructs</a></li>
<li><a class="reference internal" href="#style-guide-recommendations">Style guide recommendations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#adding-new-operators">Adding new operators</a></li>
<li><a class="reference internal" href="#naming-the-operator-and-protocol">Naming the operator and protocol</a></li>
<li><a class="reference internal" href="#using-existing-keywords">Using existing keywords</a></li>
<li><a class="reference internal" href="#naming-the-protocol-methods">Naming the protocol methods</a></li>
<li><a class="reference internal" href="#making-binary-if-right-associative">Making binary <code class="docutils literal notranslate"><span class="pre">if</span></code> right-associative</a></li>
<li><a class="reference internal" href="#naming-the-standard-circuit-breakers">Naming the standard circuit breakers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#risks-and-concerns">Risks and concerns</a></li>
<li><a class="reference internal" href="#design-discussion">Design Discussion</a><ul>
<li><a class="reference internal" href="#protocol-walk-through">Protocol walk-through</a></li>
<li><a class="reference internal" href="#respecting-de-morgan-s-laws">Respecting De Morgans Laws</a></li>
<li><a class="reference internal" href="#arbitrary-sentinel-objects">Arbitrary sentinel objects</a></li>
<li><a class="reference internal" href="#implicitly-defined-circuit-breakers-in-circuit-breaking-expressions">Implicitly defined circuit breakers in circuit breaking expressions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="pep-deferral">
<h2><a class="toc-backref" href="#pep-deferral" role="doc-backlink">PEP Deferral</a></h2>
<p>Further consideration of this PEP has been deferred until Python 3.8 at the
earliest.</p>
</section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>Inspired by <a class="pep reference internal" href="../pep-0335/" title="PEP 335 Overloadable Boolean Operators">PEP 335</a>, <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a>, <a class="pep reference internal" href="../pep-0531/" title="PEP 531 Existence checking operators">PEP 531</a>, and the related discussions, this PEP
proposes the definition of a new circuit breaking protocol (using the
method names <code class="docutils literal notranslate"><span class="pre">__then__</span></code> and <code class="docutils literal notranslate"><span class="pre">__else__</span></code>) that provides a common underlying
semantic foundation for:</p>
<ul class="simple">
<li>conditional expressions: <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">if</span> <span class="pre">COND</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
<li>logical conjunction: <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">and</span> <span class="pre">RHS</span></code></li>
<li>logical disjunction: <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">or</span> <span class="pre">RHS</span></code></li>
<li>the None-aware operators proposed in <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a></li>
<li>the rich comparison chaining model proposed in <a class="pep reference internal" href="../pep-0535/" title="PEP 535 Rich comparison chaining">PEP 535</a></li>
</ul>
<p>Taking advantage of the new protocol, it further proposes that the definition
of conditional expressions be revised to also permit the use of <code class="docutils literal notranslate"><span class="pre">if</span></code> and
<code class="docutils literal notranslate"><span class="pre">else</span></code> respectively as right-associative and left-associative general
purpose short-circuiting operators:</p>
<ul class="simple">
<li>Right-associative short-circuiting: <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">if</span> <span class="pre">RHS</span></code></li>
<li>Left-associative short-circuiting: <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
</ul>
<p>In order to make logical inversion (<code class="docutils literal notranslate"><span class="pre">not</span> <span class="pre">EXPR</span></code>) consistent with the above
changes, it also proposes the introduction of a new logical inversion protocol
(using the method name <code class="docutils literal notranslate"><span class="pre">__not__</span></code>).</p>
<p>To force short-circuiting of a circuit breaker without having to evaluate
the expression creating it twice, a new <code class="docutils literal notranslate"><span class="pre">operator.short_circuit(obj)</span></code>
helper function will be added to the operator module.</p>
<p>Finally, a new standard <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code> type is proposed to decouple
an objects truth value (as used to determine control flow) from the value
it returns from short-circuited circuit breaking expressions, with the
following factory functions added to the operator module to represent
particularly common switching idioms:</p>
<ul class="simple">
<li>switching on <code class="docutils literal notranslate"><span class="pre">bool(obj)</span></code>: <code class="docutils literal notranslate"><span class="pre">operator.true(obj)</span></code></li>
<li>switching on <code class="docutils literal notranslate"><span class="pre">not</span> <span class="pre">bool(obj)</span></code>: <code class="docutils literal notranslate"><span class="pre">operator.false(obj)</span></code></li>
<li>switching on <code class="docutils literal notranslate"><span class="pre">obj</span> <span class="pre">is</span> <span class="pre">value</span></code>: <code class="docutils literal notranslate"><span class="pre">operator.is_sentinel(obj,</span> <span class="pre">value)</span></code></li>
<li>switching on <code class="docutils literal notranslate"><span class="pre">obj</span> <span class="pre">is</span> <span class="pre">not</span> <span class="pre">value</span></code>: <code class="docutils literal notranslate"><span class="pre">operator.is_not_sentinel(obj,</span> <span class="pre">value)</span></code></li>
</ul>
</section>
<section id="relationship-with-other-peps">
<h2><a class="toc-backref" href="#relationship-with-other-peps" role="doc-backlink">Relationship with other PEPs</a></h2>
<p>This PEP builds on an extended history of work in other proposals. Some of
the key proposals are discussed below.</p>
<section id="pep-531-existence-checking-protocol">
<h3><a class="toc-backref" href="#pep-531-existence-checking-protocol" role="doc-backlink">PEP 531: Existence checking protocol</a></h3>
<p>This PEP is a direct successor to <a class="pep reference internal" href="../pep-0531/" title="PEP 531 Existence checking operators">PEP 531</a>, replacing the existence checking
protocol and the new <code class="docutils literal notranslate"><span class="pre">?then</span></code> and <code class="docutils literal notranslate"><span class="pre">?else</span></code> syntactic operators defined there
with the new circuit breaking protocol and adjustments to conditional
expressions and the <code class="docutils literal notranslate"><span class="pre">not</span></code> operator.</p>
</section>
<section id="pep-505-none-aware-operators">
<h3><a class="toc-backref" href="#pep-505-none-aware-operators" role="doc-backlink">PEP 505: None-aware operators</a></h3>
<p>This PEP complements the None-aware operator proposals in <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a>, by offering
an underlying protocol-driven semantic framework that explains their
short-circuiting behaviour as highly optimised syntactic sugar for particular
uses of conditional expressions.</p>
<p>Given the changes proposed by this PEP:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">??</span> <span class="pre">RHS</span></code> would roughly be <code class="docutils literal notranslate"><span class="pre">is_not_sentinel(LHS,</span> <span class="pre">None)</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">EXPR?.attr</span></code> would roughly be <code class="docutils literal notranslate"><span class="pre">EXPR.attr</span> <span class="pre">if</span> <span class="pre">is_not_sentinel(EXPR,</span> <span class="pre">None)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">EXPR?[key]</span></code> would roughly be <code class="docutils literal notranslate"><span class="pre">EXPR[key]</span> <span class="pre">if</span> <span class="pre">is_not_sentinel(EXPR,</span> <span class="pre">None)</span></code></li>
</ul>
<p>In all three cases, the dedicated syntactic form would be optimised to avoid
actually creating the circuit breaker instance and instead implement the
underlying control flow directly. In the latter two cases, the syntactic form
would also avoid evaluating <code class="docutils literal notranslate"><span class="pre">EXPR</span></code> twice.</p>
<p>This means that while the None-aware operators would remain highly specialised
and specific to None, other sentinel values would still be usable through the
more general protocol-driven proposal in this PEP.</p>
</section>
<section id="pep-335-overloadable-boolean-operators">
<h3><a class="toc-backref" href="#pep-335-overloadable-boolean-operators" role="doc-backlink">PEP 335: Overloadable Boolean operators</a></h3>
<p><a class="pep reference internal" href="../pep-0335/" title="PEP 335 Overloadable Boolean Operators">PEP 335</a> proposed the ability to overload the short-circuiting <code class="docutils literal notranslate"><span class="pre">and</span></code> and
<code class="docutils literal notranslate"><span class="pre">or</span></code> operators directly, with the ability to overload the semantics of
comparison chaining being one of the consequences of that change. The
proposal in an earlier version of this PEP to instead handle the element-wise
comparison use case by changing the semantic definition of comparison chaining
is drawn directly from Guidos rejection of <a class="pep reference internal" href="../pep-0335/" title="PEP 335 Overloadable Boolean Operators">PEP 335</a> <a class="footnote-reference brackets" href="#id4" id="id1">[1]</a>.</p>
<p>However, initial feedback on this PEP indicated that the number of different
proposals that it covered made it difficult to read, so that part of the
proposal has been separated out as <a class="pep reference internal" href="../pep-0535/" title="PEP 535 Rich comparison chaining">PEP 535</a>.</p>
</section>
<section id="pep-535-rich-comparison-chaining">
<h3><a class="toc-backref" href="#pep-535-rich-comparison-chaining" role="doc-backlink">PEP 535: Rich comparison chaining</a></h3>
<p>As noted above, <a class="pep reference internal" href="../pep-0535/" title="PEP 535 Rich comparison chaining">PEP 535</a> is a proposal to build on the circuit breaking protocol
defined in this PEP in order to expand the rich comparison support introduced
in <a class="pep reference internal" href="../pep-0207/" title="PEP 207 Rich Comparisons">PEP 207</a> to also handle comparison chaining operations like
<code class="docutils literal notranslate"><span class="pre">LEFT_BOUND</span> <span class="pre">&lt;</span> <span class="pre">VALUE</span> <span class="pre">&lt;</span> <span class="pre">RIGHT_BOUND</span></code>.</p>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<section id="the-circuit-breaking-protocol-if-else">
<h3><a class="toc-backref" href="#the-circuit-breaking-protocol-if-else" role="doc-backlink">The circuit breaking protocol (<code class="docutils literal notranslate"><span class="pre">if-else</span></code>)</a></h3>
<p>Conditional expressions (<code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">if</span> <span class="pre">COND</span> <span class="pre">else</span> <span class="pre">RHS</span></code>) are currently interpreted
as an expression level equivalent to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">COND</span><span class="p">:</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">LHS</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">RHS</span>
</pre></div>
</div>
<p>This PEP proposes changing that expansion to allow the checked condition to
implement a new “circuit breaking” protocol that allows it to see, and
potentially alter, the result of either or both branches of the expression:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_cb</span> <span class="o">=</span> <span class="n">COND</span>
<span class="n">_type_cb</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">cb</span><span class="p">)</span>
<span class="k">if</span> <span class="n">_cb</span><span class="p">:</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">LHS</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">_type_cb</span><span class="p">,</span> <span class="s2">&quot;__then__&quot;</span><span class="p">):</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">_type_cb</span><span class="o">.</span><span class="n">__then__</span><span class="p">(</span><span class="n">_cb</span><span class="p">,</span> <span class="n">_expr_result</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">RHS</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">_type_cb</span><span class="p">,</span> <span class="s2">&quot;__else__&quot;</span><span class="p">):</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">_type_cb</span><span class="o">.</span><span class="n">__else__</span><span class="p">(</span><span class="n">_cb</span><span class="p">,</span> <span class="n">_expr_result</span><span class="p">)</span>
</pre></div>
</div>
<p>As shown, interpreter implementations would be required to access only the
protocol method needed for the branch of the conditional expression that is
actually executed. Consistent with other protocol methods, the special methods
would be looked up via the circuit breakers type, rather than directly on the
instance.</p>
</section>
<section id="circuit-breaking-operators-binary-if-and-binary-else">
<h3><a class="toc-backref" href="#circuit-breaking-operators-binary-if-and-binary-else" role="doc-backlink">Circuit breaking operators (binary <code class="docutils literal notranslate"><span class="pre">if</span></code> and binary <code class="docutils literal notranslate"><span class="pre">else</span></code>)</a></h3>
<p>The proposed name of the protocol doesnt come from the proposed changes to
the semantics of conditional expressions. Rather, it comes from the proposed
addition of <code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">else</span></code> as general purpose protocol driven
short-circuiting operators to complement the existing <code class="docutils literal notranslate"><span class="pre">True</span></code> and <code class="docutils literal notranslate"><span class="pre">False</span></code>
based short-circuiting operators (<code class="docutils literal notranslate"><span class="pre">or</span></code> and <code class="docutils literal notranslate"><span class="pre">and</span></code>, respectively) as well
as the <code class="docutils literal notranslate"><span class="pre">None</span></code> based short-circuiting operator proposed in <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a> (<code class="docutils literal notranslate"><span class="pre">??</span></code>).</p>
<p>Together, these two operators would be known as the circuit breaking operators.</p>
<p>In order to support this usage, the definition of conditional expressions in
the language grammar would be updated to make both the <code class="docutils literal notranslate"><span class="pre">if</span></code> clause and
the <code class="docutils literal notranslate"><span class="pre">else</span></code> clause optional:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">test</span><span class="p">:</span> <span class="n">else_test</span> <span class="p">[</span><span class="s1">&#39;if&#39;</span> <span class="n">or_test</span> <span class="p">[</span><span class="s1">&#39;else&#39;</span> <span class="n">test</span><span class="p">]]</span> <span class="o">|</span> <span class="n">lambdef</span>
<span class="n">else_test</span><span class="p">:</span> <span class="n">or_test</span> <span class="p">[</span><span class="s1">&#39;else&#39;</span> <span class="n">test</span><span class="p">]</span>
</pre></div>
</div>
<p>Note that we would need to avoid the apparent simplification to
<code class="docutils literal notranslate"><span class="pre">else_test</span> <span class="pre">('if'</span> <span class="pre">else_test)*</span></code> in order to make it easier for compiler
implementations to correctly preserve the semantics of normal conditional
expressions.</p>
<p>The definition of the <code class="docutils literal notranslate"><span class="pre">test_nocond</span></code> node in the grammar (which deliberately
excludes conditional expressions) would remain unchanged, so the circuit
breaking operators would require parentheses when used in the <code class="docutils literal notranslate"><span class="pre">if</span></code>
clause of comprehensions and generator expressions just as conditional
expressions themselves do.</p>
<p>This grammar definition means precedence/associativity in the otherwise
ambiguous case of <code class="docutils literal notranslate"><span class="pre">expr1</span> <span class="pre">if</span> <span class="pre">cond</span> <span class="pre">else</span> <span class="pre">expr2</span> <span class="pre">else</span> <span class="pre">expr3</span></code> resolves as
<code class="docutils literal notranslate"><span class="pre">(expr1</span> <span class="pre">if</span> <span class="pre">cond</span> <span class="pre">else</span> <span class="pre">expr2)</span> <span class="pre">else</span> <span class="pre">epxr3</span></code>. However, a guideline will also be
added to <a class="pep reference internal" href="../pep-0008/" title="PEP 8 Style Guide for Python Code">PEP 8</a> to say “dont do that”, as such a construct will be inherently
confusing for readers, regardless of how the interpreter executes it.</p>
<p>The right-associative circuit breaking operator (<code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">if</span> <span class="pre">RHS</span></code>) would then
be expanded as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_cb</span> <span class="o">=</span> <span class="n">RHS</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">LHS</span> <span class="k">if</span> <span class="n">_cb</span> <span class="k">else</span> <span class="n">_cb</span>
</pre></div>
</div>
<p>While the left-associative circuit breaking operator (<code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">else</span> <span class="pre">RHS</span></code>) would
be expanded as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_cb</span> <span class="o">=</span> <span class="n">LHS</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">_cb</span> <span class="k">if</span> <span class="n">_cb</span> <span class="k">else</span> <span class="n">RHS</span>
</pre></div>
</div>
<p>The key point to note in both cases is that when the circuit breaking
expression short-circuits, the condition expression is used as the result of
the expression <em>unless</em> the condition is a circuit breaker. In the latter
case, the appropriate circuit breaker protocol method is called as usual, but
the circuit breaker itself is supplied as the method argument.</p>
<p>This allows circuit breakers to reliably detect short-circuiting by checking
for cases when the argument passed in as the candidate expression result is
<code class="docutils literal notranslate"><span class="pre">self</span></code>.</p>
</section>
<section id="overloading-logical-inversion-not">
<h3><a class="toc-backref" href="#overloading-logical-inversion-not" role="doc-backlink">Overloading logical inversion (<code class="docutils literal notranslate"><span class="pre">not</span></code>)</a></h3>
<p>Any circuit breaker definition will have a logical inverse that is still a
circuit breaker, but inverts the answer as to when to short circuit the
expression evaluation. For example, the <code class="docutils literal notranslate"><span class="pre">operator.true</span></code> and
<code class="docutils literal notranslate"><span class="pre">operator.false</span></code> circuit breakers proposed in this PEP are each others
logical inverse.</p>
<p>A new protocol method, <code class="docutils literal notranslate"><span class="pre">__not__(self)</span></code>, will be introduced to permit circuit
breakers and other types to override <code class="docutils literal notranslate"><span class="pre">not</span></code> expressions to return their
logical inverse rather than a coerced boolean result.</p>
<p>To preserve the semantics of existing language optimisations (such as
eliminating double negations directly in a boolean context as redundant),
<code class="docutils literal notranslate"><span class="pre">__not__</span></code> implementations will be required to respect the following
invariant:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="ow">not</span> <span class="nb">bool</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="o">==</span> <span class="nb">bool</span><span class="p">(</span><span class="ow">not</span> <span class="n">obj</span><span class="p">)</span>
</pre></div>
</div>
<p>However, symmetric circuit breakers (those that implement all of <code class="docutils literal notranslate"><span class="pre">__bool__</span></code>,
<code class="docutils literal notranslate"><span class="pre">__not__</span></code>, <code class="docutils literal notranslate"><span class="pre">__then__</span></code> and <code class="docutils literal notranslate"><span class="pre">__else__</span></code>) would only be expected to respect
the full semantics of boolean logic when all circuit breakers involved in the
expression are using a consistent definition of “truth”. This is covered
further in <a class="reference internal" href="#respecting-de-morgan-s-laws">Respecting De Morgans Laws</a>.</p>
</section>
<section id="forcing-short-circuiting-behaviour">
<h3><a class="toc-backref" href="#forcing-short-circuiting-behaviour" role="doc-backlink">Forcing short-circuiting behaviour</a></h3>
<p>Invocation of a circuit breakers short-circuiting behaviour can be forced by
using it as all three operands in a conditional expression:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span> <span class="k">if</span> <span class="n">obj</span> <span class="k">else</span> <span class="n">obj</span>
</pre></div>
</div>
<p>Or, equivalently, as both operands in a circuit breaking expression:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span> <span class="k">if</span> <span class="n">obj</span>
<span class="n">obj</span> <span class="k">else</span> <span class="n">obj</span>
</pre></div>
</div>
<p>Rather than requiring the using of any of these patterns, this PEP proposes
to add a dedicated function to the <code class="docutils literal notranslate"><span class="pre">operator</span></code> to explicitly short-circuit
a circuit breaker, while passing other objects through unmodified:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">short_circuit</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Replace circuit breakers with their short-circuited result</span>
<span class="sd"> Passes other input values through unmodified.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">obj</span> <span class="k">if</span> <span class="n">obj</span> <span class="k">else</span> <span class="n">obj</span>
</pre></div>
</div>
</section>
<section id="circuit-breaking-identity-comparisons-is-and-is-not">
<h3><a class="toc-backref" href="#circuit-breaking-identity-comparisons-is-and-is-not" role="doc-backlink">Circuit breaking identity comparisons (<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>)</a></h3>
<p>In the absence of any standard circuit breakers, the proposed <code class="docutils literal notranslate"><span class="pre">if</span></code> and
<code class="docutils literal notranslate"><span class="pre">else</span></code> operators would largely just be unusual spellings of the existing
<code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> logical operators.</p>
<p>However, this PEP further proposes to provide a new general purpose
<code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code> type that implements the appropriate short
circuiting logic, as well as factory functions in the operator module
that correspond to 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>
<p>These would be defined in such a way that the following expressions produce
<code class="docutils literal notranslate"><span class="pre">VALUE</span></code> rather than <code class="docutils literal notranslate"><span class="pre">False</span></code> when the conditional check fails:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">EXPR</span> <span class="k">if</span> <span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span>
<span class="n">EXPR</span> <span class="k">if</span> <span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span>
</pre></div>
</div>
<p>And similarly, these would produce <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> rather than <code class="docutils literal notranslate"><span class="pre">True</span></code> when the
conditional check succeeds:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
<span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
</pre></div>
</div>
<p>In effect, these comparisons would be defined such that the leading
<code class="docutils literal notranslate"><span class="pre">VALUE</span> <span class="pre">if</span></code> and trailing <code class="docutils literal notranslate"><span class="pre">else</span> <span class="pre">VALUE</span></code> clauses can be omitted as implied in
expressions of the following forms:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># To handle &quot;if&quot; expressions, &quot; else VALUE&quot; is implied when omitted</span>
<span class="n">EXPR</span> <span class="k">if</span> <span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">VALUE</span>
<span class="n">EXPR</span> <span class="k">if</span> <span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">VALUE</span>
<span class="c1"># To handle &quot;else&quot; expressions, &quot;VALUE if &quot; is implied when omitted</span>
<span class="n">VALUE</span> <span class="k">if</span> <span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
<span class="n">VALUE</span> <span class="k">if</span> <span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
</pre></div>
</div>
<p>The proposed <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code> type would represent this behaviour
programmatically as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CircuitBreaker</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Simple circuit breaker type&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">bool_value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bool_value</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">bool_value</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__bool__</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">bool_value</span>
<span class="k">def</span> <span class="nf">__not__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">CircuitBreaker</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">bool_value</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__then__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">result</span><span class="p">):</span>
<span class="k">if</span> <span class="n">result</span> <span class="ow">is</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">value</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">def</span> <span class="nf">__else__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">result</span><span class="p">):</span>
<span class="k">if</span> <span class="n">result</span> <span class="ow">is</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">value</span>
<span class="k">return</span> <span class="n">result</span>
</pre></div>
</div>
<p>The key characteristic of these circuit breakers is that they are <em>ephemeral</em>:
when they are told that short circuiting has taken place (by receiving a
reference to themselves as the candidate expression result), they return the
original value, rather than the circuit breaking wrapper.</p>
<p>The short-circuiting detection is defined such that the wrapper will always
be removed if you explicitly pass the same circuit breaker instance to both
sides of a circuit breaking operator or use one as all three operands in a
conditional expression:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">breaker</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">foo</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">operator</span><span class="o">.</span><span class="n">short_circuit</span><span class="p">(</span><span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">if</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">else</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">if</span> <span class="n">breaker</span> <span class="k">else</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="n">breaker</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">foo</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">operator</span><span class="o">.</span><span class="n">short_circuit</span><span class="p">(</span><span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">if</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">else</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
<span class="k">assert</span> <span class="p">(</span><span class="n">breaker</span> <span class="k">if</span> <span class="n">breaker</span> <span class="k">else</span> <span class="n">breaker</span><span class="p">)</span> <span class="ow">is</span> <span class="n">foo</span>
</pre></div>
</div>
<p>The factory functions in the <code class="docutils literal notranslate"><span class="pre">operator</span></code> module would then make it
straightforward to create circuit breakers that correspond to identity
checks 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="k">def</span> <span class="nf">is_sentinel</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">sentinel</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns a circuit breaker switching on &#39;value is sentinel&#39;&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">sentinel</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">is_not_sentinel</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">sentinel</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns a circuit breaker switching on &#39;value is not sentinel&#39;&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">sentinel</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="truth-checking-comparisons">
<h3><a class="toc-backref" href="#truth-checking-comparisons" role="doc-backlink">Truth checking comparisons</a></h3>
<p>Due to their short-circuiting nature, the runtime logic underlying the <code class="docutils literal notranslate"><span class="pre">and</span></code>
and <code class="docutils literal notranslate"><span class="pre">or</span></code> operators has never previously been accessible through the
<code class="docutils literal notranslate"><span class="pre">operator</span></code> or <code class="docutils literal notranslate"><span class="pre">types</span></code> modules.</p>
<p>The introduction of circuit breaking operators and circuit breakers allows
that logic to be captured in the operator module as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">true</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns a circuit breaker switching on &#39;bool(value)&#39;&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">bool</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">false</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns a circuit breaker switching on &#39;not bool(value)&#39;&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">CircuitBreaker</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="ow">not</span> <span class="nb">bool</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">or</span> <span class="pre">RHS</span></code> would be effectively <code class="docutils literal notranslate"><span class="pre">true(LHS)</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">and</span> <span class="pre">RHS</span></code> would be effectively <code class="docutils literal notranslate"><span class="pre">false(LHS)</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
</ul>
<p>No actual change would take place in these operator definitions, the new
circuit breaking protocol and operators would just provide a way to make the
control flow logic programmable, rather than hardcoding the sense of the check
at development time.</p>
<p>Respecting the rules of boolean logic, these expressions could also be
expanded in their inverted form by using the right-associative circuit
breaking operator instead:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">or</span> <span class="pre">RHS</span></code> would be effectively <code class="docutils literal notranslate"><span class="pre">RHS</span> <span class="pre">if</span> <span class="pre">false(LHS)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">and</span> <span class="pre">RHS</span></code> would be effectively <code class="docutils literal notranslate"><span class="pre">RHS</span> <span class="pre">if</span> <span class="pre">true(LHS)</span></code></li>
</ul>
</section>
<section id="none-aware-operators">
<h3><a class="toc-backref" href="#none-aware-operators" role="doc-backlink">None-aware operators</a></h3>
<p>If both this PEP and <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a>s None-aware operators were accepted, then the
proposed <code class="docutils literal notranslate"><span class="pre">is_sentinel</span></code> and <code class="docutils literal notranslate"><span class="pre">is_not_sentinel</span></code> circuit breaker factories
would be used to encapsulate the notion of “None checking”: seeing if a value
is <code class="docutils literal notranslate"><span class="pre">None</span></code> and either falling back to an alternative value (an operation known
as “None-coalescing”) or passing it through as the result of the overall
expression (an operation known as “None-severing” or “None-propagating”).</p>
<p>Given these circuit breakers, <code class="docutils literal notranslate"><span class="pre">LHS</span> <span class="pre">??</span> <span class="pre">RHS</span></code> would be roughly equivalent to
both of the following:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">is_not_sentinel(LHS,</span> <span class="pre">None)</span> <span class="pre">else</span> <span class="pre">RHS</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">RHS</span> <span class="pre">if</span> <span class="pre">is_sentinel(LHS,</span> <span class="pre">None)</span></code></li>
</ul>
<p>Due to the way they inject control flow into attribute lookup and subscripting
operations, None-aware attribute access and None-aware subscripting cant be
expressed directly in terms of the circuit breaking operators, but they can
still be defined in terms of the underlying circuit breaking protocol.</p>
<p>In those terms, <code class="docutils literal notranslate"><span class="pre">EXPR?.ATTR[KEY].SUBATTR()</span></code> would be semantically
equivalent to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_lookup_base</span> <span class="o">=</span> <span class="n">EXPR</span>
<span class="n">_circuit_breaker</span> <span class="o">=</span> <span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">_lookup_base</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">_lookup_base</span><span class="o">.</span><span class="n">ATTR</span><span class="p">[</span><span class="n">KEY</span><span class="p">]</span><span class="o">.</span><span class="n">SUBATTR</span><span class="p">()</span> <span class="k">if</span> <span class="n">_circuit_breaker</span>
</pre></div>
</div>
<p>Similarly, <code class="docutils literal notranslate"><span class="pre">EXPR?[KEY].ATTR.SUBATTR()</span></code> would be semantically equivalent
to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_lookup_base</span> <span class="o">=</span> <span class="n">EXPR</span>
<span class="n">_circuit_breaker</span> <span class="o">=</span> <span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">_lookup_base</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">_expr_result</span> <span class="o">=</span> <span class="n">_lookup_base</span><span class="p">[</span><span class="n">KEY</span><span class="p">]</span><span class="o">.</span><span class="n">ATTR</span><span class="o">.</span><span class="n">SUBATTR</span><span class="p">()</span> <span class="k">if</span> <span class="n">_circuit_breaker</span>
</pre></div>
</div>
<p>The actual implementations of the None-aware operators would presumably be
optimised to skip actually creating the circuit breaker instance, but the
above expansions would still provide an accurate description of the observable
behaviour of the operators at runtime.</p>
</section>
<section id="rich-chained-comparisons">
<h3><a class="toc-backref" href="#rich-chained-comparisons" role="doc-backlink">Rich chained comparisons</a></h3>
<p>Refer to <a class="pep reference internal" href="../pep-0535/" title="PEP 535 Rich comparison chaining">PEP 535</a> for a detailed discussion of this possible use case.</p>
</section>
<section id="other-conditional-constructs">
<h3><a class="toc-backref" href="#other-conditional-constructs" role="doc-backlink">Other conditional constructs</a></h3>
<p>No changes are proposed to if statements, while statements, comprehensions,
or generator expressions, as the boolean clauses they contain are used
entirely for control flow purposes and never return a result as such.</p>
<p>However, its worth noting that while such proposals are outside the scope of
this PEP, the circuit breaking protocol defined here would already be
sufficient to support constructs like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">is_not_none</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">return</span> <span class="n">is_sentinel</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">while</span> <span class="n">is_not_none</span><span class="p">(</span><span class="n">dynamic_query</span><span class="p">())</span> <span class="k">as</span> <span class="n">result</span><span class="p">:</span>
<span class="o">...</span> <span class="c1"># Code using result</span>
</pre></div>
</div>
<p>and:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">is_not_none</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span> <span class="k">as</span> <span class="n">match</span><span class="p">:</span>
<span class="o">...</span> <span class="c1"># Code using match</span>
</pre></div>
</div>
<p>This could be done by assigning the result of
<code class="docutils literal notranslate"><span class="pre">operator.short_circuit(CONDITION)</span></code> to the name given in the <code class="docutils literal notranslate"><span class="pre">as</span></code> clause,
rather than assigning <code class="docutils literal notranslate"><span class="pre">CONDITION</span></code> to the given name directly.</p>
</section>
<section id="style-guide-recommendations">
<h3><a class="toc-backref" href="#style-guide-recommendations" role="doc-backlink">Style guide recommendations</a></h3>
<p>The following additions to <a class="pep reference internal" href="../pep-0008/" title="PEP 8 Style Guide for Python Code">PEP 8</a> are proposed in relation to the new features
introduced by this PEP:</p>
<ul class="simple">
<li>Avoid combining conditional expressions (<code class="docutils literal notranslate"><span class="pre">if-else</span></code>) and the standalone
circuit breaking operators (<code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">else</span></code>) in a single expression -
use one or the other depending on the situation, but not both.</li>
<li>Avoid using conditional expressions (<code class="docutils literal notranslate"><span class="pre">if-else</span></code>) and the standalone
circuit breaking operators (<code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">else</span></code>) as part of <code class="docutils literal notranslate"><span class="pre">if</span></code>
conditions in <code class="docutils literal notranslate"><span class="pre">if</span></code> statements and the filter clauses of comprehensions
and generator expressions.</li>
</ul>
</section>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<section id="adding-new-operators">
<h3><a class="toc-backref" href="#adding-new-operators" role="doc-backlink">Adding new operators</a></h3>
<p>Similar to <a class="pep reference internal" href="../pep-0335/" title="PEP 335 Overloadable Boolean Operators">PEP 335</a>, early drafts of this PEP focused on making the existing
<code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> operators less rigid in their interpretation, rather than
proposing new operators. However, this proved to be problematic for a few key
reasons:</p>
<ul class="simple">
<li>the <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> operators have a long established and stable meaning,
so readers would inevitably be surprised if their meaning now became
dependent on the type of the left operand. Even new users would be confused
by this change due to 25+ years of teaching material that assumes the
current well-known semantics for these operators</li>
<li>Python interpreter implementations, including CPython, have taken advantage
of the existing semantics of <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> when defining runtime and
compile time optimisations, which would all need to be reviewed and
potentially discarded if the semantics of those operations changed</li>
<li>it isnt clear what names would be appropriate for the new methods needed
to define the protocol</li>
</ul>
<p>Proposing short-circuiting binary variants of the existing <code class="docutils literal notranslate"><span class="pre">if-else</span></code> ternary
operator instead resolves all of those issues:</p>
<ul class="simple">
<li>the runtime semantics of <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> remain entirely unchanged</li>
<li>while the semantics of the unary <code class="docutils literal notranslate"><span class="pre">not</span></code> operator do change, the invariant
required of <code class="docutils literal notranslate"><span class="pre">__not__</span></code> implementations means that existing expression
optimisations in boolean contexts will remain valid.</li>
<li><code class="docutils literal notranslate"><span class="pre">__else__</span></code> is the short-circuiting outcome for <code class="docutils literal notranslate"><span class="pre">if</span></code> expressions due to
the absence of a trailing <code class="docutils literal notranslate"><span class="pre">else</span></code> clause</li>
<li><code class="docutils literal notranslate"><span class="pre">__then__</span></code> is the short-circuiting outcome for <code class="docutils literal notranslate"><span class="pre">else</span></code> expressions due to
the absence of a leading <code class="docutils literal notranslate"><span class="pre">if</span></code> clause (this connection would be even clearer
if the method name was <code class="docutils literal notranslate"><span class="pre">__if__</span></code>, but that would be ambiguous given the
other uses of the <code class="docutils literal notranslate"><span class="pre">if</span></code> keyword that wont invoke the circuit breaking
protocol)</li>
</ul>
</section>
<section id="naming-the-operator-and-protocol">
<h3><a class="toc-backref" href="#naming-the-operator-and-protocol" role="doc-backlink">Naming the operator and protocol</a></h3>
<p>The names “circuit breaking operator”, “circuit breaking protocol” and
“circuit breaker” are all inspired by the phrase “short circuiting operator”:
the general language design term for operators that only conditionally
evaluate their right operand.</p>
<p>The electrical analogy is that circuit breakers in Python detect and handle
short circuits in expressions before they trigger any exceptions similar to the
way that circuit breakers detect and handle short circuits in electrical
systems before they damage any equipment or harm any humans.</p>
<p>The Python level analogy is that just as a <code class="docutils literal notranslate"><span class="pre">break</span></code> statement lets you
terminate a loop before it reaches its natural conclusion, a circuit breaking
expression lets you terminate evaluation of the expression and produce a result
immediately.</p>
</section>
<section id="using-existing-keywords">
<h3><a class="toc-backref" href="#using-existing-keywords" role="doc-backlink">Using existing keywords</a></h3>
<p>Using existing keywords has the benefit of allowing the new operators to
be introduced without a <code class="docutils literal notranslate"><span class="pre">__future__</span></code> statement.</p>
<p><code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">else</span></code> are semantically appropriate for the proposed new protocol,
and the only additional syntactic ambiguity introduced arises when the new
operators are combined with the explicit <code class="docutils literal notranslate"><span class="pre">if-else</span></code> conditional expression
syntax.</p>
<p>The PEP handles that ambiguity by explicitly specifying how it should be
handled by interpreter implementers, but proposing to point out in <a class="pep reference internal" href="../pep-0008/" title="PEP 8 Style Guide for Python Code">PEP 8</a>
that even though interpreters will understand it, human readers probably
wont, and hence it wont be a good idea to use both conditional expressions
and the circuit breaking operators in a single expression.</p>
</section>
<section id="naming-the-protocol-methods">
<h3><a class="toc-backref" href="#naming-the-protocol-methods" role="doc-backlink">Naming the protocol methods</a></h3>
<p>Naming the <code class="docutils literal notranslate"><span class="pre">__else__</span></code> method was straightforward, as reusing the operator
keyword name results in a special method name that is both obvious and
unambiguous.</p>
<p>Naming the <code class="docutils literal notranslate"><span class="pre">__then__</span></code> method was less straightforward, as there was another
possible option in using the keyword-based name <code class="docutils literal notranslate"><span class="pre">__if__</span></code>.</p>
<p>The problem with <code class="docutils literal notranslate"><span class="pre">__if__</span></code> is that there would continue to be many cases
where the <code class="docutils literal notranslate"><span class="pre">if</span></code> keyword appeared, with an expression to its immediate right,
but the <code class="docutils literal notranslate"><span class="pre">__if__</span></code> special method would not be invoked. Instead, the
<code class="docutils literal notranslate"><span class="pre">bool()</span></code> builtin and its underlying special methods (<code class="docutils literal notranslate"><span class="pre">__bool__</span></code>,
<code class="docutils literal notranslate"><span class="pre">__len__</span></code>) would be invoked, while <code class="docutils literal notranslate"><span class="pre">__if__</span></code> had no effect.</p>
<p>With the boolean protocol already playing a part in conditional expressions and
the new circuit breaking protocol, the less ambiguous name <code class="docutils literal notranslate"><span class="pre">__then__</span></code> was
chosen based on the terminology commonly used in computer science and
programming language design to describe the first clause of an <code class="docutils literal notranslate"><span class="pre">if</span></code>
statement.</p>
</section>
<section id="making-binary-if-right-associative">
<h3><a class="toc-backref" href="#making-binary-if-right-associative" role="doc-backlink">Making binary <code class="docutils literal notranslate"><span class="pre">if</span></code> right-associative</a></h3>
<p>The precedent set by conditional expressions means that a binary
short-circuiting <code class="docutils literal notranslate"><span class="pre">if</span></code> expression must necessarily have the condition on the
right as a matter of consistency.</p>
<p>With the right operand always being evaluated first, and the left operand not
being evaluated at all if the right operand is true in a boolean context,
the natural outcome is a right-associative operator.</p>
</section>
<section id="naming-the-standard-circuit-breakers">
<h3><a class="toc-backref" href="#naming-the-standard-circuit-breakers" role="doc-backlink">Naming the standard circuit breakers</a></h3>
<p>When used solely with the left-associative circuit breaking operator,
explicit circuit breaker names for unary checks read well if they start with
the preposition <code class="docutils literal notranslate"><span class="pre">if_</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">operator</span><span class="o">.</span><span class="n">if_true</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span>
<span class="n">operator</span><span class="o">.</span><span class="n">if_false</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span>
</pre></div>
</div>
<p>However, incorporating the <code class="docutils literal notranslate"><span class="pre">if_</span></code> doesnt read as well when performing
logical inversion:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">if_true</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span>
<span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">if_false</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span>
</pre></div>
</div>
<p>Or when using the right-associative circuit breaking operator:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">LHS</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">if_true</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span>
<span class="n">LHS</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">if_false</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span>
</pre></div>
</div>
<p>Or when naming a binary comparison operation:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">operator</span><span class="o">.</span><span class="n">if_is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
<span class="n">operator</span><span class="o">.</span><span class="n">if_is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
</pre></div>
</div>
<p>By contrast, omitting the preposition from the circuit breaker name gives a
result that reads reasonably well in all forms for unary checks:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">operator</span><span class="o">.</span><span class="n">true</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span> <span class="c1"># Preceding &quot;LHS if &quot; implied</span>
<span class="n">operator</span><span class="o">.</span><span class="n">false</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span> <span class="c1"># Preceding &quot;LHS if &quot; implied</span>
<span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">true</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span> <span class="c1"># Preceding &quot;LHS if &quot; implied</span>
<span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">false</span><span class="p">(</span><span class="n">LHS</span><span class="p">)</span> <span class="k">else</span> <span class="n">RHS</span> <span class="c1"># Preceding &quot;LHS if &quot; implied</span>
<span class="n">LHS</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">true</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span> <span class="c1"># Trailing &quot; else RHS&quot; implied</span>
<span class="n">LHS</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">false</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span> <span class="c1"># Trailing &quot; else RHS&quot; implied</span>
<span class="n">LHS</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">true</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span> <span class="c1"># Trailing &quot; else RHS&quot; implied</span>
<span class="n">LHS</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">operator</span><span class="o">.</span><span class="n">false</span><span class="p">(</span><span class="n">RHS</span><span class="p">)</span> <span class="c1"># Trailing &quot; else RHS&quot; implied</span>
</pre></div>
</div>
<p>And also reads well for binary checks:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">operator</span><span class="o">.</span><span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
<span class="n">operator</span><span class="o">.</span><span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span> <span class="k">else</span> <span class="n">EXPR</span>
<span class="n">EXPR</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">is_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span>
<span class="n">EXPR</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">VALUE</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">)</span>
</pre></div>
</div>
</section>
</section>
<section id="risks-and-concerns">
<h2><a class="toc-backref" href="#risks-and-concerns" role="doc-backlink">Risks and concerns</a></h2>
<p>This PEP has been designed specifically to address the risks and concerns
raised when discussing PEPs 335, 505 and 531.</p>
<ul class="simple">
<li>it defines new operators and adjusts the definition of chained comparison
(in a separate PEP) rather than impacting the existing <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code>
operators</li>
<li>the proposed new operators are general purpose short-circuiting binary
operators that can even be used to express the existing semantics of <code class="docutils literal notranslate"><span class="pre">and</span></code>
and <code class="docutils literal notranslate"><span class="pre">or</span></code> rather than focusing solely and inflexibly on identity checking
against <code class="docutils literal notranslate"><span class="pre">None</span></code></li>
<li>the changes to the <code class="docutils literal notranslate"><span class="pre">not</span></code> unary operator and 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>
binary comparison operators are defined in such a way that control flow
optimisations based on the existing semantics remain valid</li>
</ul>
<p>One consequence of this approach is that this PEP <em>on its own</em> doesnt produce
much in the way of direct benefits to end users aside from making it possible
to omit some common <code class="docutils literal notranslate"><span class="pre">None</span> <span class="pre">if</span></code> prefixes and <code class="docutils literal notranslate"><span class="pre">else</span> <span class="pre">None</span></code> suffixes from
particular forms of conditional expression.</p>
<p>Instead, what it mainly provides is a common foundation that would allow the
None-aware operator proposals in <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a> and the rich comparison chaining
proposal in <a class="pep reference internal" href="../pep-0535/" title="PEP 535 Rich comparison chaining">PEP 535</a> to be pursued atop a common underlying semantic framework
that would also be shared with conditional expressions and the existing <code class="docutils literal notranslate"><span class="pre">and</span></code>
and <code class="docutils literal notranslate"><span class="pre">or</span></code> operators.</p>
</section>
<section id="design-discussion">
<h2><a class="toc-backref" href="#design-discussion" role="doc-backlink">Design Discussion</a></h2>
<section id="protocol-walk-through">
<h3><a class="toc-backref" href="#protocol-walk-through" role="doc-backlink">Protocol walk-through</a></h3>
<p>The following diagram illustrates the core concepts behind the circuit
breaking protocol (although it glosses over the technical detail of looking
up the special methods via the type rather than the instance):</p>
<img alt="diagram of circuit breaking protocol applied to ternary expression" class="invert-in-dark-mode" src="../_images/circuit-breaking-protocol.svg" /><p>We will work through the following expression:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">is_not_none</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="n">operator</span><span class="o">.</span><span class="n">is_not_sentinel</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="k">if</span> <span class="n">is_not_none</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">))</span> <span class="k">else</span> <span class="n">y</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">is_not_none</span></code> is a helper function that invokes the proposed
<code class="docutils literal notranslate"><span class="pre">operator.is_not_sentinel</span></code> <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code> factory with <code class="docutils literal notranslate"><span class="pre">None</span></code> as
the sentinel value. <code class="docutils literal notranslate"><span class="pre">data</span></code> is a container (such as a builtin <code class="docutils literal notranslate"><span class="pre">dict</span></code>
instance) that returns <code class="docutils literal notranslate"><span class="pre">None</span></code> when the <code class="docutils literal notranslate"><span class="pre">get()</span></code> method is called with an
unknown key.</p>
<p>We can rewrite the example to give a name to the circuit breaker instance:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">maybe_value</span> <span class="o">=</span> <span class="n">is_not_none</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">))</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="k">if</span> <span class="n">maybe_value</span> <span class="k">else</span> <span class="n">y</span>
</pre></div>
</div>
<p>Here the <code class="docutils literal notranslate"><span class="pre">maybe_value</span></code> circuit breaker instance corresponds to <code class="docutils literal notranslate"><span class="pre">breaker</span></code>
in the diagram.</p>
<p>The ternary condition is evaluated by calling <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code>, which is
the same as Pythons existing behavior. The change in behavior is that instead
of directly returning one of the operands <code class="docutils literal notranslate"><span class="pre">x</span></code> or <code class="docutils literal notranslate"><span class="pre">y</span></code>, the circuit breaking
protocol passes the relevant operand to the circuit breaker used in the
condition.</p>
<p>If <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code> evaluates to <code class="docutils literal notranslate"><span class="pre">True</span></code> (i.e. the requested
key exists and its value is not <code class="docutils literal notranslate"><span class="pre">None</span></code>) then the interpreter calls
<code class="docutils literal notranslate"><span class="pre">type(maybe_value).__then__(maybe_value,</span> <span class="pre">x)</span></code>. Otherwise, it calls
<code class="docutils literal notranslate"><span class="pre">type(maybe_value).__else__(maybe_value,</span> <span class="pre">y)</span></code>.</p>
<p>The protocol also applies to the new <code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">else</span></code> binary operators,
but in these cases, the interpreter needs a way to indicate the missing third
operand. It does this by re-using the circuit breaker itself in that role.</p>
<p>Consider these two expressions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="k">if</span> <span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="k">if</span> <span class="n">operator</span><span class="o">.</span><span class="n">is_sentinel</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">),</span> <span class="kc">None</span><span class="p">)</span>
</pre></div>
</div>
<p>The first form of this expression returns <code class="docutils literal notranslate"><span class="pre">x</span></code> if <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span> <span class="pre">is</span> <span class="pre">None</span></code>,
but otherwise returns <code class="docutils literal notranslate"><span class="pre">False</span></code>, which almost certainly isnt what we want.</p>
<p>By contrast, the second form of this expression still returns <code class="docutils literal notranslate"><span class="pre">x</span></code> if
<code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span> <span class="pre">is</span> <span class="pre">None</span></code>, but otherwise returns <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code>, which
is significantly more useful behaviour.</p>
<p>We can understand this behavior by rewriting it as a ternary expression with
an explicitly named circuit breaker instance:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">maybe_value</span> <span class="o">=</span> <span class="n">operator</span><span class="o">.</span><span class="n">is_sentinel</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">),</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="k">if</span> <span class="n">maybe_value</span> <span class="k">else</span> <span class="n">maybe_value</span>
</pre></div>
</div>
<p>If <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code> is <code class="docutils literal notranslate"><span class="pre">True</span></code> (i.e. <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>),
then the interpreter calls <code class="docutils literal notranslate"><span class="pre">type(maybe_value).__then__(maybe_value,</span> <span class="pre">x)</span></code>. The
implementation of <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker.__then__</span></code> doesnt see anything that
indicates short-circuiting has taken place, and hence returns <code class="docutils literal notranslate"><span class="pre">x</span></code>.</p>
<p>By contrast, if <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code> is <code class="docutils literal notranslate"><span class="pre">False</span></code> (i.e. <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code>
is <em>not</em> <code class="docutils literal notranslate"><span class="pre">None</span></code>), the interpreter calls
<code class="docutils literal notranslate"><span class="pre">type(maybe_value).__else__(maybe_value,</span> <span class="pre">maybe_value)</span></code>. The implementation of
<code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker.__else__</span></code> detects that the instance method has received
itself as its argument and returns the wrapped value (i.e. <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code>)
rather than the circuit breaker.</p>
<p>The same logic applies to <code class="docutils literal notranslate"><span class="pre">else</span></code>, only reversed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">is_not_none</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">))</span> <span class="k">else</span> <span class="n">y</span>
</pre></div>
</div>
<p>This expression returns <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code> if it is not <code class="docutils literal notranslate"><span class="pre">None</span></code>, otherwise it
evaluates and returns <code class="docutils literal notranslate"><span class="pre">y</span></code>. To understand the mechanics, we rewrite the
expression as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">maybe_value</span> <span class="o">=</span> <span class="n">is_not_none</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">))</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">maybe_value</span> <span class="k">if</span> <span class="n">maybe_value</span> <span class="k">else</span> <span class="n">y</span>
</pre></div>
</div>
<p>If <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code> is <code class="docutils literal notranslate"><span class="pre">True</span></code>, then the expression short-circuits and
the interpreter calls <code class="docutils literal notranslate"><span class="pre">type(maybe_value).__else__(maybe_value,</span> <span class="pre">maybe_value)</span></code>.
The implementation of <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker.__then__</span></code> detects that the
instance method has received itself as its argument and returns the wrapped
value (i.e. <code class="docutils literal notranslate"><span class="pre">data.get(&quot;key&quot;)</span></code>) rather than the circuit breaker.</p>
<p>If <code class="docutils literal notranslate"><span class="pre">bool(maybe_value)</span></code> is <code class="docutils literal notranslate"><span class="pre">True</span></code>, the interpreter calls
<code class="docutils literal notranslate"><span class="pre">type(maybe_value).__else__(maybe_value,</span> <span class="pre">y)</span></code>. The implementation of
<code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker.__else__</span></code> doesnt see anything that indicates
short-circuiting has taken place, and hence returns <code class="docutils literal notranslate"><span class="pre">y</span></code>.</p>
</section>
<section id="respecting-de-morgan-s-laws">
<h3><a class="toc-backref" href="#respecting-de-morgan-s-laws" role="doc-backlink">Respecting De Morgans Laws</a></h3>
<p>Similar to <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code>, the binary short-circuiting operators will
permit multiple ways of writing essentially the same expression. This
seeming redundancy is unfortunately an implied consequence of defining the
protocol as a full boolean algebra, as boolean algebras respect a pair of
properties known as “De Morgans Laws”: the ability to express the results
of <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> operations in terms of each other and a suitable
combination of <code class="docutils literal notranslate"><span class="pre">not</span></code> operations.</p>
<p>For <code class="docutils literal notranslate"><span class="pre">and</span></code> and <code class="docutils literal notranslate"><span class="pre">or</span></code> in Python, these invariants can be described as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="nb">bool</span><span class="p">(</span><span class="n">A</span> <span class="ow">and</span> <span class="n">B</span><span class="p">)</span> <span class="o">==</span> <span class="nb">bool</span><span class="p">(</span><span class="ow">not</span> <span class="p">(</span><span class="ow">not</span> <span class="n">A</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">B</span><span class="p">))</span>
<span class="k">assert</span> <span class="nb">bool</span><span class="p">(</span><span class="n">A</span> <span class="ow">or</span> <span class="n">B</span><span class="p">)</span> <span class="o">==</span> <span class="nb">bool</span><span class="p">(</span><span class="ow">not</span> <span class="p">(</span><span class="ow">not</span> <span class="n">A</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">B</span><span class="p">))</span>
</pre></div>
</div>
<p>That is, if you take one of the operators, invert both operands, switch to the
other operator, and then invert the overall result, youll get the same
answer (in a boolean sense) as you did from the original operator. (This may
seem redundant, but in many situations it actually lets you eliminate double
negatives and find tautologically true or false subexpressions, thus reducing
the overall expression size).</p>
<p>For circuit breakers, defining a suitable invariant is complicated by the
fact that theyre often going to be designed to eliminate themselves from the
expression result when theyre short-circuited, which is an inherently
asymmetric behaviour. Accordingly, that inherent asymmetry needs to be
accounted for when mapping De Morgans Laws to the expected behaviour of
symmetric circuit breakers.</p>
<p>One way this complication can be addressed is to wrap the operand that would
otherwise short-circuit in <code class="docutils literal notranslate"><span class="pre">operator.true</span></code>, ensuring that when <code class="docutils literal notranslate"><span class="pre">bool</span></code> is
applied to the overall result, it uses the same definition of truth that was
used to decide which branch to evaluate, rather than applying <code class="docutils literal notranslate"><span class="pre">bool</span></code> directly
to the circuit breakers input value.</p>
<p>Specifically, for the new short-circuiting operators, the following properties
would be reasonably expected to hold for any well-behaved symmetric circuit
breaker that implements both <code class="docutils literal notranslate"><span class="pre">__bool__</span></code> and <code class="docutils literal notranslate"><span class="pre">__not__</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="nb">bool</span><span class="p">(</span><span class="n">B</span> <span class="k">if</span> <span class="n">true</span><span class="p">(</span><span class="n">A</span><span class="p">))</span> <span class="o">==</span> <span class="nb">bool</span><span class="p">(</span><span class="ow">not</span> <span class="p">(</span><span class="n">true</span><span class="p">(</span><span class="ow">not</span> <span class="n">A</span><span class="p">)</span> <span class="k">else</span> <span class="ow">not</span> <span class="n">B</span><span class="p">))</span>
<span class="k">assert</span> <span class="nb">bool</span><span class="p">(</span><span class="n">true</span><span class="p">(</span><span class="n">A</span><span class="p">)</span> <span class="k">else</span> <span class="n">B</span><span class="p">)</span> <span class="o">==</span> <span class="nb">bool</span><span class="p">(</span><span class="ow">not</span> <span class="p">(</span><span class="ow">not</span> <span class="n">B</span> <span class="k">if</span> <span class="n">true</span><span class="p">(</span><span class="ow">not</span> <span class="n">A</span><span class="p">)))</span>
</pre></div>
</div>
<p>Note the order of operations on the right hand side (applying <code class="docutils literal notranslate"><span class="pre">true</span></code>
<em>after</em> inverting the input circuit breaker) - this ensures that an
assertion is actually being made about <code class="docutils literal notranslate"><span class="pre">type(A).__not__</span></code>, rather than
merely being about the behaviour of <code class="docutils literal notranslate"><span class="pre">type(true(A)).__not__</span></code>.</p>
<p>At the very least, <code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code> instances would respect this
logic, allowing existing boolean expression optimisations (like double
negative elimination) to continue to be applied.</p>
</section>
<section id="arbitrary-sentinel-objects">
<h3><a class="toc-backref" href="#arbitrary-sentinel-objects" role="doc-backlink">Arbitrary sentinel objects</a></h3>
<p>Unlike PEPs 505 and 531, the proposal in this PEP readily handles custom
sentinel objects:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_MISSING</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="c1"># Using the sentinel to check whether or not an argument was supplied</span>
<span class="k">def</span> <span class="nf">my_func</span><span class="p">(</span><span class="n">arg</span><span class="o">=</span><span class="n">_MISSING</span><span class="p">):</span>
<span class="n">arg</span> <span class="o">=</span> <span class="n">make_default</span><span class="p">()</span> <span class="k">if</span> <span class="n">is_sentinel</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">_MISSING</span><span class="p">)</span> <span class="c1"># &quot;else arg&quot; implied</span>
</pre></div>
</div>
</section>
<section id="implicitly-defined-circuit-breakers-in-circuit-breaking-expressions">
<h3><a class="toc-backref" href="#implicitly-defined-circuit-breakers-in-circuit-breaking-expressions" role="doc-backlink">Implicitly defined circuit breakers in circuit breaking expressions</a></h3>
<p>A never-posted draft of this PEP explored the idea of special casing 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> binary operators such that they were automatically
treated as circuit breakers when used in the context of a circuit breaking
expression. Unfortunately, it turned out that this approach necessarily
resulted in one of two highly undesirable outcomes:</p>
<ol class="upperalpha simple">
<li>the return type of these expressions changed universally from <code class="docutils literal notranslate"><span class="pre">bool</span></code> to
<code class="docutils literal notranslate"><span class="pre">types.CircuitBreaker</span></code>, potentially creating a backwards compatibility
problem (especially when working with extension module APIs that
specifically look for a builtin boolean value with <code class="docutils literal notranslate"><span class="pre">PyBool_Check</span></code> rather
than passing the supplied value through <code class="docutils literal notranslate"><span class="pre">PyObject_IsTrue</span></code> or using
the <code class="docutils literal notranslate"><span class="pre">p</span></code> (predicate) format in one of the argument parsing functions)</li>
<li>the return type of these expressions became <em>context dependent</em>, meaning
that other routine refactorings (like pulling a comparison operation out
into a local variable) could have a significant impact on the runtime
semantics of a piece of code</li>
</ol>
<p>Neither of those possible outcomes seems warranted by the proposal in this PEP,
so it reverted to the current design where circuit breaker instances must be
created explicitly via API calls, and are never produced implicitly.</p>
</section>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>As with <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a>, actual implementation has been deferred pending in-principle
interest in the idea of making these changes.</p>
<p>…TBD…</p>
</section>
<section id="acknowledgements">
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
<p>Thanks go to Steven DAprano for his detailed critique <a class="footnote-reference brackets" href="#id5" id="id2">[2]</a> of the initial
draft of this PEP that inspired many of the changes in the second draft, as
well as to all of the other participants in that discussion thread <a class="footnote-reference brackets" href="#id6" id="id3">[3]</a>.</p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id4" role="doc-footnote">
<dt class="label" id="id4">[<a href="#id1">1</a>]</dt>
<dd>PEP 335 rejection notification
(<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-March/117510.html">https://mail.python.org/pipermail/python-dev/2012-March/117510.html</a>)</aside>
<aside class="footnote brackets" id="id5" role="doc-footnote">
<dt class="label" id="id5">[<a href="#id2">2</a>]</dt>
<dd>Steven DApranos critique of the initial draft
(<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2016-November/043615.html">https://mail.python.org/pipermail/python-ideas/2016-November/043615.html</a>)</aside>
<aside class="footnote brackets" id="id6" role="doc-footnote">
<dt class="label" id="id6">[<a href="#id3">3</a>]</dt>
<dd>python-ideas thread discussing initial draft
(<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2016-November/043563.html">https://mail.python.org/pipermail/python-ideas/2016-November/043563.html</a>)</aside>
</aside>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain under the terms of the
CC0 1.0 license: <a class="reference external" href="https://creativecommons.org/publicdomain/zero/1.0/">https://creativecommons.org/publicdomain/zero/1.0/</a></p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0532.rst">https://github.com/python/peps/blob/main/peps/pep-0532.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0532.rst">2023-10-11 12:05:51 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#pep-deferral">PEP Deferral</a></li>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#relationship-with-other-peps">Relationship with other PEPs</a><ul>
<li><a class="reference internal" href="#pep-531-existence-checking-protocol">PEP 531: Existence checking protocol</a></li>
<li><a class="reference internal" href="#pep-505-none-aware-operators">PEP 505: None-aware operators</a></li>
<li><a class="reference internal" href="#pep-335-overloadable-boolean-operators">PEP 335: Overloadable Boolean operators</a></li>
<li><a class="reference internal" href="#pep-535-rich-comparison-chaining">PEP 535: Rich comparison chaining</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#the-circuit-breaking-protocol-if-else">The circuit breaking protocol (<code class="docutils literal notranslate"><span class="pre">if-else</span></code>)</a></li>
<li><a class="reference internal" href="#circuit-breaking-operators-binary-if-and-binary-else">Circuit breaking operators (binary <code class="docutils literal notranslate"><span class="pre">if</span></code> and binary <code class="docutils literal notranslate"><span class="pre">else</span></code>)</a></li>
<li><a class="reference internal" href="#overloading-logical-inversion-not">Overloading logical inversion (<code class="docutils literal notranslate"><span class="pre">not</span></code>)</a></li>
<li><a class="reference internal" href="#forcing-short-circuiting-behaviour">Forcing short-circuiting behaviour</a></li>
<li><a class="reference internal" href="#circuit-breaking-identity-comparisons-is-and-is-not">Circuit breaking identity comparisons (<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>)</a></li>
<li><a class="reference internal" href="#truth-checking-comparisons">Truth checking comparisons</a></li>
<li><a class="reference internal" href="#none-aware-operators">None-aware operators</a></li>
<li><a class="reference internal" href="#rich-chained-comparisons">Rich chained comparisons</a></li>
<li><a class="reference internal" href="#other-conditional-constructs">Other conditional constructs</a></li>
<li><a class="reference internal" href="#style-guide-recommendations">Style guide recommendations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#adding-new-operators">Adding new operators</a></li>
<li><a class="reference internal" href="#naming-the-operator-and-protocol">Naming the operator and protocol</a></li>
<li><a class="reference internal" href="#using-existing-keywords">Using existing keywords</a></li>
<li><a class="reference internal" href="#naming-the-protocol-methods">Naming the protocol methods</a></li>
<li><a class="reference internal" href="#making-binary-if-right-associative">Making binary <code class="docutils literal notranslate"><span class="pre">if</span></code> right-associative</a></li>
<li><a class="reference internal" href="#naming-the-standard-circuit-breakers">Naming the standard circuit breakers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#risks-and-concerns">Risks and concerns</a></li>
<li><a class="reference internal" href="#design-discussion">Design Discussion</a><ul>
<li><a class="reference internal" href="#protocol-walk-through">Protocol walk-through</a></li>
<li><a class="reference internal" href="#respecting-de-morgan-s-laws">Respecting De Morgans Laws</a></li>
<li><a class="reference internal" href="#arbitrary-sentinel-objects">Arbitrary sentinel objects</a></li>
<li><a class="reference internal" href="#implicitly-defined-circuit-breakers-in-circuit-breaking-expressions">Implicitly defined circuit breakers in circuit breaking expressions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
<li><a class="reference internal" href="#references">References</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-0532.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>