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

Python Enhancement Proposals

PEP 687 – Isolating modules in the standard library

Autor:
Erlend Egeberg Aasland <erlend at python.org>, Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Akzeptiert
Typ:
Standards Track
Benötigt:
489, 573, 630
Erstellt:
04-Apr-2022
Python-Version:
3.12
Post-History:
04-Apr-2022, 11-Apr-2022
Resolution:
Discourse-Nachricht

Inhaltsverzeichnis

Zusammenfassung

Erweiterungen in der Standardbibliothek werden auf Multi-Phasen-Initialisierung (PEP 489) umgestellt, und wo immer möglich, wird aller Zustand in Modulobjekten statt in prozessglobalen Variablen gespeichert.

Hinweis zur Rückdatierung

Ein Großteil dieses Vorschlags wurde bereits umgesetzt. Wir legen diese PEP vor, um die Änderungen zu erklären, Konsens darüber zu suchen, ob sie gut sind, die verbleibenden Änderungen vorzuschlagen und Best Practices für neue Module festzulegen.

Motivation & Begründung

Die informative PEP 630 beschreibt den Hintergrund, die Motivation, die Begründung, die Auswirkungen und die Implementierungshinweise der vorgeschlagenen Änderungen, wie sie generell für jedes Erweiterungsmodul (nicht nur für die Standardbibliothek) gelten.

Sie ist ein integraler Bestandteil dieses Vorschlags. Lesen Sie sie zuerst.

Diese PEP diskutiert Besonderheiten der Standardbibliothek.

Spezifikation

Der Hauptteil von PEP 630 wird in ein HOWTO in der Python-Dokumentation umgewandelt, und diese PEP wird zurückgezogen (als "Final" markiert).

Alle Erweiterungsmodule in der Standardbibliothek werden auf die in PEP 489 eingeführte Multi-Phasen-Initialisierung umgestellt.

Alle stdlib-Erweiterungsmodule werden *isoliert*. Das heißt:

  • Von dem Modul definierte Typen, Funktionen und andere Objekte sind entweder unveränderlich oder werden nicht mit anderen Modulinstanzen geteilt.
  • Modulspezifischer Zustand wird nicht mit anderen Modulinstanzen geteilt, es sei denn, er stellt globalen Zustand dar.

    Zum Beispiel wird _csv.field_size_limit eine modulspezifische Zahl abrufen/setzen. Andererseits werden Funktionen wie readline.get_history_item oder os.getpid weiterhin mit Zustand arbeiten, der prozessglobal ist (außerhalb des Moduls und möglicherweise mit anderen Bibliotheken, einschließlich nicht-Python-Bibliotheken, geteilt).

Konvertierung zu Heap-Typen

Statische Typen, die keinen Zugriff auf Modulzustand benötigen und keine anderen Gründe für eine Konvertierung haben, sollten statisch bleiben.

Typen, deren Methoden Zugriff auf ihre Modulinstanz benötigen, werden gemäß PEP 630 in Heap-Typen konvertiert, mit folgenden Überlegungen:

  • Alle Standardbibliothekstypen, die früher statische Typen waren, sollten unveränderlich bleiben. Heap-Typen müssen mit dem Flag Py_TPFLAGS_IMMUTABLE_TYPE definiert werden, um die Unveränderlichkeit beizubehalten. Siehe bpo-43908.

    Tests sollten sicherstellen, dass TypeError ausgelöst wird, wenn versucht wird, ein neues Attribut eines unveränderlichen Typs zu erstellen.

  • Ein statischer Typ mit tp_new = NULL hat keinen öffentlichen Konstruktor, aber Heap-Typen erben den Konstruktor von der Basisklasse. Stellen Sie sicher, dass Typen, die zuvor nicht instanziierbar waren, diese Funktion beibehalten; verwenden Sie Py_TPFLAGS_DISALLOW_INSTANTIATION. Fügen Sie Tests mit test.support.check_disallow_instantiation() hinzu. Siehe bpo-43916.
  • Konvertierte Heap-Typen können unbeabsichtigt serialisierbar (pickle-fähig) werden. Testen Sie, ob der Aufruf von pickle.dumps vor und nach der Konvertierung dasselbe Ergebnis liefert. Wenn der Test fehlschlägt, fügen Sie eine __reduce__-Methode hinzu, die TypeError auslöst. Siehe PR-21002 für ein Beispiel.

