Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 679 – Neue assert-Anweisungssyntax mit Klammern

Autor:
Pablo Galindo Salgado <pablogsal at python.org>, Stan Ulbrych <stanulbrych at gmail.com>
Discussions-To:
Discourse thread
Status:
Entwurf
Typ:
Standards Track
Erstellt:
07-Jan-2022
Python-Version:
3.15
Post-History:
08-Sep-2025, 10-Jan-2022

Inhaltsverzeichnis

Zusammenfassung

Diese PEP schlägt vor, Klammern in der Zwei-Argument-Form von assert zuzulassen. Der Interpreter wird assert (expr, msg) als assert expr, msg neu interpretieren und so die häufige Fehlerquelle beseitigen, bei der solcher Code zuvor als Assertion eines Zwei-Elemente- Tuple behandelt wurde, das immer wahrheitsgetreu ist.

Motivation

Es ist ein häufiger Benutzerfehler bei der Verwendung der Form der assert-Anweisung, die die Fehlermeldung enthält, diese in Klammern zu setzen [1] [2]. Dies liegt daran, dass viele Anfänger annehmen, assert sei eine Funktion. Die prominenten unittest-Methoden, insbesondere assertTrue(), erfordern ebenfalls Klammern um die Assertion und die Nachricht.

Leider wird dieser Fehler unbemerkt durchgehen, da die assert immer erfolgreich sein wird [6], da sie als assert-Anweisung interpretiert wird, bei der der Ausdruck ein Zwei-Tupel ist, das immer einen wahrheitsgetreuen Wert hat. Der Fehler tritt auch häufig auf, wenn der Test oder die Beschreibung über eine einzelne Zeile hinaus erweitert wird, da Klammern eine natürliche Möglichkeit dafür sind.

Dies ist so verbreitet, dass seit 3.10 vom Compiler eine SyntaxWarning ausgegeben wird und mehrere Code-Linter [3] [4].

Darüber hinaus erlauben einige andere Anweisungen in der Sprache auf die eine oder andere Weise geklammerte Formen, zum Beispiel import-Anweisungen (from x import (a,b,c)) oder del-Anweisungen (del (a,b,c)).

Das Zulassen von Klammern wird nicht nur die Fehlerquelle beseitigen, sondern auch Benutzern und Autoformatierern ermöglichen, lange assert-Anweisungen über mehrere Zeilen so zu formatieren, wie es die Autoren dieses Dokuments für natürlicher halten. Obwohl es derzeit möglich ist, lange assert-Anweisungen mit Backslashes (wie von PEP 8 empfohlen) oder Klammern und einem Komma über mehrere Zeilen zu formatieren

assert (
  very very long
  test
), (
  "very very long "
  "error message"
)

glauben die Autoren dieses Dokuments, dass die vorgeschlagene geklammerte Form klarer und intuitiver ist und besser mit der Formatierung anderer Grammatikkonstrukte übereinstimmt

assert (
  very very long
  test,

  "very very long "
  "message"
)

Begründung

Aufgrund von Kompatibilitätsbedenken (siehe Abschnitt unten), um Benutzer über die neue Änderung zu informieren, wie ein bisheriges Zwei-Elemente-Tupel geparst wird, wird bis Python 3.17 eine SyntaxWarning mit einer Nachricht wie "neue Assertionssyntax, wird erstes Element des Tupels assertieren" ausgegeben. Zum Beispiel bei Verwendung der neuen Syntax

>>> assert ('Petr' == 'Pablo', "That doesn't look right!")
<python-input-0>:0: SyntaxWarning: new assertion syntax, will assert first element of tuple
Traceback (most recent call last):
  File "<python-input-0>", line 1, in <module>
    assert ('Petr' == 'Pablo', "That doesn't look right!")
            ^^^^^^^^^^^^^^^^^
AssertionError: That doesn't look right!

Beachten Sie, dass die Verbesserung von Syntaxwarnungen im Allgemeinen außerhalb des Geltungsbereichs dieser PEP liegt.

Spezifikation

Die formale Grammatik der assert-Anweisung wird geändert zu [8]

| 'assert' '(' expression ',' expression [','] ')' &(NEWLINE | ';')
| 'assert' a=expression [',' expression ]

wobei die erste Zeile die neue Form der assert-Anweisung ist, die Klammern zulässt und bis 3.17 eine SyntaxWarning auslöst. Die Vorausschau ist notwendig, um zu verhindern, dass der Parser das Tupel gierig als vollständige Anweisung erfasst, sodass Anweisungen wie assert (a, b) <= c, "something" weiterhin korrekt geparst werden.

Implementierungs-Hinweise

Diese Änderung kann im Parser oder im Compiler implementiert werden. Die Spezifikation, dass eine SyntaxWarning zur Information der Benutzer über die neue Syntax ausgegeben wird, erschwert die Implementierung, da Warnungen während der Kompilierung ausgegeben werden sollten.

Die Autoren glauben, dass eine ideale Implementierung im Parser erfolgen würde [8], was dazu führt, dass assert (x,y) das gleiche AST wie assert x,y hat. Dies erfordert einen zweistufigen Implementierungsplan mit einem notwendigen vorübergehenden Kompromiss.

Implementierung im Parser

Eine reine Parserimplementierung mit der Warnungsspezifikation ist nicht möglich. (Beachten Sie, dass ohne die Warnungsspezifikation die reine Parserimplementierung eine kleine Grammatikänderung ist [5]). Um die Warnung auszugeben, muss der Compiler über die neue Syntax informiert sein, was bedeutet, dass ein optionales Flag erforderlich wäre, da die Information sonst beim Parsen verloren geht. Als solches würde das AST eines assert mit Klammern so aussehen, mit einem paren_syntax=1 Flag

>>> print(ast.dump(ast.parse('assert(True, "Error message")'), indent=4))
Module(
    body=[
        Assert(
            test=Constant(value=True),
            msg=Constant(value='Error message'),
            paren_syntax=1)])

Implementierung im Compiler

Die neue Syntax kann im Compiler implementiert werden, indem Tupel der Länge zwei speziell behandelt werden. Dies hat jedoch den Nebeneffekt, dass das AST während des Übergangszeitraums, in dem die SyntaxWarning ausgegeben wird, überhaupt nicht verändert wird.

Sobald die SyntaxWarning entfernt ist, kann die Implementierung auf Parser-Ebene verschoben werden, wo die geklammerte Form direkt in die gleiche AST-Struktur wie assert expression, message geparst würde. Dieser Ansatz ist rückwärtskompatibler, da viele Tools, die mit ASTs arbeiten, mehr Zeit zur Anpassung haben.

Abwärtskompatibilität

Die Änderung ist technisch nicht abwärtskompatibel. Ob zunächst im Parser oder Compiler implementiert, assert (x,y), was derzeit als assert-Anweisung mit einem 2-Tupel als Subjekt interpretiert wird und immer wahrheitsgetreu ist, wird als assert x,y interpretiert.

Andererseits sind assert-Anweisungen dieser Art immer erfolgreich, so dass sie in Benutzercode effektiv nichts tun. Die Autoren dieses Dokuments sind der Meinung, dass diese Inkompatibilität vorteilhaft ist, da sie diese Fälle im Benutzercode hervorhebt, während sie zuvor unbemerkt geblieben wären. Dieser Fall hat seit Python 3.10 bereits eine SyntaxWarning ausgelöst, so dass es eine Deputationsperiode von über 5 Jahren gab. Das fortgesetzte Auslösen einer SyntaxWarning sollte Überraschungen vermeiden.

Die Änderung wird auch zu Änderungen am AST von assert (x,y) führen, was derzeit ist

Module(
    body=[
        Assert(
            test=Tuple(
                elts=[
                    Name(id='x', ctx=Load()),
                    Name(id='y', ctx=Load())],
                ctx=Load()))],
    type_ignores=[])

die endgültige Implementierung, in Python 3.18, wird zu folgendem AST führen

Module(
    body=[
        Assert(
            test=Name(id='x', ctx=Load()),
            msg=Name(id='y', ctx=Load()))],
    type_ignores=[])

Das Problem dabei ist, dass das AST der ersten Form technisch „falsch“ sein wird, da wir bereits eine spezialisierte Form für das AST einer assert-Anweisung mit Test und Nachricht haben (die zweite). Die anfängliche Implementierung im Compiler wird diese Änderung verzögern und Bedenken hinsichtlich der Abwärtskompatibilität ausräumen, da Tools mehr Zeit zum Anpassen haben.

Wie man das lehrt

Die neue Syntax der assert-Anweisung wird als Teil des Sprachstandards dokumentiert.

Beim Lehren der Form mit Fehlermeldung der assert-Anweisung kann nun darauf hingewiesen werden, dass auch das Hinzufügen von Klammern wie erwartet funktioniert, was es ermöglicht, die Anweisung über mehrere Zeilen zu verteilen.

Referenzimplementierung

Eine Referenzimplementierung im Parser ist in diesem Branch zu finden und eine Referenzimplementierung im Compiler in diesem Branch.

Abgelehnte Ideen

Hinzufügen einer Syntax mit einem Schlüsselwort

Überall sonst in der Python-Syntax trennt das Komma variable „Listen“ von homogenen Elementen, wie die Elemente eines Tupels oder Listen, Parameter/Argumente von Funktionen oder Importziele. Nach der Einführung von except...as in Python 3.0 bleibt die assert-Anweisung die einzige Ausnahme von dieser Konvention.

Es ist möglich, dass die Verwirrung der Benutzer zumindest teilweise auf die Erwartung zurückzuführen ist, dass durch Kommas getrennte Elemente äquivalent sind. Das Einschließen des Ausdrucks und der Nachricht einer assert-Anweisung in Klammern würde sie visuell noch stärker zusammenbinden. Dadurch sieht assert einer Funktionsaufruf ähnlicher und fördert eine falsche Denkweise.

Als mögliche Lösung wurde vorgeschlagen [7], das Komma durch ein Schlüsselwort zu ersetzen, und die Form würde Klammern zulassen, zum Beispiel

assert condition else "message"
assert (condition else "message")

Das Komma könnte dann langsam und vorsichtig deprecationiert werden, beginnend mit dem Fall, in dem es in Klammern erscheint, was bereits eine SyntaxWarning auslöst.

Die Autoren dieser PEP glauben, dass das Hinzufügen einer völlig neuen Syntax erstens das häufige Anfängerproblem, das diese PEP beheben soll, nicht lösen wird und zweitens die Formatierung von assert-Anweisungen über mehrere Zeilen nicht verbessert, was nach Ansicht der Autoren durch die vorgeschlagene Syntax verbessert wird.

Sicherheitsimplikationen

Für diese Änderung gibt es keine Sicherheitsimplikationen.

Danksagungen

Diese Änderung wurde ursprünglich in python/cpython#90325 diskutiert und vorgeschlagen.

Vielen Dank an Petr Viktorin für seine Hilfe während des Entwurfsprozesses dieser PEP.

Fußnoten


Quelle: https://github.com/python/peps/blob/main/peps/pep-0679.rst

Zuletzt geändert: 2025-09-21 17:32:27 GMT