python-peps/pep-0679/index.html

281 lines
20 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 679 Allow parentheses in assert statements | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0679/">
<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 679 Allow parentheses in assert statements | peps.python.org'>
<meta property="og:description" content="This PEP proposes to allow parentheses surrounding the two-argument form of assert statements. This will cause the interpreter to reinterpret what before would have been an assert with a two-element tuple that will always be True (assert (expression, me...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0679/">
<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="This PEP proposes to allow parentheses surrounding the two-argument form of assert statements. This will cause the interpreter to reinterpret what before would have been an assert with a two-element tuple that will always be True (assert (expression, me...">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 679</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 679 Allow parentheses in assert statements</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Pablo Galindo Salgado &lt;pablogsal&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-679-allow-parentheses-in-assert-statements/13003">Discourse thread</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Proposal under active discussion and revision">Draft</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">07-Jan-2022</dd>
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
<dd class="field-even">3.12</dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</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="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP proposes to allow parentheses surrounding the two-argument form of
assert statements. This will cause the interpreter to reinterpret what before
would have been an assert with a two-element tuple that will always be True
(<code class="docutils literal notranslate"><span class="pre">assert</span> <span class="pre">(expression,</span> <span class="pre">message)</span></code>) to an assert statement with a subject and a
failure message, equivalent to the statement with the parentheses removed
(<code class="docutils literal notranslate"><span class="pre">assert</span> <span class="pre">expression,</span> <span class="pre">message</span></code>).</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>It is a common user mistake when using the form of the assert statement that includes
the error message to surround it with parentheses. Unfortunately, this mistake
passes undetected as the assert will always pass, because it is
interpreted as an assert statement where the expression is a two-tuple, which
always has truth-y value.</p>
<p>The mistake most often happens when extending the test or description beyond a
single line, as parentheses are the natural way to do that.</p>
<p>This is so common that a <code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> is <a class="reference external" href="https://bugs.python.org/issue35029">now emitted by the compiler</a>.</p>
<p>Additionally, some other statements in the language allow parenthesized forms
in one way or another like <code class="docutils literal notranslate"><span class="pre">import</span></code> statements (<code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">x</span> <span class="pre">import</span> <span class="pre">(a,b,c)</span></code>) and
<code class="docutils literal notranslate"><span class="pre">del</span></code> statements (<code class="docutils literal notranslate"><span class="pre">del</span> <span class="pre">(a,b,c)</span></code>).</p>
<p>Allowing parentheses not only will remove the common mistake but also will allow
users and auto-formatters to format long assert statements over multiple lines
in what the authors of this document believe will be a more natural way.
Although is possible to currently format long <code class="docutils literal notranslate"><span class="pre">assert</span></code> statements over
multiple lines as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="p">(</span>
<span class="n">very</span> <span class="n">very</span> <span class="n">long</span>
<span class="n">expression</span>
<span class="p">),</span> <span class="p">(</span>
<span class="s2">&quot;very very long &quot;</span>
<span class="s2">&quot;message&quot;</span>
<span class="p">)</span>
</pre></div>
</div>
<p>the authors of this document believe the parenthesized form is more clear and more consistent with
the formatting of other grammar constructs:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="p">(</span>
<span class="n">very</span> <span class="n">very</span> <span class="n">long</span>
<span class="n">expression</span><span class="p">,</span>
<span class="s2">&quot;very very long &quot;</span>
<span class="s2">&quot;message&quot;</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>This change has been originally discussed and proposed in <a class="reference internal" href="#bpo-46167" id="id1"><span>[bpo-46167]</span></a>.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>This change can be implemented in the parser or in the compiler. We have
selected implementing this change in the parser because doing it in the compiler
will require re-interpreting the AST of an assert statement with a two-tuple:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Module</span><span class="p">(</span>
<span class="n">body</span><span class="o">=</span><span class="p">[</span>
<span class="n">Assert</span><span class="p">(</span>
<span class="n">test</span><span class="o">=</span><span class="n">Tuple</span><span class="p">(</span>
<span class="n">elts</span><span class="o">=</span><span class="p">[</span>
<span class="n">Name</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="n">ctx</span><span class="o">=</span><span class="n">Load</span><span class="p">()),</span>
<span class="n">Name</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s1">&#39;y&#39;</span><span class="p">,</span> <span class="n">ctx</span><span class="o">=</span><span class="n">Load</span><span class="p">())],</span>
<span class="n">ctx</span><span class="o">=</span><span class="n">Load</span><span class="p">()))],</span>
<span class="n">type_ignores</span><span class="o">=</span><span class="p">[])</span>
</pre></div>
</div>
<p>as the AST of an assert statement with an expression and a message:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Module</span><span class="p">(</span>
<span class="n">body</span><span class="o">=</span><span class="p">[</span>
<span class="n">Assert</span><span class="p">(</span>
<span class="n">test</span><span class="o">=</span><span class="n">Name</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="n">ctx</span><span class="o">=</span><span class="n">Load</span><span class="p">()),</span>
<span class="n">msg</span><span class="o">=</span><span class="n">Name</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s1">&#39;y&#39;</span><span class="p">,</span> <span class="n">ctx</span><span class="o">=</span><span class="n">Load</span><span class="p">()))],</span>
<span class="n">type_ignores</span><span class="o">=</span><span class="p">[])</span>
</pre></div>
</div>
<p>The problem with this approach is that the AST of the first form will
technically be “incorrect” as we already have a specialized form for the AST of
an assert statement with a test and a message (the second one). This
means that many tools that deal with ASTs will need to be aware of this change
in semantics, which will be confusing as there is already a correct form that
better expresses the new meaning.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>This PEP proposes changing the grammar of the <code class="docutils literal notranslate"><span class="pre">assert</span></code> statement to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">|</span> <span class="s1">&#39;assert&#39;</span> <span class="s1">&#39;(&#39;</span> <span class="n">expression</span> <span class="s1">&#39;,&#39;</span> <span class="n">expression</span> <span class="p">[</span><span class="s1">&#39;,&#39;</span><span class="p">]</span> <span class="s1">&#39;)&#39;</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">NEWLINE</span> <span class="o">|</span> <span class="s1">&#39;;&#39;</span><span class="p">)</span>
<span class="o">|</span> <span class="s1">&#39;assert&#39;</span> <span class="n">a</span><span class="o">=</span><span class="n">expression</span> <span class="p">[</span><span class="s1">&#39;,&#39;</span> <span class="n">expression</span> <span class="p">]</span>
</pre></div>
</div>
<p>Where the first line is the new form of the assert statement that allows
parentheses. The lookahead is needed so statements like <code class="docutils literal notranslate"><span class="pre">assert</span> <span class="pre">(a,</span> <span class="pre">b)</span> <span class="pre">&lt;=</span> <span class="pre">c,</span>
<span class="pre">&quot;something&quot;</span></code> are still parsed correctly and to prevent the parser to eagerly
capture the tuple as the full statement.</p>
<p>Optionally, new “invalid” rule can be added to produce custom syntax errors to
cover tuples with 0, 1, 3 or more elements.</p>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p>The change is not technically backwards compatible, as parsing <code class="docutils literal notranslate"><span class="pre">assert</span> <span class="pre">(x,y)</span></code>
is currently interpreted as an assert statement with a 2-tuple as the subject,
while after this change it will be interpreted as <code class="docutils literal notranslate"><span class="pre">assert</span> <span class="pre">x,y</span></code>.</p>
<p>On the other hand, assert statements of this kind always pass, so they are
effectively not doing anything in user code. The authors of this document think
that this backwards incompatibility nature is beneficial, as it will highlight
these cases in user code while before they will have passed unnoticed (assuming that
these cases still exist because users are ignoring syntax warnings).</p>
</section>
<section id="security-implications">
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
<p>There are no security implications for this change.</p>
</section>
<section id="how-to-teach-this">
<h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2>
<p>The new form of the <code class="docutils literal notranslate"><span class="pre">assert</span></code> statement will be documented as part of the language
standard.</p>
<p>When teaching the form with error message of the <code class="docutils literal notranslate"><span class="pre">assert</span></code> statement to users,
now it can be noted that adding parentheses also work as expected, which allows to break
the statement over multiple lines.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>A proposed draft PR with the change exist in <a class="reference internal" href="#gh-30247" id="id2"><span>[GH-30247]</span></a>.</p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<div role="list" class="citation-list">
<div class="citation" id="bpo-46167" role="doc-biblioentry">
<dt class="label" id="bpo-46167">[<a href="#id1">bpo-46167</a>]</dt>
<dd><a class="reference external" href="https://bugs.python.org/issue46167">https://bugs.python.org/issue46167</a></div>
<div class="citation" id="gh-30247" role="doc-biblioentry">
<dt class="label" id="gh-30247">[<a href="#id2">GH-30247</a>]</dt>
<dd><a class="reference external" href="https://github.com/python/cpython/pull/30247">https://github.com/python/cpython/pull/30247</a></div>
</div>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0679.rst">https://github.com/python/peps/blob/main/peps/pep-0679.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0679.rst">2023-09-09 17:39:29 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</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-0679.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>