Hello,
I am happy to announce that PEP 679 has been updated after its initial debut in 2022!
Hello,
I am happy to announce that PEP 679 has been updated after its initial debut in 2022!
The Git diff: PEP 679: Modify proposal and improve sections (#4575) · python/peps@7c81675 · GitHub
From prior discussion, PEP 679 -- Allow parentheses in assert statements, I’m inferring this is being reintroduced now because Python 3.9 is end of life next month?
I think it’s a bad idea because assert is not a function and trying to protect newbies from a mistake muddles that distinction.
I understand what you mean but I think you are interpreting this in a different way the pep is indicating: we are not making it a function, we are just allow parentheses on the argument of the statement. The same way you would allow del (a,b)
and del a,b
FWIW, this is also my objection to the PEP.
I see allowing parentheses as more like from (foo import bar)
than del (a, b)
or with (a, b as z)
.
But, the PEP now covers that as a rejected idea.
Why isn’t it also like except (X, Y):
? Which can now be except X, Y:
since PEP 758 on the basis that there’s only one possible intention and so the parentheses were superfluous?
This proposal is going the other direction, but is also implying that the parentheses are superfluous - with or without them, assert
will now behave the same, with the only impact being an unintentional no-op becoming the intended assertion check.[1]
FTR, I don’t consider finally/return
to be an unintentional no-op, or that most of the proposed alternatives could always be presumed to be the intended behaviour, which is why I’m applying a different argument here than in that case. ↩︎
The behaviour is more like except (X as Y):
– the X and Y are not the same kind of thing. It’s like if we allowed parentheses around Python 2’s except, so you could write except (ValueError, exc): print(exc)
The PEP argues to make assert
more like other constructs that use a comma; I believe that assert
should not use a comma in the first place.
FWIW, I’m a big fan of this. It’s just amazing how often you find this construct already is seemingly working code.
I am also in favor of this. While learning the language recently, it was difficult for me to understand the purpose or reason why assert x,y
should not be equivalent to assert (x,y)
or even assert(x,y)
.
It took me 10 minutes arguing with ChatGPT to understand why the x,y
was not parsed as a tuple to begin with (albeit a good lesson on python grammar), and another 10 minutes until it finally admitted that yes this is a viable change and in fact there was an existing PEP to address it.
I’m glad to see this PEP revisited and hope to see it implemented.
While it’s not exactly the same construct, for (x,y) in z
is equivalent to for x,y in z
, and without deep knowledge of python, it’s hard for a beginner to understand why assert should have a different “meaning” when parentheses are used.
But I think the biggest argument for this change is that assert (truthy, tuple)
is just not a very useful construct. Maybe a nice party trick, but I can’t imagine any seasoned programmer accepting it in a code review, instead of a more clear formulation of the author’s intent.
I am not a fun of this. In all other cases, comma separates homogeneous things (names or expressions). There were few exceptions in Python 2: except
, print>>file
and assert
. The former two were changed in Python 3, only assert
remains. It wouldn’t be out of place if comma was replaced with a keyword in Python 3, but this is too late now, there is enough justification for such change, and nothing suitable comes to mind.
Anyway, I don’t think this is good idea. It will make parsing more complex. Not that it is impossible to write a simple rule for the current parser, but it would be more difficult to emit a warning for wrong cases (3-tuple, 1-tuple, tuple in parentheses). And it will be more difficult to teach which syntax is good and which is wrong.
I’m not a fan either because it looks like a tuple or a function call, but the second expression is not evaluated if the condition is true, unlike an actual tuple or function call.
As for an alternative to the comma, would else
have worked?
assert is_valid(value) else ValueError("invalid value")
Yeah, too late now.
I’m not sure I see the value in else
here.
I can imagine a block level construct being useful, but it would use essentially the same syntax:
assert debug_check:
# debugging code to figure out what happened
# implicit raise AssertionError()
assert debug_check, msg:
# etc
# implicit raise AssertionError(msg)
assert(debug_check, msg):
# etc
# implicit raise AssertionError(msg)
assert debug_check:
raise SomeOtherError()
The comma is something like syntactic sugar to avoid an otherwise lengthy syntax for simple debugging, and supporting parens in the assert
statement IMO is just a way to prevent cavities.
I’m in favour of this too, but didn’t want to start the bikeshedding The PEP authors can do that if they want to.
But since it already does use a comma, making the inconsistent and meaningless case into a consistent and meaningful case seems like a good move.
I wonder if the backwards incompatibility is really as bad as the PEP makes it out to be.
If someone has assert (pred, message)
in current code, their code is broken since they have an accidental no-op in there. And they get a syntax warning (really a form of lint) reminding them of that.
Why can’t we just implement the new syntax without a warning? The warning will probably just deter people from using the new syntax.
True, in some cases the assert will be stale and cause a failure in otherwise “working” code. But usually I presume that failure will be welcome, as it indicates a latent bug: either the code has a bug, or the assert is broken. And I expect that in most cases the assert will continue to pass.
I also assume that very little “pro” code (popular packages and apps, proprietary code) will be hit by this, because of the tendency to run tests with warnings→errors on. (Witness the complaints about the warning that was added for control flow in finally
.)
Then again I think Pablo and Stan have probably thought deep and hard about this and came to the conclusion that the current proposal is best, and I don’t want to interfere with that.
If we never want to add this syntax, then PEP 679 is the right way to go. That’s ultimately for the SC do decide.
Except for that caveat, the PEP is great ;‍)
IMO, the PEP should be a bit more explicit about the ast
changes: the new paren_syntax
on ast.Assert
should be an attribute and optional __init__
argument, right?
I think paren_syntax
should not be removed in 3.18 – that removal is an additional avoidable backcompat break.
Also, SyntaxWarning
itself is problematic – see PEP 765 discussions. We decided that fixing that is out of scope for this PEP, but perhaps a note in “Rejected ideas” would be appropriate.
Right. The code that will be broken has been raising SyntaxWarning
since 3.10, so while it’s good to note that things will break, it’s not a big deal.
Why it’s a bad idea.
#!/usr/bin/env python3
x = 7
def side_effect():
global x
x = 8
return x
assert side_effect() == 8, "not eight"
assert (side_effect() == 8, "not eight")
print(x)
So with python3.12 there’s a warning about the parenthesis version: *SyntaxWarning: assertion is always true, perhaps remove parentheses?
assert (side_effect() == 8, “not eight”)
There’s no warning that python3 side_effect.py prints 8 and python3 -O side_effect.py prints 7.
In the spirit of “if it looks like a duck”, asserts are not fucntions so we should not make them look like functions.
Isn’t this true regardless of the decision here? How is this related?
The warning doesn’t actually tell the user anything about assertions with side effects though. If they were to follow the warning, they’d just remove the brackets and be back to having an assertion with side effects bug.
Unless your point is that allowing the brackets makes it look like a function, making the fact that assertions are effectively optional less apparent?
Exactly. The less it looks like a function the less users are likely to think it’s a function.
It’s a strong indication an assert is not a function. “Why am I getting this warning? The first argument is supposed to be true. I better read the docs / google / ask my favorite AI what’s going on(*)”