PEP 565 – Show DeprecationWarning in __main__
- Autor:
- Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 12-Nov-2017
- Python-Version:
- 3.7
- Post-History:
- 12-Nov-2017, 25-Nov-2017
- Resolution:
- Python-Dev Nachricht
Zusammenfassung
In Python 2.7 und Python 3.2 wurden die Standard-Warnfilter aktualisiert, um DeprecationWarning standardmäßig zu unterdrücken. Dies geschah, damit Warnungen über veraltete Funktionen in Entwicklungswerkzeugen, die selbst in Python geschrieben waren (z. B. Linter, statische Analysetools, Test-Runner, Code-Generatoren) sowie in anderen Anwendungen, die zufällig in Python geschrieben wurden, für die Benutzer nicht sichtbar waren, es sei denn, diese hatten sich explizit dafür entschieden, sie zu sehen.
Diese Änderung hatte jedoch den unglücklichen Nebeneffekt, dass DeprecationWarning für ihren primären Verwendungszweck – die rechtzeitige Benachrichtigung von Benutzern über API-Änderungen (sei es in CPython, der Standardbibliothek oder Drittanbieter-Bibliotheken) – deutlich weniger wirksam wurde.
Um diese Situation zu verbessern, schlägt dieses PEP eine einzige Anpassung des Standard-Warnfilters vor: standardmäßige Anzeige von Deprecationswarnungen, die dem Hauptmodul zugeordnet sind.
Diese Änderung bedeutet, dass Code, der an der interaktiven Eingabeaufforderung eingegeben wird, und Code in Einzelfail-Skripten diese Warnungen standardmäßig wieder melden, während sie für verpackten Code, der als Teil eines importierbaren Moduls verteilt wird, weiterhin standardmäßig unterdrückt werden.
Das PEP schlägt außerdem eine Reihe kleiner Anpassungen an der Referenzimplementierung und der Dokumentation der Standardbibliothek vor, um das Warnsystem für neue Python-Entwickler zugänglicher zu machen.
Als Teil der Dokumentationsaktualisierungen wird deutlicher gemacht, dass der unittest Test-Runner standardmäßig alle Warnungen anzeigt, wenn Testfälle ausgeführt werden, und dass andere Test-Runner aufgefordert werden, diesem Beispiel zu folgen.
Spezifikation
Neuer Standard-Filtereintrag für Warnungen
Die aktuelle Liste der Standard-Warnfilter besteht aus
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::BytesWarning
ignore::ResourceWarning
Der Standard unittest Test-Runner verwendet dann warnings.catch_warnings() und warnings.simplefilter('default'), um die Standardfilter während der Ausführung von Testfällen zu überschreiben.
Die in diesem PEP vorgeschlagene Änderung besteht darin, die Liste der Standard-Warnfilter zu aktualisieren zu
default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::BytesWarning
ignore::ResourceWarning
Dies bedeutet, dass in Fällen, in denen der nominale Speicherort der Warnung (wie durch den Parameter stacklevel in warnings.warn bestimmt) das __main__ Modul ist, das erste Vorkommen jeder DeprecationWarning wieder gemeldet wird.
Diese Änderung wird dazu führen, dass DeprecationWarning standardmäßig angezeigt wird für
- direkt an der interaktiven Eingabeaufforderung ausgeführter Code
- direkt als Teil eines Einzelfail-Skripts ausgeführter Code
Während sie standardmäßig weiterhin verborgen bleibt für
- Code, der aus einem anderen Modul in einer
zipapp-Archivdatei__main__.pyimportiert wird - Code, der aus einem anderen Modul in einem ausführbaren Paket-Submodul
__main__importiert wird - Code, der aus einem ausführbaren Skript-Wrapper importiert wird, der zur Installationszeit basierend auf einer
console_scripts- odergui_scripts-Eintragspunktdefinition generiert wurde
Das bedeutet, dass Tool-Entwickler, die ein installierbares oder ausführbares Artefakt (wie eine zipapp-Archivdatei) zur Verteilung an ihre Benutzer erstellen, keine Änderung gegenüber dem Status Quo feststellen werden, während Benutzer von eher ad-hoc persönlichen oder lokal verteilten Skripten wahrscheinlich wieder relevante Deprecationswarnungen sehen werden (wie sie es in Python 2.6 und früher taten).
Zusätzlicher Anwendungsfall für FutureWarning
Die Dokumentation der Standardbibliothek wird aktualisiert, um die Verwendung von FutureWarning (anstelle von DeprecationWarning) für Rückwärtskompatibilitätswarnungen, die für *Benutzer* einer Anwendung bestimmt sind, explizit zu empfehlen. (Dies wird zusätzlich zur bestehenden Verwendung von FutureWarning zur Warnung vor Konstrukten, die gültiger Code in der Zukunft bleiben, aber andere Semantik haben werden.)
Dies ergibt die folgenden drei unterschiedlichen Kategorien von Rückwärtskompatibilitätswarnungen mit drei unterschiedlichen Zielgruppen
PendingDeprecationWarning: Standardmäßig für allen Code unterdrückt. Die Zielgruppe sind Python-Entwickler, die ein aktives Interesse an der Sicherstellung der zukünftigen Kompatibilität ihrer Software haben (z. B. professionelle Python-Anwendungsentwickler mit spezifischen Support-Pflichten).DeprecationWarning: Standardmäßig für Code gemeldet, der direkt im__main__-Modul ausgeführt wird (da solcher Code als relativ unwahrscheinlich gilt, eine dedizierte Testsuite zu haben), aber standardmäßig für Code in anderen Modulen unterdrückt. Die Zielgruppe sind Python-Entwickler, die das Risiko eingehen, dass Upgrades ihrer Abhängigkeiten (einschließlich Upgrades von Python selbst) ihre Software brechen (z. B. Entwickler, die Python zum Skripting von Umgebungen verwenden, in denen jemand anderes die Zeitplanung von Abhängigkeitsupgrades kontrolliert).FutureWarning: Standardmäßig für allen Code gemeldet. Die Zielgruppe sind Benutzer von in Python geschriebenen Anwendungen und nicht andere Python-Entwickler (z. B. Warnung vor der Verwendung einer veralteten Einstellung in einem Konfigurationsdateiformat).
Für Autoren von Bibliotheken und Frameworks, die sicherstellen möchten, dass ihre API-Kompatibilitätswarnungen zuverlässiger von ihren Benutzern gesehen werden, wird empfohlen, eine benutzerdefinierte Warnungsklasse zu verwenden, die von DeprecationWarning in Python 3.7+ und von FutureWarning in früheren Versionen abgeleitet ist.
Empfohlene Filtereinstellungen für Test-Runner
Entwickler von Test-Runnern wird geraten, eine Logik zu implementieren, die der folgenden entspricht, wenn sie ihre Standard-Warnfilter bestimmen
if not sys.warnoptions:
warnings.simplefilter("default")
Dies aktiviert effektiv alle Warnungen standardmäßig, als ob die Kommandozeilenoption -Wd übergeben worden wäre.
Beachten Sie, dass die tatsächliche Aktivierung von BytesWarning in einer Testsuite immer noch das Übergeben der Option -b an den Interpreter über die Kommandozeile erfordert. Für implizite Byte-Konvertierungs- und Byte-Vergleichswarnungen wird nur die Warnfilter-Maschinerie verwendet, um zu bestimmen, ob sie als Warnungen ausgegeben oder als Ausnahmen ausgelöst werden sollen – wenn das Kommandozeilenflag nicht gesetzt ist, gibt der Interpreter die Warnung gar nicht erst aus.
Empfohlene Filtereinstellungen für interaktive Shells
Entwickler von interaktiven Shells wird geraten, einen Filter hinzuzufügen, der DeprecationWarning in dem Namensraum aktiviert, in dem Benutzercode eingegeben und ausgeführt wird.
Wenn dieser Namensraum __main__ ist (wie es bei der Standard-CPython-REPL der Fall ist), sind keine weiteren Änderungen über die in diesem PEP vorgeschlagenen hinaus erforderlich.
Interaktive Shell-Implementierungen, die einen anderen Namensraum als __main__ verwenden, müssen ihren eigenen Filter hinzufügen. Zum Beispiel verwendet IPython den folgenden Befehl ([6]), um einen geeigneten Filter einzurichten
warnings.filterwarnings("default", category=DeprecationWarning,
module=self.user_ns.get("__name__"))
Weitere Dokumentationsaktualisierungen
Die aktuelle Referenzdokumentation für das Warnsystem ist relativ kurz, was spezifische *Beispiele* für mögliche Einstellungen für die -W Kommandozeilenoption oder die Umgebungsvariable PYTHONWARNINGS angeht, die bestimmte Ergebnisse erzielen.
Die folgenden Verbesserungen werden als Teil der Implementierung dieses PEP vorgeschlagen
- Explizites Auflisten der folgenden Einträge unter der Beschreibung der Umgebungsvariable
PYTHONWARNINGSPYTHONWARNINGS=error # Convert to exceptions PYTHONWARNINGS=always # Warn every time PYTHONWARNINGS=default # Warn once per call location PYTHONWARNINGS=module # Warn once per calling module PYTHONWARNINGS=once # Warn once per Python process PYTHONWARNINGS=ignore # Never warn
- Explizites Auflisten der entsprechenden Kurzoptionen (
-We,-Wa,-Wd,-Wm,-Wo,-Wi) für jede der Warnaktionsarten, die unter der Dokumentation des-WKommandozeilenschalters aufgeführt sind - Explizites Auflisten des Standardfiltersets in der Dokumentation des Moduls
warningsunter Verwendung der Notationaction::categoryundaction::category:module - Explizites Auflisten des folgenden Ausschnitts in der Dokumentation von
warnings.simplefilterals empfohlener Ansatz zum standardmäßigen Ausschalten aller Warnungen in einer Python-Anwendung, während sie immer noch überPYTHONWARNINGSoder den-WKommandozeilenschalter wieder aktiviert werden könnenif not sys.warnoptions: warnings.simplefilter("ignore")
Keine dieser Änderungen ist *neu* (sie funktionieren bereits in allen noch unterstützten Python-Versionen), aber sie sind angesichts der aktuellen Struktur der zugehörigen Dokumentation nicht besonders offensichtlich.
Referenzimplementierung
Eine Referenzimplementierung ist im PR [4] verfügbar, der von der zugehörigen Tracker-Aufgabe für dieses PEP [5] verlinkt ist.
Als Nebeneffekt der Implementierung dieses PEP wird die interne Liste der Warnfilter die Verwendung einfacher Zeichenketten als Teil von Filterdefinitionen zulassen (zusätzlich zur bestehenden Verwendung von kompilierten regulären Ausdrücken). Wenn vorhanden, werden die einfachen Zeichenketten nur auf exakte Übereinstimmungen verglichen. Dieser Ansatz ermöglicht es, den neuen Standardfilter beim Interpreterstart hinzuzufügen, ohne dass ein früher Zugriff auf das Modul re erforderlich ist.
Motivation
Wie in [1] diskutiert und in [2] erwähnt, haben Python 2.7 und Python 3.2 die Standardbehandlung von DeprecationWarning so geändert, dass
- die Warnung während der normalen Codeausführung standardmäßig unterdrückt wurde
- der
unittestTest-Runner aktualisiert wurde, um sie beim Ausführen von Tests wieder zu aktivieren
Die Absicht war, Fälle von Tooling-Ausgaben wie der folgenden zu vermeiden
$ devtool mycode/
/usr/lib/python3.6/site-packages/devtool/cli.py:1: DeprecationWarning: 'async' and 'await' will become reserved keywords in Python 3.7
async = True
... actual tool output ...
Selbst wenn devtool ein Werkzeug speziell für Python-Programmierer ist, ist dies keine besonders nützliche Warnung, da sie bei jeder Ausführung angezeigt wird, obwohl der wichtigste hilfreiche Schritt, den ein Endbenutzer unternehmen kann, darin besteht, einen Fehler an die Entwickler von devtool zu melden.
Die Warnung ist noch weniger hilfreich für allgemeine Entwicklertools, die für mehr als nur Python verwendet werden, und fast völlig *un*hilfreich für Anwendungen, die zufällig in Python geschrieben sind und nicht unbedingt für ein Entwicklerpublikum bestimmt sind.
Diese Änderung erwies sich jedoch als unbeabsichtigte Folgen für die folgenden Zielgruppen
- jeder, der einen anderen Test-Runner als den standardmäßigen in
unittesteingebauten verwendet (die Aufforderung an Drittanbieter-Test-Runner, ihre Standard-Warnfilter zu ändern, wurde nie explizit gemacht, daher verlassen sich viele von ihnen immer noch auf die Interpreter-Standardeinstellungen, die für produktive Anwendungen geeignet sind) - jeder, der den Standard
unittestTest-Runner verwendet, um seinen Python-Code in einem Unterprozess zu testen (da selbstunittestdie Warnungs-Einstellungen nur im aktuellen Prozess anpasst) - jeder, der Python-Code an der interaktiven Eingabeaufforderung oder als Teil eines direkt ausgeführten Skripts schreibt, das überhaupt keine Python-weite Testsuite hatte
In diesen Fällen wurde DeprecationWarning fast vollständig äquivalent zu PendingDeprecationWarning: sie wurde einfach nie gesehen.
Einschränkungen des PEP-Umfangs
Dieses PEP dient speziell dazu, sowohl die vorgeschlagene Ergänzung des Standard-Warnfilters für 3.7 zu erläutern, *als auch* die Begründung für die ursprüngliche Änderung der Behandlung von DeprecationWarning in Python 2.7 und 3.2 klarer darzulegen.
Dieses PEP löst nicht alle bekannten Probleme mit dem aktuellen Ansatz zur Behandlung von Deprecationswarnungen. Insbesondere
- Der Standard
unittestTest-Runner meldet derzeit keine Deprecationswarnungen, die beim Modulimport ausgelöst werden, da die Überschreibung des Warnfilters nur während der Testausführung erfolgt, nicht während der Testentdeckung und -ladung. - Der Standard
unittestTest-Runner meldet derzeit keine Deprecationswarnungen in Unterprozessen, da die Überschreibung des Warnfilters direkt auf das geladenewarnings-Modul angewendet wird und nicht auf die UmgebungsvariablePYTHONWARNINGS. - Die Standardbibliothek bietet keine einfache Möglichkeit, sich dafür zu entscheiden, alle Warnungen zu sehen, die *von* einer bestimmten Abhängigkeit vor dem Upgrade ausgegeben werden (das Drittanbieter-Modul
warn[3] bietet dies zwar, aber die Aktivierung erfordert Monkeypatching des Standardbibliotheksmodulswarnings). - Wenn Software in unterstützende Module ausgelagert wurde, diese Module aber wenig oder keine automatisierte Testabdeckung haben, wird das erneute standardmäßige Aktivieren von Deprecationswarnungen in
__main__wahrscheinlich nicht dazu beitragen, Probleme mit der API-Kompatibilität zu finden. Kurzfristig ist die beste derzeit verfügbare Antwort, betroffene Anwendungen mitPYTHONWARNINGS=default::DeprecationWarningoderpython -W default::DeprecationWarningauszuführen und auf derenstderr-Ausgabe zu achten. Langfristig ist dies wirklich eine Frage für Forscher, die sich mit statischer Analyse von Python-Code beschäftigen: wie man die Verwendung veralteter APIs zuverlässig findet und wie man ableitet, dass eine API oder ein Parameter aufgrund vonwarnings.warn-Aufrufen veraltet ist, ohne tatsächlich entweder den Code, der die API bereitstellt, oder den Code, der darauf zugreift, auszuführen.
Obwohl dies reale Probleme mit dem Status Quo sind, sind sie von der Berücksichtigung in diesem PEP ausgeschlossen, da sie komplexere Lösungen als einen einzigen zusätzlichen Eintrag im Standard-Warnfilter erfordern, und deren Lösung zumindest potenziell nicht den PEP-Prozess durchlaufen muss.
Für jeden, der daran interessiert ist, sie weiter zu verfolgen, wären die ersten beiden Anfragen zur Verbesserung des Moduls unittest, die dritte eine Anfrage zur Verbesserung des Moduls warnings, während die letzte nur ein PEP erfordern würde, wenn die Ableitung von API-Veralterungen aus deren Inhalten als unlösbares Codeanalyseproblem angesehen würde und stattdessen eine explizite Funktions- und Parameterkennzeichnungs-Syntax in Annotationen vorgeschlagen würde.
Die CPython-Referenzimplementierung wird in 3.7 auch die folgenden verwandten Änderungen enthalten
- eine neue Kommandozeilenoption
-X dev, die mehrere entwicklerzentrierte Einstellungen (einschließlich-Wd) zu einem Kommandozeilenflag kombiniert: https://github.com/python/cpython/issues/76224 - Änderung des Verhaltens in Debug-Builds, um mehr der Warnungen anzuzeigen, die in regulären Interpreter-Builds standardmäßig ausgeschaltet sind: https://github.com/python/cpython/issues/76269
Unabhängig von den in diesem PEP vorgeschlagenen Änderungen an den Standardfiltern ist Issue 32229 [7] ein Vorschlag zur Hinzufügung einer warnings.hide_warnings API, um es Anwendungsentwicklern zu erleichtern, Warnungen während des normalen Betriebs zu unterdrücken und sie beim Testen leicht sichtbar zu machen.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0565.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT