mirror of https://github.com/python/peps
998 lines
105 KiB
HTML
998 lines
105 KiB
HTML
|
||
<!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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </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 <ncoghlan at gmail.com>,
|
||
Mark E. Haase <mehaase at gmail.com></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 Morgan’s 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 object’s 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 Guido’s 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"><</span> <span class="pre">VALUE</span> <span class="pre"><</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">"__then__"</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">"__else__"</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 breaker’s 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 doesn’t 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">'if'</span> <span class="n">or_test</span> <span class="p">[</span><span class="s1">'else'</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">'else'</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 “don’t 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 other’s
|
||
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 Morgan’s 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 breaker’s 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">"""Replace circuit breakers with their short-circuited result</span>
|
||
|
||
<span class="sd"> Passes other input values through unmodified.</span>
|
||
<span class="sd"> """</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 "if" expressions, " else VALUE" 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 "else" expressions, "VALUE if " 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">"""Simple circuit breaker type"""</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">"""Returns a circuit breaker switching on 'value is sentinel'"""</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">"""Returns a circuit breaker switching on 'value is not sentinel'"""</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">"""Returns a circuit breaker switching on 'bool(value)'"""</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">"""Returns a circuit breaker switching on 'not bool(value)'"""</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 can’t 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, it’s 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 isn’t 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 won’t 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
|
||
won’t, and hence it won’t 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> doesn’t 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 "LHS if " 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 "LHS if " 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 "LHS if " 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 "LHS if " 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 " else RHS" 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 " else RHS" 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 " else RHS" 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 " else RHS" 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> doesn’t 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">>>> </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">>>> </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">"key"</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">>>> </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">"key"</span><span class="p">))</span>
|
||
<span class="gp">>>> </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 Python’s 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">>>> </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">"key"</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span>
|
||
<span class="gp">>>> </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">"key"</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("key")</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 isn’t 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("key")</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("key")</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">>>> </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">"key"</span><span class="p">),</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="gp">>>> </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("key")</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> doesn’t 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("key")</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("key")</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">>>> </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">"key"</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("key")</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">>>> </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">"key"</span><span class="p">))</span>
|
||
<span class="gp">>>> </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("key")</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> doesn’t 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 Morgan’s 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 Morgan’s 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, you’ll 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 they’re often going to be designed to eliminate themselves from the
|
||
expression result when they’re short-circuited, which is an inherently
|
||
asymmetric behaviour. Accordingly, that inherent asymmetry needs to be
|
||
accounted for when mapping De Morgan’s 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 breaker’s 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"># "else arg" 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 D’Aprano 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 D’Aprano’s 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 Morgan’s 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> |