Diese Probleme werden dem Devguide hinzugefügt, um zukünftige Konvertierungen zu unterstützen.

Wenn eine andere Art von Problem gefunden wird, sollte das betreffende Modul unverändert bleiben, bis eine Lösung gefunden und zum Devguide hinzugefügt wurde und bereits konvertierte Module überprüft und behoben wurden.

Prozess

Der folgende Prozess sollte dem Devguide hinzugefügt werden und bis zur Konvertierung aller Module bestehen bleiben. Alle neuen Erkenntnisse sollten dort oder im allgemeinen HOWTO dokumentiert werden.

Teil 1: Vorbereitung

  1. Öffnen Sie eine Diskussion, entweder im Bug-Tracker oder auf Discourse. Beziehen Sie den Modulbetreuer und/oder Code-Owner ein. Erklären Sie den Grund und die Begründung für die Änderungen.
  2. Identifizieren Sie Leistungsengpässe bei globalem Zustand. Erstellen Sie eine Proof-of-Concept-Implementierung und messen Sie die Leistungsauswirkungen. pyperf ist ein gutes Werkzeug für Benchmarking.
  3. Erstellen Sie einen Implementierungsplan. Für kleine Module mit wenigen Typen kann ein einzelner PR die Aufgabe erledigen. Für größere Module mit vielen Typen und möglicherweise auch externen Bibliotheks-Callbacks sind mehrere PRs erforderlich.

Teil 2: Implementierung

Hinweis: Dies ist ein vorgeschlagener Implementierungsplan für ein komplexes Modul, basierend auf den Erfahrungen mit anderen Modulen. Vereinfachen Sie ihn ruhig für kleinere Module.

  1. Fügen Sie Argument Clinic hinzu, wo immer möglich; dies ermöglicht Ihnen die einfache Verwendung der definierenden Klasse, um Modulzustand von Typmethoden abzurufen.
  2. Bereiten Sie sich auf Modulzustand vor; richten Sie eine Modulzustands-struct ein, fügen Sie eine Instanz als statische globale Variable hinzu und erstellen Sie Hilfs-Stubs zum Abrufen des Modulzustands.
  3. Fügen Sie relevante globale Variablen zur Modulzustands-struct hinzu und ändern Sie Code, der auf den globalen Zustand zugreift, so dass stattdessen die Modulzustandshilfen verwendet werden. Dieser Schritt kann in mehrere PRs aufgeteilt werden.
  4. Konvertieren Sie statische Typen nach Bedarf in Heap-Typen.
  5. Konvertieren Sie die globale Modulzustandsstruktur in echten Modulzustand.
  6. Implementieren Sie Multi-Phasen-Initialisierung.

Die Schritte 4 bis 6 sollten vorzugsweise in einer einzigen Alpha-Entwicklungsphase landen.

Abwärtskompatibilität

Erweiterungsmodule in der Standardbibliothek können nun mehr als einmal geladen werden. Zum Beispiel führt das Löschen eines solchen Moduls aus sys.modules und das erneute Importieren zu einer frischen Modulinstanz, die von allen zuvor geladenen Instanzen isoliert ist.

Dies kann sich auf Code auswirken, der das frühere Verhalten erwartete: Globale Variablen von Erweiterungsmodulen wurden flach aus dem zuerst geladenen Modul kopiert.

Sicherheitsimplikationen

Keine bekannt.

Wie man das lehrt

Ein großer Teil dieses Vorschlags ist ein HOWTO für erfahrene Benutzer, das in die Dokumentation verschoben wird.

Anfänger sollten nicht betroffen sein.

Referenzimplementierung

Die meisten Änderungen sind nun im Hauptzweig, als Commits für diese Probleme

Als Beispiel sind Änderungen und Korrekturen im _csv-Modul


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

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