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

Python Enhancement Proposals

PEP 652 – Aufrechterhaltung der Stable ABI

Autor:
Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Typ:
Standards Track
Erstellt:
09-Feb-2021
Python-Version:
3.10
Resolution:
Python-Dev Nachricht

Inhaltsverzeichnis

Wichtig

Diese PEP ist ein historisches Dokument. Die aktuelle, kanonische Dokumentation finden Sie nun unter C API-Stabilität (Benutzerdokumentation) und Änderung der C API von Python (Entwicklerdokumentation).

×

Siehe PEP 1, um Änderungen vorzuschlagen.

Zusammenfassung

CPython's Limited C-API und Stable ABI, eingeführt in PEP 384, werden in einer einzigen, endgültigen Datei formalisiert, getestet und dokumentiert.

Motivation

PEP 384 definierte eine Limited API und eine Stable ABI, die es Extendern und Embeddern von CPython ermöglichen, Erweiterungsmodule zu kompilieren, die binärkompatibel mit jeder nachfolgenden Version von 3.x sind. Theoretisch bringt dies mehrere Vorteile

  • Ein Modul kann einmal pro Plattform erstellt und mehrere Python-Versionen unterstützt werden, was Zeit, Energie und Aufmerksamkeit der Wartungsleute für Builds reduziert (im Austausch für potenziell schlechtere Leistung).
  • Binäre Wheels, die die Stable ABI verwenden, funktionieren mit neuen Versionen von CPython während der Vorabversionsphase und können in Umgebungen getestet werden, in denen das Bauen aus dem Quellcode nicht praktikabel ist.
  • Als willkommener Nebeneffekt des Verbergens von Implementierungsdetails durch die Limited API wird diese API zu einem praktikablen Ziel für alternative Python-Implementierungen, die mit der vollständigen C API inkompatibel wären.

Rückblickend hat PEP 384 und seine Implementierung jedoch mehrere Probleme

  • Sie ist schlecht definiert. Laut PEP 384 sind Funktionen opt-out: Alle Funktionen, die nicht speziell markiert sind, gehören zur Stable ABI. In der Praxis gibt es für Windows eine Liste, die opt-in ist. Für Benutzer gibt es ein #define, das nur die Stable ABI verfügbar machen sollte, aber es gibt keinen Prozess, der sicherstellt, dass es auf dem neuesten Stand gehalten wird. Ebenso wenig gibt es einen Prozess zur Aktualisierung der Dokumentation.
  • Bis vor kurzem wurde die Stable ABI überhaupt nicht getestet. Sie neigt dazu zu brechen. Beispielsweise kann die Änderung einer Funktion in ein Makro die Stable ABI brechen, da das Funktionssymbol entfernt wird.
  • Es gibt keine Möglichkeit, Teile der Limited API zu deprecaten.
  • Sie ist unvollständig. Einige Operationen sind in der Stable ABI nicht verfügbar, mit wenig Grund außer "wir haben vergessen". (Dieser letzte Punkt ist einer, bei dem die PEP nicht helfen wird.)

Diese PEP definiert die Limited API klarer und führt Prozesse ein, die darauf abzielen, die Stable ABI und Limited API nützlicher und robuster zu machen.

Begründung

Diese PEP enthält viele Klarstellungen und Definitionen, aber nur eine große technische Änderung: Die Stable ABI wird explizit in einer von Menschen gepflegten "Manifest"-Datei aufgeführt.

Es gab Bemühungen, solche Listen automatisch zu sammeln, z. B. durch Scannen der von Python exportierten Symbole. Eine solche Automatisierung mag einfacher zu warten erscheinen als eine handgefertigte Datei, hat aber erhebliche Probleme: Zum Beispiel hat die Menge der exportierten Symbole plattformspezifische Variationen. Auch sind die Kosten für die Aktualisierung eines expliziten Manifests gering im Vergleich zur Gesamtarbeit, die in die Änderung einer API fließen sollte, die für immer unterstützt werden muss (oder bis Python 3 sein Lebensende erreicht, falls das früher eintritt).

Diese PEP schlägt vor, Dinge aus dem Manifest automatisch zu generieren: zunächst Dokumentation und DLL-Inhalte, mit späteren Möglichkeiten, auch Tests zu automatisieren.

Stable ABI vs. Limited API

