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
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_limiteine modulspezifische Zahl abrufen/setzen. Andererseits werden Funktionen wiereadline.get_history_itemoderos.getpidweiterhin 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_TYPEdefiniert werden, um die Unveränderlichkeit beizubehalten. Siehe bpo-43908.Tests sollten sicherstellen, dass
TypeErrorausgelöst wird, wenn versucht wird, ein neues Attribut eines unveränderlichen Typs zu erstellen. - Ein statischer Typ mit
tp_new = NULLhat 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 SiePy_TPFLAGS_DISALLOW_INSTANTIATION. Fügen Sie Tests mittest.support.check_disallow_instantiation()hinzu. Siehe bpo-43916. - Konvertierte Heap-Typen können unbeabsichtigt serialisierbar (
pickle-fähig) werden. Testen Sie, ob der Aufruf vonpickle.dumpsvor und nach der Konvertierung dasselbe Ergebnis liefert. Wenn der Test fehlschlägt, fügen Sie eine__reduce__-Methode hinzu, dieTypeErrorauslö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
- Ö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.
- Identifizieren Sie Leistungsengpässe bei globalem Zustand. Erstellen Sie eine Proof-of-Concept-Implementierung und messen Sie die Leistungsauswirkungen.
pyperfist ein gutes Werkzeug für Benchmarking. - 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.
- 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.
- Bereiten Sie sich auf Modulzustand vor; richten Sie eine Modulzustands-
structein, fügen Sie eine Instanz als statische globale Variable hinzu und erstellen Sie Hilfs-Stubs zum Abrufen des Modulzustands. - Fügen Sie relevante globale Variablen zur Modulzustands-
structhinzu 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. - Konvertieren Sie statische Typen nach Bedarf in Heap-Typen.
- Konvertieren Sie die globale Modulzustandsstruktur in echten Modulzustand.
- 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
- bpo-40077, Konvertierung von statischen Typen in Heap-Typen: Verwendung von PyType_FromSpec()
- bpo-46417, Löschen von statischen Typen in Py_Finalize() für eingebettetes Python
- bpo-1635741, Py_Finalize() löscht beim Beenden nicht alle Python-Objekte
Als Beispiel sind Änderungen und Korrekturen im _csv-Modul
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-0687.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT