177 lines
5.6 KiB
ReStructuredText
177 lines
5.6 KiB
ReStructuredText
|
PEP: 679
|
||
|
Title: Allow parentheses in assert statements
|
||
|
Author: Pablo Galindo Salgado <pablogsal@python.org>
|
||
|
Discussions-To: <email address or URL>
|
||
|
Status: Draft
|
||
|
Type: Standards Track
|
||
|
Content-Type: text/x-rst
|
||
|
Created: 07-Jan-2022
|
||
|
Python-Version: 3.11
|
||
|
|
||
|
|
||
|
Abstract
|
||
|
========
|
||
|
|
||
|
This pep proposes to allow parentheses surrounding the two-subject 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, message)``) to an assert statement with a subject and a
|
||
|
failure message, equivalent to the statement with the parentheses removed
|
||
|
(``assert expression, message``).
|
||
|
|
||
|
|
||
|
Motivation
|
||
|
==========
|
||
|
|
||
|
Is a common user mistake when using the form of the assert stamens that includes
|
||
|
the error message to surround it by parentheses. Unfortunately, this mistake
|
||
|
passes undetected as the assert will always pass due the the fact that is
|
||
|
interpreted as an assert statement where the expression is a two-tuple, which
|
||
|
always has truth-y value.
|
||
|
|
||
|
The mistake most often happens when extending thing or description beyond a
|
||
|
single line on assert statements as () are the natural way to do that and as it
|
||
|
is with assert being a statement.
|
||
|
|
||
|
This is so common that a ``SyntaxWarning`` is `now emitted by the compiler
|
||
|
<https://bugs.python.org/issue35029>`_.
|
||
|
|
||
|
Additionally, some other statements in the language allow parenthesized forms
|
||
|
in one way or another like ``import`` statements (``from x import (a,b,c)``), ``del``
|
||
|
statements (``del (a,b,c)``).
|
||
|
|
||
|
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 ``assert`` statements over
|
||
|
multiple lines as::
|
||
|
|
||
|
assert (
|
||
|
very very long
|
||
|
expression
|
||
|
), (
|
||
|
"very very long "
|
||
|
"message"
|
||
|
)
|
||
|
|
||
|
the authors of this document believe the parenthesized form is more clear and more consistent with
|
||
|
the formatting of other grammar constructs::
|
||
|
|
||
|
assert (
|
||
|
very very long
|
||
|
expression,
|
||
|
|
||
|
"very very long "
|
||
|
"message",
|
||
|
)
|
||
|
|
||
|
This change have been originally discussed and proposed in [bpo-46167]_.
|
||
|
|
||
|
Rationale
|
||
|
=========
|
||
|
|
||
|
This change can be implemented in the parser or in the compiler. We have
|
||
|
selected implementing this change in the parser because doing in in the compiler
|
||
|
will require re-interpreting the AST of an assert statement with a two-tuple::
|
||
|
|
||
|
Module(
|
||
|
body=[
|
||
|
Assert(
|
||
|
test=Tuple(
|
||
|
elts=[
|
||
|
Name(id='x', ctx=Load()),
|
||
|
Name(id='y', ctx=Load())],
|
||
|
ctx=Load()))],
|
||
|
type_ignores=[])
|
||
|
|
||
|
as the AST of an assert statement with an expression and a message::
|
||
|
|
||
|
Module(
|
||
|
body=[
|
||
|
Assert(
|
||
|
test=Name(id='x', ctx=Load()),
|
||
|
msg=Name(id='y', ctx=Load()))],
|
||
|
type_ignores=[])
|
||
|
|
||
|
The problem with this approach is that the AST of the first form will
|
||
|
technically be "incorrect" as we already have an specialized form for the AST of
|
||
|
an assert statement with an expression 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.
|
||
|
|
||
|
Specification
|
||
|
=============
|
||
|
|
||
|
This PEP proposes changing the grammar from the ``assert`` statement to: ::
|
||
|
|
||
|
| 'assert' '(' expression ',' expression [','] ')' &(NEWLINE | ';')
|
||
|
| 'assert' a=expression [',' expression ]
|
||
|
|
||
|
Where the first line is the new form of the assert statement that allows
|
||
|
parentheses. The lookahead is needed so statements like ``assert (a, b) <= c,
|
||
|
"something"`` are still parsed correctly and to prevent the parser to eagerly
|
||
|
capture the tuple as the full statement.
|
||
|
|
||
|
Optionally, new "invalid" rule can be added to produce custom syntax errors to
|
||
|
cover tuples with 0, 1, 3 or more elements.
|
||
|
|
||
|
|
||
|
Backwards Compatibility
|
||
|
=======================
|
||
|
|
||
|
The change is not technically backwards compatible, as parsing ``assert (x,y)``
|
||
|
is currently interpreted as an assert statement with a 2-tuple as the subject,
|
||
|
while after this change it will be interpreted as ``assert x,y``.
|
||
|
|
||
|
On the other hand, this kind of assert statements are always true 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 is because users are ignoring syntax warnings).
|
||
|
|
||
|
Security Implications
|
||
|
=====================
|
||
|
|
||
|
There are no security implications for this change.
|
||
|
|
||
|
|
||
|
How to Teach This
|
||
|
=================
|
||
|
|
||
|
The new form of the ``assert`` statement will be documented as part of the language
|
||
|
standard.
|
||
|
|
||
|
When teaching the form with error message of the ``assert`` statement to users,
|
||
|
now it can be noted that adding parentheses also work as expected, which allows to break
|
||
|
the statement over multiple lines.
|
||
|
|
||
|
|
||
|
Reference Implementation
|
||
|
========================
|
||
|
|
||
|
A proposed draft PR with the change exist in [GH-30247]_.
|
||
|
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [bpo-46167] https://bugs.python.org/issue46167
|
||
|
.. [GH-30247] https://github.com/python/cpython/pull/30247
|
||
|
|
||
|
|
||
|
Copyright
|
||
|
=========
|
||
|
|
||
|
This document is placed in the public domain or under the
|
||
|
CC0-1.0-Universal license, whichever is more permissive.
|
||
|
|
||
|
..
|
||
|
Local Variables:
|
||
|
mode: indented-text
|
||
|
indent-tabs-mode: nil
|
||
|
sentence-end-double-space: t
|
||
|
fill-column: 70
|
||
|
coding: utf-8
|
||
|
End:
|