PEP 384 und dieses Dokument befassen sich mit der Limited API und der Stable ABI, zwei verwandten, aber unterschiedlichen Konzepten. Kurz gesagt

  • Die Stable ABI ist ein Versprechen, dass bestimmte Erweiterungen, die mit CPython 3.x kompiliert wurden, binärkompatibel mit allen nachfolgenden Versionen von CPython 3.x sind.
  • Die Limited API ist eine Teilmenge der C API von CPython, die solche Erweiterungen erzeugt.

Dieser Abschnitt klärt diese Begriffe und definiert einige ihrer Semantiken (entweder vorbestehend oder hier neu vorgeschlagen).

Das Wort "Erweiterungen" wird als Kurzform für allen Code verwendet, der die Python-API nutzt, z. B. Erweiterungsmodule oder Software, die Python einbettet.

Stabile ABI

Die CPython Stable ABI ist ein Versprechen, dass Erweiterungen, die gegen eine bestimmte Stable ABI-Version kompiliert wurden, mit jedem neueren Interpreter der gleichen Hauptversion kompatibel sind.

Die Stable ABI definiert keine vollständige binäre Schnittstelle: Wichtige Details wie das Layout von Strukturen im Speicher oder Aufrufkonventionen werden von der Plattform und dem Compiler sowie dessen Einstellungen bestimmt. Das Stable ABI-Versprechen gilt nur, wenn diese niederen Details ebenfalls stabil sind.

Eine Erweiterung, die mit der CPython 3.10 Stable ABI erstellt wurde, ist beispielsweise mit CPython 3.11, 3.12 usw. nutzbar. Sie ist nicht unbedingt mit CPython 4.0 oder mit CPython 3.10 auf einer anderen Plattform kompatibel.

Die Stable ABI ist nicht generell vorwärtskompatibel: Eine Erweiterung, die mit CPython 3.10 erstellt und getestet wurde, ist im Allgemeinen nicht mit CPython 3.9 kompatibel.

Hinweis

Beispielsweise kann ab Python 3.10 der Py_tp_doc Slot auf NULL gesetzt werden, während in älteren Versionen ein NULL Wert wahrscheinlich den Interpreter abstürzen lässt.

Die Stable ABI tauscht Leistung gegen ihre Stabilität. Beispielsweise verwenden Erweiterungen, die für eine bestimmte CPython-Version erstellt wurden, automatisch schnellere Makros anstelle von Funktionen in der Stable ABI.

Zukünftige Python-Versionen können einige Mitglieder der Stable ABI als veraltet markieren. Veraltete Mitglieder funktionieren weiterhin, können aber Probleme wie reduzierte Leistung oder, in den extremsten Fällen, Speicher-/Ressourcenlecks aufweisen.

Limited API

Das Stable ABI-Versprechen gilt für Erweiterungen, die aus Code kompiliert werden, der sich auf die Limited API (Application Programming Interface) beschränkt. Die Limited API ist eine Teilmenge der C API von CPython.

Erweiterungen, die auf die Limited API abzielen, sollten das Präprozessor-Makro Py_LIMITED_API entweder auf 3 oder die aktuelle PYTHON_API_VERSION setzen. Dies aktiviert Stable ABI-Versionen mehrerer Funktionen und begrenzt die Definitionen auf die Limited API. (Beachten Sie jedoch, dass das Makro nicht perfekt ist: Aufgrund technischer Probleme oder Versäumnisse können auch mit aktivierter Makrodefinition einige Nicht-Limited-API-Funktionen verfügbar sein.)

Die Limited API ist nicht garantiert stabil. In Zukunft können Teile der Limited API als veraltet markiert werden. Sie können sogar entfernt werden, solange die Stable ABI stabil bleibt und die allgemeine Rückwärtskompatibilitätsrichtlinie von Python, PEP 387, eingehalten wird.

Hinweis

Zum Beispiel kann eine Funktionsdeklaration aus öffentlichen Header-Dateien entfernt, aber in der Bibliothek belassen werden. Dies ist derzeit eine Möglichkeit für die Zukunft; diese PEP schlägt keinen konkreten Prozess für Deprecations und Entfernungen vor.

