Proposal: safer assertion() syntax as a follow-up idea to PEP 679

Hi everyone,

Although PEP 679 was rejected, the review process made me think of an alternative way to improve safety while keeping the familiar semantics of assert.

The idea is to introduce a new assertion syntax that explicitly requires parentheses, thus eliminating the long-standing “two-element tuple always-truthy” footgun while keeping the classic assert statement intact.

The grammar sketch would look like this:

'assertion' '(' expression ',' expression ')' &(NEWLINE | ';')

Key points

  • The new assertion() syntax always requires parentheses, making it visually and syntactically clear.
  • It avoids the classic assert (a, b)(a, b) tuple trap entirely.
  • Using parentheses with the old assert statement would raise a SyntaxWarning recommending the safer assertion() form.
  • The legacy assert will remain available throughout the Python 3 series, while assertion() would be the recommended form starting in 3.15.
  • For Python 4, we can later decide whether to keep only the parenthesized form or fully transition to assertion().

If people find this direction reasonable, I’m happy to write a draft PEP and prototype the implementation.

cc: @Stanfromireland @pablogsal PEP 679 authors

3 Likes

Another possible option could be safe_assert. If people prefer that direction, I’m fine with it too.
Since I’m not a native English speaker, I’m not entirely sure how assertion feels to others.

Just to clarify, since you’ve included a grammar sketch in your proposal, but suggested a function; you are proposing a new builtin function as follows?

def assertion(test: bool, message: str) -> None:
   ...

Making assert a builtin (and thus making it overrideable) seems like it would open up a few interesting opportunities for overloading/wrappers, which could be nice!

1 Like

IIUC, it depends on the bytecode and should be ignored once the optimizer is enabled.
See: cpython/Python/codegen.c at d78d7a50b06c4ea10d13fc2dcb42607a97f9260c · python/cpython · GitHub

So, I’m not sure we can introduce it as a built-in function.

1 Like

Perhaps the C implementation itself can just check if the flag is enabled, and if that’s the case, return before checking anything.

That however would still leave the evaluation of the Boolean expression, which is skipped in assert with -o too (iirc). That would mean that their semantics are not the same, unless -o completely removes all function calls to assertion.

def expr() -> bool:
    print("Within expr")
    return True

assert expr(), "expr is false" # Nothing gets printed in `-o`
assertion(expr(), "expr is false") # We still get "Within expr" in `-o` mode

Therefore, I’m +/-1 right now, we’ll have to see how this discussion continues.

2 Likes

I vote against safe_* as a name, as a general rule. One of the things I’ve learned about that naming scheme during code reviews is that there’s frequently confusion about what “safe” means.

I think “assertion” is okay, but I like function and statement names to be verbs, not nouns. So names like require, affirm, claim all seem a bit better (but they lose the connection to “AssertionError”, so that’s not great).


I really like the idea of trying to warn/remove this footgun, but I think the naming is essential. Without a good name, I don’t feel great about trying to fully replace assert, which seems key to the proposal.

I don’t see a great naming option, so I feel stuck without a good suggestion for how to advance this idea.

8 Likes

Another idea was using assert!, but it would require a lot of discussion since it’s not currently supported in Python.

2 Likes

I don’t think the tuple problem with assert is serious enough to warrant adding yet another piece of new syntax and pushing the entire ecosystem to change existing assert statements.

27 Likes

Just a proposal, since some people want to solve the issue without breaking the current behavior. And I don’t think that it is pushing because it only raises SyntaxWarnings that are already falsy used.
Maybe some people can argue that it would be solved by the linter side rather than Python itself, and I am not against those opinions.

1 Like

Perhaps asserted would work, indicating, that it is asserted that some expr evaluated to something truthy, so it’s asserted that the code afterwards will only be executed once it’s asserted (sounds repetitive ik).

1 Like