PEP 597 – Hinzufügen einer optionalen EncodingWarning
- Autor:
- Inada Naoki <songofacandy at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 05-Juni-2019
- Python-Version:
- 3.10
Zusammenfassung
Fügt eine neue Warnungskategorie EncodingWarning hinzu. Diese wird ausgegeben, wenn das encoding Argument für open() weggelassen wird und die standardmäßige, lokalspezifische Kodierung verwendet wird.
Die Warnung ist standardmäßig deaktiviert. Eine neue Befehlszeilenoption -X warn_default_encoding und eine neue Umgebungsvariable PYTHONWARNDEFAULTENCODING können verwendet werden, um sie zu aktivieren.
Ein Argumentwert "locale" für encoding wird ebenfalls hinzugefügt. Er gibt explizit an, dass die Locale-Kodierung verwendet werden soll, und unterdrückt die Warnung.
Motivation
Die Verwendung der Standardkodierung ist ein häufiger Fehler
Entwickler, die macOS oder Linux verwenden, vergessen möglicherweise, dass die Standardkodierung nicht immer UTF-8 ist.
Zum Beispiel ist die Verwendung von long_description = open("README.md").read() in setup.py ein häufiger Fehler. Viele Windows-Benutzer können solche Pakete nicht installieren, wenn in ihrer UTF-8-kodierten README.md-Datei mindestens ein Nicht-ASCII-Zeichen vorhanden ist (z. B. Emojis, Autorennamen, Copyright-Symbole usw.).
Von den 4000 am häufigsten heruntergeladenen Paketen von PyPI enthalten 489 Nicht-ASCII-Zeichen in ihrer README-Datei, und 82 schlagen fehl, wenn sie aus dem Quellcode in Nicht-UTF-8-Locales installiert werden, da für eine Nicht-ASCII-Datei keine Kodierung angegeben wurde. [1]
Ein weiteres Beispiel ist logging.basicConfig(filename="log.txt"). Einige Benutzer erwarten möglicherweise, dass es standardmäßig UTF-8 verwendet, aber tatsächlich wird die Locale-Kodierung verwendet. [2]
Selbst Python-Experten gehen möglicherweise davon aus, dass die Standardkodierung UTF-8 ist. Dies führt zu Fehlern, die nur unter Windows auftreten; siehe zum Beispiel [3], [4], [5] und [6].
Das Ausgeben einer Warnung, wenn das encoding-Argument weggelassen wird, hilft dabei, solche Fehler zu finden.
Expliziter Weg zur Verwendung der lokalspezifischen Kodierung
open(filename) gibt nicht explizit an, welche Kodierung erwartet wird
- Wenn ASCII angenommen wird, ist dies kein Fehler, kann aber zu Leistungseinbußen unter Windows führen, insbesondere bei lokalspezifischen Kodierungen, die nicht Latin-1 sind.
- Wenn UTF-8 angenommen wird, kann dies ein Fehler oder ein plattformspezifisches Skript sein.
- Wenn die Locale-Kodierung angenommen wird, ist das Verhalten wie erwartet (kann sich jedoch ändern, wenn zukünftige Python-Versionen die Standardeinstellung ändern).
Aus dieser Sicht ist open(filename) kein lesbarer Code.
encoding=locale.getpreferredencoding(False) kann verwendet werden, um die Locale-Kodierung explizit anzugeben, ist aber zu lang und leicht misszuverstehen (man kann zum Beispiel vergessen, False als Argument zu übergeben).
Dieses PEP bietet eine explizite Möglichkeit, die Locale-Kodierung anzugeben.
Vorbereitung auf die Änderung der Standardkodierung zu UTF-8
Da UTF-8 zum De-facto-Standard für Textkodierung geworden ist, könnten wir in Zukunft standardmäßig dazu übergehen.
Eine solche Änderung würde jedoch viele Anwendungen und Bibliotheken beeinträchtigen. Wenn wir überall, wo das encoding-Argument weggelassen wird, DeprecationWarning ausgeben würden, wäre dies zu laut und schmerzhaft.
Obwohl dieses PEP keine Änderung der Standardkodierung vorschlägt, wird es helfen, diese Änderung zu ermöglichen, indem
- die Anzahl der weggelassenen
encoding-Argumente in Bibliotheken reduziert wird, bevor wir standardmäßig eineDeprecationWarningausgeben. - Benutzern ermöglicht wird,
encoding="locale"zu übergeben, um die aktuelle Warnung und jede zukünftig hinzugefügteDeprecationWarningzu unterdrücken und gleichzeitig ein konsistentes Verhalten beizubehalten, wenn spätere Python-Versionen die Standardeinstellung ändern, was die Unterstützung für jede Python-Version >=3.10 gewährleistet.
Spezifikation
EncodingWarning
Fügt eine neue EncodingWarning-Warnungsklasse als Unterklasse von Warning hinzu. Sie wird ausgegeben, wenn das encoding-Argument weggelassen wird und die standardmäßige, lokalspezifische Kodierung verwendet wird.
Optionen zur Aktivierung der Warnung
Die Option -X warn_default_encoding und die Umgebungsvariable PYTHONWARNDEFAULTENCODING werden hinzugefügt. Sie werden verwendet, um EncodingWarning zu aktivieren.
sys.flags.warn_default_encoding wird ebenfalls hinzugefügt. Das Flag ist wahr, wenn EncodingWarning aktiviert ist.
Wenn das Flag gesetzt ist, geben io.TextIOWrapper(), open() und andere Module, die sie verwenden, eine EncodingWarning aus, wenn das encoding-Argument weggelassen wird.
Da EncodingWarning eine Unterklasse von Warning ist, werden sie standardmäßig angezeigt (wenn das Flag warn_default_encoding gesetzt ist), im Gegensatz zu DeprecationWarning.
encoding="locale"
io.TextIOWrapper akzeptiert "locale" als gültiges Argument für encoding. Es hat dieselbe Bedeutung wie das aktuelle encoding=None, außer dass io.TextIOWrapper keine EncodingWarning ausgibt, wenn encoding="locale" angegeben ist.
io.text_encoding()
io.text_encoding() ist ein Helfer für Funktionen mit einem encoding=None-Parameter, die ihn an io.TextIOWrapper() oder open() übergeben.
Eine reine Python-Implementierung würde so aussehen
def text_encoding(encoding, stacklevel=1):
"""A helper function to choose the text encoding.
When *encoding* is not None, just return it.
Otherwise, return the default text encoding (i.e. "locale").
This function emits an EncodingWarning if *encoding* is None and
sys.flags.warn_default_encoding is true.
This function can be used in APIs with an encoding=None parameter
that pass it to TextIOWrapper or open.
However, please consider using encoding="utf-8" for new APIs.
"""
if encoding is None:
if sys.flags.warn_default_encoding:
import warnings
warnings.warn(
"'encoding' argument not specified.",
EncodingWarning, stacklevel + 2)
encoding = "locale"
return encoding
Zum Beispiel kann pathlib.Path.read_text() dies so verwenden
def read_text(self, encoding=None, errors=None):
encoding = io.text_encoding(encoding)
with self.open(mode='r', encoding=encoding, errors=errors) as f:
return f.read()
Durch die Verwendung von io.text_encoding() wird EncodingWarning für den Aufrufer von read_text() ausgegeben, anstatt für read_text() selbst.
Betroffene Standardbibliotheksmodule
Viele Standardbibliotheksmodule werden von dieser Änderung betroffen sein.
Die meisten APIs, die encoding=None akzeptieren, verwenden io.text_encoding(), wie im vorherigen Abschnitt beschrieben.
Wo die Verwendung der Locale-Kodierung als Standardkodierung sinnvoll ist, wird stattdessen encoding="locale" verwendet. Zum Beispiel wird das Modul subprocess die Locale-Kodierung als Standard für Pipes verwenden.
Viele Tests verwenden open() ohne Angabe von encoding, um ASCII-Textdateien zu lesen. Sie sollten mit encoding="ascii" neu geschrieben werden.
Begründung
Opt-in-Warnung
Obwohl DeprecationWarning standardmäßig unterdrückt wird, wäre die ständige Ausgabe von DeprecationWarning, wenn das encoding-Argument weggelassen wird, zu laut.
Laute Warnungen können dazu führen, dass Entwickler die DeprecationWarning ignorieren.
"locale" ist kein Codec-Alias
Wir fügen "locale" nicht als Codec-Alias hinzu, da die Locale zur Laufzeit geändert werden kann.
Zusätzlich prüft TextIOWrapper os.device_encoding(), wenn encoding=None ist. Dieses Verhalten kann nicht in einem Codec implementiert werden.
Abwärtskompatibilität
Die neue Warnung wird nicht standardmäßig ausgegeben, daher ist dieses PEP zu 100 % abwärtskompatibel.
Vorwärtskompatibilität
Das Übergeben von "locale" als Argument für encoding ist nicht zukunftssicher. Code, der es verwendet, funktioniert nicht auf Python-Versionen älter als 3.10 und löst stattdessen LookupError: unknown encoding: locale aus.
Bis Entwickler die Unterstützung für Python 3.9 fallen lassen können, kann EncodingWarning nur zum Finden fehlender encoding="utf-8"-Argumente verwendet werden.
Wie man das lehrt
Für neue Benutzer
Da EncodingWarning zum Schreiben plattformübergreifenden Codes verwendet wird, gibt es keinen Bedarf, neue Benutzer darauf zu schulen.
Wir können einfach empfehlen, UTF-8 für Textdateien zu verwenden und encoding="utf-8" beim Öffnen zu verwenden.
Für erfahrene Benutzer
Die Verwendung von open(filename) zum Lesen von Textdateien, die in UTF-8 kodiert sind, ist ein häufiger Fehler. Es funktioniert möglicherweise nicht unter Windows, da UTF-8 nicht die Standardkodierung ist.
Sie können -X warn_default_encoding oder PYTHONWARNDEFAULTENCODING=1 verwenden, um diese Art von Fehler zu finden.
Das Weglassen des encoding-Arguments ist kein Fehler beim Öffnen von Textdateien, die in der Locale-Kodierung kodiert sind, aber encoding="locale" wird in Python 3.10 und neuer empfohlen, da es expliziter ist.
Referenzimplementierung
Diskussionen
Der neueste Diskussionsfaden ist: https://mail.python.org/archives/list/python-dev@python.org/thread/SFYUP2TWD5JZ5KDLVSTZ44GWKVY4YNCV/
- Warum dies nicht in Linters implementieren?
encoding="locale"undio.text_encoding()müssen in Python implementiert werden.- Es ist schwierig, alle Aufrufer von Funktionen zu finden, die
open()oderTextIOWrapper()wrappen (siehe Abschnittio.text_encoding()).
- Viele Entwickler werden die Option nicht verwenden.
- Einige werden es tun und die Warnungen an die Bibliotheken melden, die sie verwenden, sodass die Option es wert ist, auch wenn viele Entwickler sie nicht aktivieren.
- Zum Beispiel habe ich [7] und [8] gefunden, indem ich
pip install -U pipausgeführt habe, und [9], indem ichtoxmit der Referenzimplementierung ausgeführt habe. Dies zeigt, wie diese Option verwendet werden kann, um potenzielle Probleme zu finden.
Referenzen
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0597.rst
Zuletzt geändert: 2025-02-01 08:56:52 GMT