Das Ziel der Limited API ist es, alles abzudecken, was zur Interaktion mit dem Interpreter benötigt wird. Der Hauptgrund, eine öffentliche API nicht in die Limited-Teilmengeneinzuschließen, sollte sein, dass sie Implementierungsdetails benötigt, die sich zwischen CPython-Versionen ändern (wie Speicherlayouts von Strukturen) – normalerweise aus Leistungsgründen.

Die Limited API ist nicht auf CPython beschränkt. Andere Implementierungen sind ermutigt, sie zu implementieren und bei ihrer Gestaltung mitzuwirken.

Spezifikation

Um die Stable ABI nützlicher und robuster zu machen, werden folgende Änderungen vorgeschlagen.

Stable ABI Manifest

Alle Mitglieder der Stable ABI – Funktionen, Typedefs, Strukturen, Daten, Makros und Konstanten – werden explizit in einer einzigen "Manifest"-Datei aufgeführt: Misc/stable_abi.txt.

Für Strukturen werden alle Felder, auf die Benutzer der Stable ABI zugreifen dürfen, explizit aufgeführt.

Das Manifest dient auch als definitive Liste der Limited API. Mitglieder, die nicht Teil der Limited API, aber Teil der Stable ABI sind (z. B. PyObject.ob_type, das durch das Makro Py_TYPE zugänglich ist), werden entsprechend annotiert.

Für Elemente, die nur auf einigen Systemen verfügbar sind, wird das Manifest das Feature-Makro aufzeichnen, das ihre Anwesenheit bestimmt (wie MS_WINDOWS oder HAVE_FORK). Um die Implementierung (und die Nutzung aus anderen Sprachen als C) zu erleichtern, werden alle solchen Makros einfache Namen sein. Wenn ein zukünftiges Element ein "negatives" Makro oder einen komplexen Ausdruck benötigt (wie ein hypothetisches #ifndef MACOSX oder #if defined(POSIX) && !defined(LINUX)), wird ein neues Feature-Makro abgeleitet.

Das Format des Manifests kann bei Bedarf geändert werden. Es sollte nur von Skripten im CPython-Repository verarbeitet werden. Wenn eine stabile Liste benötigt wird, kann ein Skript hinzugefügt werden, um sie zu generieren.

Folgendes wird aus dem ABI-Manifest generiert

  • Quellcode für die Windows-Shared-Bibliothek, PC/python3dll.c.
  • Eingabe für die Dokumentation (siehe unten).
  • Testfall, der die Laufzeitverfügbarkeit von Symbolen prüft (siehe unten).

Das Folgende wird im Rahmen der kontinuierlichen Integration gegen das Stable ABI-Manifest geprüft

  • Die Zusammenfassung der Referenzzählung, Doc/data/refcounts.txt, enthält alle Funktionen in der Stable ABI (neben anderen).
  • Die Funktionen/Strukturen, die deklariert sind, und Konstanten/Makros, die definiert sind, wenn Python.h mit Py_LIMITED_API definiert ist. (Anfangs nur Linux; Prüfungen auf anderen Systemen können in Zukunft hinzugefügt werden.)

Nach der anfänglichen Implementierung werden Details wie Funktionsargumente hinzugefügt und das Manifest auf interne Konsistenz geprüft (z. B. alle in Funktionssignaturen verwendeten Typen gehören zur API).

Inhalt der Stable ABI

Das anfängliche Stable ABI-Manifest wird Folgendes enthalten

  • Die in PEP 384 spezifizierte Stable ABI.
  • Alles, was in PC/python3dll.c aufgeführt ist.
  • Alle Strukturen (Struktur-Typedefs), die diese Funktionen zurückgeben oder als Argumente erhalten. (Felder solcher Strukturen werden nicht notwendigerweise hinzugefügt.)
  • Neue Typ-Slots, wie z. B. Py_am_aiter.
  • Die Typ-Flags Py_TPFLAGS_DEFAULT, Py_TPFLAGS_BASETYPE, Py_TPFLAGS_HAVE_GC, Py_TPFLAGS_METHOD_DESCRIPTOR.
  • Die Aufrufkonventionen METH_* (außer veralteten).
  • Alle API, die von Makros benötigt wird, ist Teil der Stable ABI (als nicht Teil der Limited API annotiert).

Elemente, die in CPython nicht mehr vorhanden sind, wenn diese PEP akzeptiert wird, werden aus der Liste entfernt.

Zusätzliche Elemente können dem anfänglichen Manifest gemäß der folgenden Checkliste hinzugefügt werden.

Dokumentation der Limited API

Hinweise wie "Teil der Limited API" werden automatisch zur Python-Dokumentation hinzugefügt, ähnlich wie die Hinweise zu Funktionen, die geliehene Referenzen zurückgeben.

Eine vollständige Liste aller Mitglieder der Limited API wird ebenfalls der Dokumentation hinzugefügt.

Testen der Stable ABI

Ein automatisch generiertes Testmodul wird hinzugefügt, um sicherzustellen, dass alle Symbole, die in die Stable ABI aufgenommen wurden, zur Kompilierzeit verfügbar sind.

Änderung der Limited API

Eine Checkliste zur Änderung der Limited API, einschließlich des Hinzufügens neuer Elemente und des Entfernens bestehender, wird dem Devguide hinzugefügt. Die Checkliste wird 1) bewährte Praktiken und häufige Fallstricke im Design der Python C API erwähnen und 2) den Entwickler durch die zu ändernden Dateien und auszuführenden Skripte führen, wenn die Limited API geändert wird.

