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

Python Enhancement Proposals

PEP 803 – Stabile ABI für Free-Threaded Builds

Autor:
Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Entwurf
Typ:
Standards Track
Benötigt:
703, 793, 697
Erstellt:
19-Aug-2025
Python-Version:
3.15
Post-History:
08-Sep-2025

Inhaltsverzeichnis

Zusammenfassung

Version 3.15 der Stable ABI wird mit Free-Threaded und GIL-aktivierten Builds kompatibel sein. Um dies zu ermöglichen, wird die interne PyObject-Struktur und verwandte APIs ab Version 3.15 der Limited API entfernt, was eine Migration zu neuen APIs für gängige Aufgaben wie die Definition von Modulen und die meisten Klassen erfordert.

Binäre Distributionen (Wheels), die mit Limited API Version 3.15 und höher erstellt wurden, sollten den ABI-Tag abi3.abi3t verwenden.

Terminologie

Dieser PEP verwendet „GIL-aktivierter Build“ als Antonym zu „Free-Threaded Build“, d.h. ein Interpreter oder eine Erweiterung, die ohne Py_GIL_DISABLED erstellt wurde.

Motivation

Die Stable ABI ist derzeit nicht für Free-Threaded Builds verfügbar. Erweiterungen werden bei Definition von Py_LIMITED_API nicht gebaut, und für GIL-aktivierte Builds von CPython erstellte Erweiterungen werden bei Free-Threaded Builds nicht geladen (oder stürzen ab).

In seinem Akzeptanz-Post für PEP 779 erklärte der Steering Council, dass er „erwartet, dass die Stable ABI für Free-Threading für Python 3.15 vorbereitet und definiert wird“.

Dieser PEP schlägt die Stable ABI für Free-Threading vor.

Hintergrund

Pythons Stable ABI, wie in PEP 384 und PEP 652 definiert, bietet eine Möglichkeit, Erweiterungsmodule zu kompilieren, die auf mehreren Minor-Versionen des CPython-Interpreters geladen werden können. Mehrere Projekte nutzen dies, um die Anzahl der zu bauenden und zu verteilenden Wheels (binäre Artefakte) für jede Veröffentlichung zu begrenzen und/oder um das Testen mit Vorabversionen von Python zu erleichtern.

Da Free-Threaded Builds (PEP 703) auf dem besten Weg sind, schließlich zum Standard zu werden (PEP 779), benötigen wir eine Möglichkeit, die Stable ABI für diese Builds verfügbar zu machen.

Um gegen die Stable ABI zu bauen, muss die Erweiterung eine *Limited API* verwenden, d.h. nur eine Teilmenge der Funktionen, Strukturen usw., die CPython bereitstellt. Die Limited API ist versioniert, und das Bauen gegen Limited API 3.X liefert eine Erweiterung, die ABI-kompatibel mit CPython 3.X und *jeder* späteren Version ist (obwohl Fehler in CPython manchmal praktisch zu Inkompatibilitäten führen). Außerdem ist die Limited API nicht „stabil“: neuere Versionen können APIs entfernen, die Teil älterer Versionen waren.

Dieser PEP schlägt die bisher bedeutendste solche Entfernung vor.

Begründung

Das Design in diesem PEP macht mehrere Annahmen

Eine ABI
Ein einzelnes kompiliertes Erweiterungsmodul sollte sowohl Free-Threaded als auch GIL-aktivierte Builds unterstützen.
Keine Rückwärtskompatibilität jetzt
Die neue Limited API wird CPython 3.14 und älter nicht unterstützen. Projekte, die diese Unterstützung benötigen, können separate Erweiterungen speziell für den Free-Threaded Interpreter 3.14 und für ältere Stable ABI-Versionen bauen.

Wir werden jedoch die Möglichkeit, die Kompatibilität auf CPython 3.14 und älter auszudehnen, nicht blockieren.

API-Änderungen sind in Ordnung
Die neue Limited API kann von Erweiterungsautoren erfordern, dass sie erhebliche Änderungen an ihrem Code vornehmen. Projekte, die dies (noch) nicht können, können weiterhin Limited API 3.14 verwenden, was zu Erweiterungen führt, die nur mit GIL-aktivierten Builds kompatibel sind.
Keine zusätzliche Konfiguration
Wir führen keine neuen „Regler“ ein, die beeinflussen, welche API verfügbar ist und womit die ABI kompatibel ist.

Spezifikation

Opaque PyObject

Version 3.15 der Limited API wird

Implikationen

Das Obsoletmachen der PyObject, PyVarObject und PyModuleDef Strukturen bedeutet

  • Ihre Felder dürfen nicht direkt zugegriffen werden.

    Anstelle von o->ob_type müssen Erweiterungen Py_TYPE(o) verwenden. Diese Verwendung ist seit einiger Zeit die bevorzugte Praxis.

  • Ihre Größe und Ausrichtung werden nicht verfügbar sein. Ausdrücke wie sizeof(PyObject) funktionieren nicht mehr.
  • Sie können nicht in andere Strukturen eingebettet werden. Dies betrifft hauptsächlich Instanzstrukturen von Erweiterungs-definierten Typen, die mit der in PEP 697 hinzugefügten API definiert werden müssen – d.h. mit einer struct *ohne* PyObject (oder einer anderen Basisklassen-Struktur) am Anfang, wobei PyObject_GetTypeData()-Aufrufe zum Zugriff auf den Speicher erforderlich sind.
  • Variablen dieser Typen können nicht erstellt werden. Dies betrifft hauptsächlich statische PyModuleDef-Variablen, die zur Definition von Erweiterungsmodulen benötigt werden. Erweiterungen müssen auf die in PEP 793 hinzugefügte API umsteigen.

Die folgenden Funktionen werden in der Praxis (in der neuen Limited API) unbrauchbar, da eine Erweiterung keine gültigen, statisch zugewiesenen Eingaben für sie erstellen kann. Um den Übergang für Erweiterungsentwickler zu erleichtern, werden sie noch nicht aus der Limited API entfernt werden.

Neuer Export Hook (PEP 793)

Die Implementierung dieses PEP erfordert, dass PEP 793 (PyModExport): Ein neuer Einstiegspunkt für C-Erweiterungsmodule) akzeptiert wird, der einen neuen „Export Hook“ für die Definition von C-Erweiterungsmodulen bereitstellt. Die Verwendung des neuen Hooks wird in der Limited API 3.15 obligatorisch.

Laufzeit-ABI-Prüfungen

Benutzer – oder besser gesagt die Werkzeuge, die sie zum Bauen und Installieren von Erweiterungen verwenden – werden weiterhin dafür verantwortlich sein, keine inkompatiblen Erweiterungen in Pythons Importpfade zu legen. Diese Entscheidung ist sinnvoll, da Werkzeuge typischerweise viel reichhaltigere Metadaten haben als das, was CPython überprüfen kann.

CPython wird jedoch eine Verteidigungslinie gegen veraltete oder falsch konfigurierte Werkzeuge oder menschliche Fehler in Form eines neuen *Modul-Slots* hinzufügen, der grundlegende ABI-Informationen enthält. Diese Informationen werden beim Laden eines Moduls überprüft, und inkompatible Erweiterungen werden abgelehnt. Die genauen Details bleiben der C API Working Group überlassen (siehe Issue 72).

Dieser Slot wird mit dem neuen Export Hook, der in PEP 793 hinzugefügt wird, *obligatorisch*. (Dieser PEP sagt derzeit „es gibt keine erforderlichen Slots“; er wird aktualisiert.)

Prüfung auf älteres abi3

Zusätzlich werden in Free-Threaded Builds PyModuleDef_Init() Erweiterungen erkennen, die die Stable ABI vor Free-Threading verwenden, eine informative Nachricht ausgeben, wenn eine geladen wird, *und* eine Ausnahme auslösen. (Implementierungshinweis: Eine Nachricht wird ausgegeben, bevor die Ausnahme ausgelöst wird, da Erweiterungen, die versuchen, eine Ausnahme mit einer inkompatiblen ABI zu behandeln, wahrscheinlich abstürzen und die Nachricht der Ausnahme verlieren.)

Diese Prüfung auf älteres abi3 basiert auf internen Bitmustern und kann in zukünftigen CPython-Versionen entfernt werden, falls das interne Objektlayout geändert werden muss.

Der abi3t Wheel-Tag

Wheels, die eine Stable ABI verwenden, die mit Free-Threaded CPython-Builds kompatibel ist, sollten einen neuen ABI-Tag verwenden: abi3t. Der Name ist so gewählt, dass er die Tatsache widerspiegelt, dass diese ABI abi3 ähnelt, außer den notwendigen Änderungen zur Unterstützung von Free-Threading (das den Buchstaben t in bestehenden, versionsspezifischen ABI-Tags wie cp314t verwendet).

Da Wheels, die mit Limited API 3.15 erstellt wurden, sowohl mit GIL-aktivierten als auch mit Free-Threaded Builds kompatibel sein werden, sollten sie die komprimierte ABI-Tag-Menge abi3.abi3t verwenden.

Neue API

Die Implementierung dieses PEP ermöglicht das Bauen von Erweiterungen, die auf Free-Threaded Python erfolgreich geladen werden können, aber nicht notwendigerweise solche, die ohne GIL Thread-sicher sind.

Limited API zur Ermöglichung von Thread-Sicherheit ohne GIL – vermutlich PyMutex, PyCriticalSection und ähnliche – werden über die C API Working Group oder in einem Folge-PEP hinzugefügt.

Rückwärts- und Vorwärtskompatibilität

Limited API 3.15 wird aufgrund der Notwendigkeit, die neue PyModExport API aus PEP 793 zu verwenden, nicht rückwärtskompatibel mit älteren CPython-Versionen sein.

Erweiterungsautoren, die nicht wechseln können, können weiterhin Limited API 3.14 und älter verwenden. Für die Kompatibilität mit Free-Threaded Builds können sie mit versionsspezifischer ABI kompilieren – zum Beispiel auf CPython 3.15 ohne Definition von Py_LIMITED_API kompilieren.

Limited API 3.15 wird vorwärtskompatibel mit zukünftigen Versionen von CPython 3.x sein. Ältere Versionen der Limited API (d.h. 3.14 und älter) werden weiterhin vorwärtskompatibel mit GIL-aktivierten Builds von CPython 3.x sein, beginnend mit der Version, die die gegebene Limited API eingeführt hat.

Kompatibilitätsübersicht

Die folgende Tabelle fasst die Kompatibilität von Wheel-Tags mit CPython-Interpretern zusammen. „GIL“ steht für GIL-aktivierten Interpreter; „FT“ steht für einen Free-Threaded.

Wheel-Tag 3.14 (GIL) 3.14 (FT) 3.15 (GIL) 3.15 (FT) 3.16+ (GIL) 3.16+ (FT)
cp314-cp314
cp314-cp314t
cp314-abi3
cp315-cp315
cp315-cp315t
cp315-abi3
cp315-abi3.abi3t

Die folgende Tabelle fasst zusammen, welcher Wheel-Tag für eine mit einem bestimmten Interpreter und dem Py_LIMITED_API Makro erstellte Erweiterung verwendet werden sollte.

Um den Wheel-Tag zu erhalten… Kompilieren auf… mit Py_LIMITED_API gesetzt auf… Hinweis
cp314-cp314 3.14 (GIL) (nicht gesetzt) bestehend
cp314-cp314t 3.14 (FT) (nicht gesetzt) bestehend
cp314-abi3 3.14+ (GIL) PY_PACK_VERSION(3, 14) bestehend
cp315-cp315 3.15 (GIL) (nicht gesetzt) fortgesetzt
cp315-cp315t 3.15 (FT) (nicht gesetzt) fortgesetzt
cp315-abi3 3.15+ (GIL) PY_PACK_VERSION(3, 15) eingestellt
cp315-abi3.abi3t 3.15+ (beliebig) PY_PACK_VERSION(3, 15) neu

Werte in der Spalte *Hinweis*

  • *bestehend*: Der Wheel-Tag wird derzeit verwendet
  • *fortgesetzt*: Der Wheel-Tag setzt das bestehende Schema fort
  • *eingestellt*: Der Wheel-Tag setzt das bestehende Schema fort, wird aber nicht mehr empfohlen. Ältere Werkzeuge können ihn möglicherweise noch generieren.
  • *neu*: Vorgeschlagen in diesem PEP.

Sicherheitsimplikationen

Keine bekannt.

Wie man das lehrt

Ein Porting-Leitfaden wird erklären müssen, wie zu den in PEP 697 (Limited C API für erweiterte Opaque-Typen) und PEP 793 (PyModExport) hinzugefügten APIs migriert wird.

Referenzimplementierung

