Python Development Mode

Hinzugefügt in Version 3.7.

Der Python Development Mode führt zusätzliche Laufzeitprüfungen ein, die zu teuer sind, um standardmäßig aktiviert zu werden. Er sollte nicht gesprächiger sein als der Standard, wenn der Code korrekt ist; neue Warnungen werden nur ausgegeben, wenn ein Problem erkannt wird.

Er kann mit der Befehlszeilenoption -X dev oder durch Setzen der Umgebungsvariable PYTHONDEVMODE auf 1 aktiviert werden.

Siehe auch Python Debug Build.

Auswirkungen des Python Development Mode

Die Aktivierung des Python Development Mode ist vergleichbar mit dem folgenden Befehl, jedoch mit zusätzlichen unten beschriebenen Effekten

PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -W default -X faulthandler

Auswirkungen des Python-Entwicklungsmodus

  • Fügt einen default Warnfilter hinzu. Die folgenden Warnungen werden angezeigt

    Normalerweise werden die oben genannten Warnungen durch die standardmäßigen Warnfilter gefiltert.

    Er verhält sich so, als ob die Befehlszeilenoption -W default verwendet wird.

    Verwenden Sie die Befehlszeilenoption -W error oder setzen Sie die Umgebungsvariable PYTHONWARNINGS auf error, um Warnungen als Fehler zu behandeln.

  • Installiert Debug-Hooks an Speicherallokatoren, um Folgendes zu überprüfen:

    • Buffer-Unterlauf

    • Buffer-Überlauf

    • Verletzung der Speicherallokator-API

    • Unsichere Verwendung des GIL

    Siehe die C-Funktion PyMem_SetupDebugHooks().

    Er verhält sich so, als ob die Umgebungsvariable PYTHONMALLOC auf debug gesetzt ist.

    Um den Python Development Mode zu aktivieren, ohne Debug-Hooks an Speicherallokatoren zu installieren, setzen Sie die Umgebungsvariable PYTHONMALLOC auf default.

  • Ruft faulthandler.enable() beim Python-Start auf, um Handler für die Signale SIGSEGV, SIGFPE, SIGABRT, SIGBUS und SIGILL zu installieren, um den Python-Traceback bei einem Absturz auszugeben.

    Er verhält sich so, als ob die Befehlszeilenoption -X faulthandler verwendet wird oder wenn die Umgebungsvariable PYTHONFAULTHANDLER auf 1 gesetzt ist.

  • Aktiviert den Asyncio Debug Mode. Zum Beispiel prüft asyncio auf nicht awaited Coroutinen und protokolliert sie.

    Er verhält sich so, als ob die Umgebungsvariable PYTHONASYNCIODEBUG auf 1 gesetzt ist.

  • Überprüft die Argumente encoding und errors für Zeichenketten-Kodierungs- und Dekodierungsoperationen. Beispiele: open(), str.encode() und bytes.decode().

    Standardmäßig wird aus Performance-Gründen das Argument errors nur beim ersten Kodierungs-/Dekodierungsfehler überprüft und das Argument encoding wird bei leeren Zeichenketten manchmal ignoriert.

  • Der Destruktor von io.IOBase protokolliert close()-Ausnahmen.

  • Setzt das Attribut dev_mode von sys.flags auf True.

Der Python Development Mode aktiviert das Modul tracemalloc nicht standardmäßig, da die damit verbundenen Kosten (für Leistung und Speicher) zu hoch wären. Die Aktivierung des Moduls tracemalloc liefert zusätzliche Informationen über den Ursprung einiger Fehler. Zum Beispiel protokolliert ResourceWarning den Traceback, wo die Ressource alloziert wurde, und ein Buffer-Überlauf-Fehler protokolliert den Traceback, wo der Speicherblock alloziert wurde.

Der Python Development Mode verhindert nicht, dass die Befehlszeilenoption -O assert-Anweisungen entfernt oder __debug__ auf False setzt.

Der Python Development Mode kann nur beim Start von Python aktiviert werden. Sein Wert kann aus sys.flags.dev_mode gelesen werden.

Geändert in Version 3.8: Der Destruktor von io.IOBase protokolliert nun close()-Ausnahmen.

Geändert in Version 3.9: Die Argumente encoding und errors werden nun für Zeichenketten-Kodierungs- und Dekodierungsoperationen überprüft.

Beispiel für ResourceWarning

Beispiel eines Skripts, das die Anzahl der Zeilen der in der Befehlszeile angegebenen Textdatei zählt

import sys

def main():
    fp = open(sys.argv[1])
    nlines = len(fp.readlines())
    print(nlines)
    # The file is closed implicitly

if __name__ == "__main__":
    main()

Das Skript schließt die Datei nicht explizit. Standardmäßig gibt Python keine Warnung aus. Beispiel mit README.txt, das 269 Zeilen hat

$ python script.py README.txt
269

Die Aktivierung des Python Development Mode zeigt eine ResourceWarning-Warnung an

$ python -X dev script.py README.txt
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback

Zusätzlich zeigt die Aktivierung von tracemalloc die Zeile an, in der die Datei geöffnet wurde

$ python -X dev -X tracemalloc=5 script.py README.rst
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
Object allocated at (most recent call last):
  File "script.py", lineno 10
    main()
  File "script.py", lineno 4
    fp = open(sys.argv[1])

Die Lösung besteht darin, die Datei explizit zu schließen. Beispiel mit einem Kontextmanager

def main():
    # Close the file explicitly when exiting the with block
    with open(sys.argv[1]) as fp:
        nlines = len(fp.readlines())
    print(nlines)

Das Nicht-Schließen einer Ressource kann dazu führen, dass sie viel länger offen bleibt als erwartet; dies kann beim Beenden von Python zu schwerwiegenden Problemen führen. Das ist in CPython schlecht, aber in PyPy noch schlimmer. Das explizite Schließen von Ressourcen macht eine Anwendung deterministischer und zuverlässiger.

Beispiel für den Fehler "Bad file descriptor"

Skript, das die erste Zeile von sich selbst anzeigt

import os

def main():
    fp = open(__file__)
    firstline = fp.readline()
    print(firstline.rstrip())
    os.close(fp.fileno())
    # The file is closed implicitly

main()

Standardmäßig gibt Python keine Warnung aus

$ python script.py
import os

Der Python Development Mode zeigt eine ResourceWarning und protokolliert einen "Bad file descriptor"-Fehler bei der Finalisierung des Datei-Objekts

$ python -X dev script.py
import os
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
Traceback (most recent call last):
  File "script.py", line 10, in <module>
    main()
OSError: [Errno 9] Bad file descriptor

os.close(fp.fileno()) schließt den Dateideskriptor. Wenn der Dateiobjekt-Finalisierer versucht, den Dateideskriptor erneut zu schließen, schlägt dies mit dem Fehler Bad file descriptor fehl. Ein Dateideskriptor darf nur einmal geschlossen werden. Im schlimmsten Fall kann das doppelte Schließen zu einem Absturz führen (siehe bpo-18748 für ein Beispiel).

Die Lösung besteht darin, die Zeile os.close(fp.fileno()) zu entfernen oder die Datei mit closefd=False zu öffnen.