Unten ist der erste Vorschlag für die Checkliste. (Nachdem die PEP akzeptiert wurde, siehe den Devguide für die aktuelle Version.)

Beachten Sie, dass die Checkliste für neue Änderungen gilt; mehrere Elemente in der *bestehenden* Limited API sind "grandfathered" und könnten heute nicht hinzugefügt werden.

Designüberlegungen

  • Stellen Sie sicher, dass die Änderung die Stable ABI jeder Python-Version seit 3.5 nicht bricht.
  • Stellen Sie sicher, dass keine freigegebenen Namen privat sind (d. h. mit einem Unterstrich beginnen).
  • Stellen Sie sicher, dass die neue API gut dokumentiert ist.
  • Stellen Sie sicher, dass die Typen aller Parameter und Rückgabewerte der hinzugefügten Funktion(en) und aller Felder der hinzugefügten Struktur(en) Teil der Limited API (oder Standard-C) sind.
  • Stellen Sie sicher, dass die neue API und ihre beabsichtigte Nutzung Standard-C folgen, nicht nur Features der aktuell unterstützten Plattformen. Beachten Sie insbesondere den C-Dialekt, der in PEP 7 spezifiziert ist.
    • Gießen Sie keinen Funktionszeiger nach void* (ein Datenzeiger) oder umgekehrt.
  • Stellen Sie sicher, dass die neue API den Konventionen für Referenzzählung folgt. (Das Befolgen dieser Konventionen erleichtert die API-Verständlichkeit und die Nutzung in anderen Python-Implementierungen.)
    • Geben Sie keine geliehenen Referenzen von Funktionen zurück.
    • Stehlen Sie keine Referenzen von Funktionsargumenten.
  • Stellen Sie sicher, dass die Besitzregeln und Lebensdauern aller zutreffenden Strukturfelder, Argumente und Rückgabewerte gut definiert sind.
  • Denken Sie an die Benutzerfreundlichkeit. (In C ist die Benutzerfreundlichkeit selbst nicht sehr wichtig; was *nützlich* ist, ist die Reduzierung des Boilerplate-Codes, der zur Nutzung der API benötigt wird. Fehler verstecken sich gerne in Boilerplates.)
    • Wenn eine Funktion oft mit einem bestimmten Wert für ein Argument aufgerufen wird, erwägen Sie, sie als Standard zu behandeln (verwendet, wenn NULL übergeben wird).
  • Denken Sie an zukünftige Erweiterungen: Zum Beispiel, wenn es möglich ist, dass zukünftige Python-Versionen ein neues Feld zu Ihrer Struktur hinzufügen müssen, wie wird das gemacht?
  • Treffen Sie so wenige Annahmen wie möglich über Details, die sich in zukünftigen CPython-Versionen ändern oder sich zwischen C API-Implementierungen unterscheiden könnten
    • Der GIL
    • Garbage Collection
    • Speicherlayout von PyObject, Listen/Tupeln und anderen Strukturen

Wenn das Befolgen dieser Richtlinien die Leistung beeinträchtigen würde, fügen Sie eine schnelle Funktion (oder ein Makro) zur Nicht-Limited API und eine stabile Entsprechung zur Limited API hinzu.

Wenn etwas unklar ist oder Sie einen guten Grund haben, die Richtlinien zu brechen, erwägen Sie, die Änderung auf der capi-sig Mailingliste zu besprechen.

Verfahren

  • Verschieben Sie die Deklaration in eine Header-Datei direkt unter Include/, in einen #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03yy0000 Block (wobei yy der Ziel-CPython-Version entspricht).
  • Erstellen Sie einen Eintrag im Stable ABI-Manifest, Misc/stable_abi.txt.
  • Generieren Sie die automatisch generierten Dateien neu mit make regen-all. (oder die Alternative für Nicht-make-Plattformen)
  • Bauen Sie Python und führen Sie die Checks mit make check-abi aus. (oder die Alternative für Nicht-make-Plattformen)

Ratschläge für Extender und Embedder

Die folgenden Hinweise werden der Dokumentation hinzugefügt, zusammen mit besseren Informationen zu diesem Thema und welche Garantien wir bieten

Erweiterungsautoren sollten mit allen unterstützten Python-Versionen testen und vorzugsweise mit der niedrigsten unterstützten Version bauen.

Das Kompilieren mit definiertem Py_LIMITED_API ist keine Garantie dafür, dass Ihr Code der Limited API oder der Stable ABI entspricht. Py_LIMITED_API deckt nur Definitionen ab, aber eine API umfasst auch andere Aspekte, wie erwartete Semantiken.

Beispiele für Probleme, die Py_LIMITED_API nicht abdeckt, sind

  • Aufruf einer Funktion mit ungültigen Argumenten
  • Eine Funktion, die ab Python 3.9 `NULL`-Werte für ein Argument akzeptiert, wird fehlschlagen, wenn sie unter Python 3.8 aufgerufen wird. Nur Tests mit 3.8 (oder niedrigeren Versionen) werden dieses Problem aufdecken.
  • Einige Strukturen enthalten einige Felder, die Teil der Stable ABI sind, und andere Felder, die es nicht sind. Py_LIMITED_API filtert solche "privaten" Felder nicht heraus.
  • Code, der etwas verwendet, das nicht als Teil der Stable ABI dokumentiert ist, aber auch mit definiertem Py_LIMITED_API verfügbar ist, kann in Zukunft brechen. Trotz der besten Bemühungen des Teams können solche Probleme auftreten.

Hinweis für Python-Redistributoren

Das Stable ABI-Versprechen beruht auf stabilen zugrundeliegenden ABI-Details, wie dem Layout von Strukturen im Speicher und Funktionsaufrufkonventionen, die vom Compiler und dessen Einstellungen beeinflusst werden. Damit das Versprechen gilt, dürfen sich diese Details zwischen CPython 3.x-Releases auf einer bestimmten Plattform nicht ändern.

Abwärtskompatibilität

Rückwärtskompatibilität ist eine verdammt gute Idee!

Diese PEP zielt auf vollständige Kompatibilität mit der bestehenden Stable ABI und Limited API ab, definiert diese Begriffe aber expliziter. Sie ist möglicherweise nicht konsistent mit einigen Interpretationen dessen, was die bestehende Stable ABI/Limited API ist.

Sicherheitsimplikationen

Keine bekannt.

Wie man das lehrt

Technische Dokumentation wird in Doc/c-api/stable bereitgestellt und vom "Was ist neu"-Dokument verlinkt. Dokumente für CPython-Kernentwickler werden dem Devguide hinzugefügt.

Referenzimplementierung

Siehe Issue 43795.

Ideen für die Zukunft

Die folgenden Probleme fallen nicht in den Geltungsbereich dieser PEP, zeigen aber mögliche zukünftige Richtungen auf.

Definieren eines Prozesses für Deprecations/Entfernungen

Während diese PEP anerkennt, dass Teile der Limited API in Zukunft als veraltet markiert oder entfernt werden könnten, ist ein Prozess dafür nicht Teil des Umfangs und wird einer möglichen zukünftigen PEP überlassen.

C-Syntax für das ABI-Manifest

Es könnte nützlich sein, das ABI-Manifest als C-Header-Datei zu haben oder Header-Dateien aus dem Manifest zu generieren. Auch dies sind Optionen für die Zukunft.

Offene Fragen

Bisher keine.


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

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