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

Python Enhancement Proposals

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

Inhaltsverzeichnis

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__.py importiert 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- oder gui_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.

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 PYTHONWARNINGS
    PYTHONWARNINGS=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 -W Kommandozeilenschalters aufgeführt sind
  • Explizites Auflisten des Standardfiltersets in der Dokumentation des Moduls warnings unter Verwendung der Notation action::category und action::category:module
  • Explizites Auflisten des folgenden Ausschnitts in der Dokumentation von warnings.simplefilter als empfohlener Ansatz zum standardmäßigen Ausschalten aller Warnungen in einer Python-Anwendung, während sie immer noch über PYTHONWARNINGS oder den -W Kommandozeilenschalter wieder aktiviert werden können
    if 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 unittest Test-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 unittest eingebauten 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 unittest Test-Runner verwendet, um seinen Python-Code in einem Unterprozess zu testen (da selbst unittest die 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 unittest Test-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 unittest Test-Runner meldet derzeit keine Deprecationswarnungen in Unterprozessen, da die Überschreibung des Warnfilters direkt auf das geladene warnings-Modul angewendet wird und nicht auf die Umgebungsvariable PYTHONWARNINGS.
  • 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 Standardbibliotheksmoduls warnings).
  • 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 mit PYTHONWARNINGS=default::DeprecationWarning oder python -W default::DeprecationWarning auszuführen und auf deren stderr-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 von warnings.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

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


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

Zuletzt geändert: 2025-02-01 08:55:40 GMT