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
defaultWarnfilter hinzu. Die folgenden Warnungen werden angezeigtNormalerweise werden die oben genannten Warnungen durch die standardmäßigen Warnfilter gefiltert.
Er verhält sich so, als ob die Befehlszeilenoption
-W defaultverwendet wird.Verwenden Sie die Befehlszeilenoption
-W erroroder setzen Sie die UmgebungsvariablePYTHONWARNINGSauferror, 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
PYTHONMALLOCaufdebuggesetzt ist.Um den Python Development Mode zu aktivieren, ohne Debug-Hooks an Speicherallokatoren zu installieren, setzen Sie die Umgebungsvariable
PYTHONMALLOCaufdefault.Ruft
faulthandler.enable()beim Python-Start auf, um Handler für die SignaleSIGSEGV,SIGFPE,SIGABRT,SIGBUSundSIGILLzu installieren, um den Python-Traceback bei einem Absturz auszugeben.Er verhält sich so, als ob die Befehlszeilenoption
-X faulthandlerverwendet wird oder wenn die UmgebungsvariablePYTHONFAULTHANDLERauf1gesetzt ist.Aktiviert den Asyncio Debug Mode. Zum Beispiel prüft
asyncioauf nicht awaited Coroutinen und protokolliert sie.Er verhält sich so, als ob die Umgebungsvariable
PYTHONASYNCIODEBUGauf1gesetzt ist.Überprüft die Argumente encoding und errors für Zeichenketten-Kodierungs- und Dekodierungsoperationen. Beispiele:
open(),str.encode()undbytes.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.IOBaseprotokolliertclose()-Ausnahmen.
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.