Dieser PEP kombiniert mehrere Stücke, die einzeln implementiert wurden

  • Opaque PyObject ist im CPython Main-Branch nach Definition des _Py_OPAQUE_PYOBJECT Makros verfügbar. Implementiert in GitHub Pull Request python/cpython#136505.
  • Für PyModExport siehe PEP 793.
  • Ein Versionsprüfungs-Slot wurde in GitHub Pull Request python/cpython#137212 implementiert.
  • Eine Prüfung auf älteres abi3 wurde in GitHub Pull Request python/cpython#137957 implementiert.
  • Für Wheel-Tags gibt es noch keine Implementierung.
  • Ein Porting-Leitfaden wurde noch nicht geschrieben.

Abgelehnte Ideen

Eine alternative stabile ABI für Free-Threading hinzufügen

Es wäre möglich, Folgendes zu tun:

  • Eine neue stabile ABI („abi3t“) speziell für Free-Threading hinzuzufügen, die mit der bestehenden abi3 inkompatibel wäre. Erweiterungen bräuchten keine Code-Änderungen, um abi3t zu targeten, und Builds wären mit Free-Threaded CPython (3.14 und höher) kompatibel.
  • Ein zusätzliches Makro („Py_OPAQUE_PYOBJECT“) zu definieren, das PyObject opak macht, wie in diesem PEP. Erweiterungen bräuchten Code-Änderungen wie in diesem PEP, und wie in diesem PEP wären kompilierte Erweiterungen („abi3.abi3t“) mit allen Builds von CPython 3.15+ kompatibel.

Dieses Schema wurde als zu komplex abgelehnt. Es würde auch das Free-Threading-Speicherlayout von PyObject zum Teil der Stable ABI machen und zukünftige Anpassungen verhindern.

Shims zur Kompatibilität mit CPython 3.14

Das Hauptproblem, das die Kompatibilität mit Python 3.14 verhindert, ist, dass mit opakem PyObject und PyModuleDef die Initialisierung eines Erweiterungsmoduls nicht machbar ist. Die Lösung, PEP 793, wird erst in Python 3.15 hinzugefügt.

Es ist möglich, dies zu umgehen, indem die Tatsache genutzt wird, dass die ABIs von 3.14 (sowohl Free-Threaded als auch GIL-aktiviert) „eingefroren“ sind, sodass eine Erweiterung den laufenden Interpreter abfragen kann und für 3.14 eine struct-Definition verwendet, die dem PyModuleDef des erkannten Builds entspricht.

Dies ist im Moment zu aufwendig, um es in Pythons Limited API zu unterstützen und zu testen, könnte aber in Zukunft erlaubt sein.

Verwendung des Python Wheel-Tags zur Bestimmung der Kompatibilität

Eine frühere Version dieses PEP vermied das Hinzufügen eines neuen Wheel-Tags (abi3t) und gab an, dass Wheels mit dem Tag abi3 mit Free-Threading kompatibel wären, wenn das *Python-Tag* cp315 oder höher wäre.

Ein solches Schema würde für diesen PEP funktionieren, kann aber nicht ausdrücken, dass eine Erweiterung sowohl mit GIL-aktivierten als auch mit Free-Threaded Builds von CPython 3.14 oder niedriger kompatibel ist. Das Hinzufügen eines neuen expliziten Tags bedeutet, dass *falls* wir solche Wheels in Zukunft zulassen, Paketierungs-Tools keine zusätzlichen Änderungen benötigen, um sie zu unterstützen. Sie würden als cp314-abi3.abi3t getaggt.

Hinzufügen eines abi4 Wheel-Tags

Anstelle von abi3t könnten wir die Version hochstufen und abi4 als Wheel-ABI-Tag verwenden. Im Wheel-Tag ist der Unterschied größtenteils kosmetischer Natur.

Allerdings schlägt dieser PEP nicht vor, den *Dateinamen*-Tag zu ändern: Erweiterungen werden mit Erweiterungen wie .abi3.so benannt. Dies zu ändern und gleichzeitig die Kompatibilität mit GIL-aktivierten Builds zu erhalten, wäre eine unnötige technische Änderung.

Die Verwendung von abi3.abi4 in Wheel-Tags, aber nur .abi3 in Dateinamen, würde inkonsistenter aussehen als abi3.abi3t und .abi3.


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

Zuletzt geändert: 2025-09-19 13:35:24 GMT