Hello,
Here is my counter-proposal to Pablo’s PEP 679. Rather than doing the smallest change to fix a problem, it tries to steer towards a better future (in a few years/decades it’ll take for new syntax to be usable).
It’s not as polished as PEP 679, but the idea should be clear.
I’ve had “suggest assert-with
” sitting on my “maybe-later” list for a while now, thanks to Pablo & co. for making me write it up!
PEP: XXX
Title: Assert-with: Dedicated syntax for assertion messages
Author: Petr Viktorin encukou@gmail.com
Discussions-To: (XXX: Discourse thread)
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 18-Jan-2022
Python-Version: 3.11
Post-History:
Abstract
This PEP proposes allowing to use the with
keyword to introduce the
exception message in assert
statements.
The current syntax, a comma, will still be valid, but discouraged
in CPython’s style guide (pep:8
).
Using a tuple
for the assertion expression will raise a SyntaxError
,
rather than a warning as in Python 3.10.
Motivation
The most common usage of the coomma in Python is as a separator of
variable-length lists of homogeneous elements, like the the items of a
tuple display, parameters/sarguments of functions, or import targets.
Not using the comma in this way leads to confusion, especially in places
where the comma looks like it could separate tuple elements.
Prior art: Except-as
In Python 2, except exc_type, name
had the same
meaning as today’s except exc_type as name
.
The comma separated two different kinds of things: a type to be
checked and a name to be assigned to.
This usage was deemed confusing, and in :pep:3110
, the comma was replaced
by the as
keyword:
as an alternative in Python 2, and as the only option in Python 3.
Assert-with
The remaining use of a comma to separate heterogeneous stuff is
the `assertstatement, where the comma separates the expression to be tested and the optional exception message. This usage is confusing as well, for many of the same reasons as in
except``.
See the Motivation in :pep:679
for details.
(That Motivation could essentially be copied here.
Pablo explains the practical issue very well.)
Rationale
A keyword expresses the meaning of the two-clause assert
statement
better than the comma.
The comma will continue to be valid, so old code will continue to work,
but we expect code style guides and linters to discourage it, making it
rare in new code that targets Python 3.11+.
Specification
The with
keyword will be allowed in place of the comma between the
condition and message of the assert
statement, making the following
statements equivalent::
assert 1 + 1 == 3 with "Expected wrong result!"
assert 1 + 1 == 3, "Expected wrong result!"
Using a tuple as the assertion expression,
which causes a SyntaxWarning in Python 3.10, will instead raise a SyntaxError::
assert (1 + 1 == 3, "Expected wrong result!") # SyntaxError
Backwards Compatibility
A SyntaxWarning is turned into a SyntaxError, so programs that contain this
kind of ineffective assert
statement will fail to compile.
This is a large downside of this proposal, but is mitigated by several factors:
- The only use case of an assert with an always-true assertion is testing,
exploring or emulating the Python language.
It is OK to break these uses if the change is beneficial enough. - Python 3.10 already emits a warning in this case. There will be a
deprecation period, albeit shorter than the two releases specified in
:pep:387
. - The fix is easy (unless the affected source code cannot be modified).
Security Implications
No negative security implications are known.
How to Teach This
If Python 3.10 an below can be ignored, teach the assert
statement with
with
instead of the comma.
Otherwise, teach the comma as before.
If an IDE, linter or code style checker can positively determine that
some code doesn’t support Python 3.10, it can choose to suggest
replacing relevant commas with with
.
Reference Implementation
None yet
Rejected Ideas
Allow parentheses in assert statements (PEP 679)
Allowing parentheses around the entire body of the assert
statement
is perhaps the easiest way to solve the issue of misleading code,
but it does not address the “overloaded comma” between heterogeneous
syntax elements.
A keyword expresses the meaning of the code better.
PEP 697 has a forward compatibility issue. Code like::
assert (1 + 1 == 3, "Expected wrong result!")
would raise AertionError under Python 3.11 but silently do nothing in
earlier versions.
Since assert
is frequently used for testing (e.g. with the
Pytest framework), this means tests for older Python versions would
become unreliable.
Disallow the comma
With parentheses around the assertion expression disallowed,
code using the old syntax will not be dangerously misleading::
assert 1 + 1 == 3, "Expected wrong result!" # works as before
Discouraging the comma in new code, rather than disallowing it entirely,
means old, battle-tested code will continue to work without modifications.
Disallow square brackets around the expression
Code like the following will still be valid::
assert [1 + 1 == 3, "Expected wrong result!"] # does nothing - always True
Like the variant with parentheses, this assertion currently always fails,
yet it will not be turned into a SyntaxError.
This is inconsistent with the treatment of parentheses::
assert (1 + 1 == 3, "Expected wrong result!") # AssertionError
Disallowing parentheses is not a general design principle,
but a special case in atonement for (what is in retrospect) a past design mistake.
Other always-True assertions should not be turned into errors.
Catching them should continue to be be the job of linters, not the parser.
Using a different keyword
with
is an existing keyword and fits the use well enough.
There’s no need to invent a new keyword, soft or hard.
Open Issues
1
PEP 679 might be updated with @cben’s suggestion:
Treat
assert (cond, message)
with new semantics, actually executing assert — but also emitSyntaxWarning
?
This idea could work with with
as well, and might be a better choice than
SyntaxError
.
2
The grammar changes are not implemented.
They seem feasible, but that’s an educated guess at this point.
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: