PEP 620 – Implementierungsdetails vor der C-API verbergen
- Autor:
- Victor Stinner <vstinner at python.org>
- Status:
- Zurückgezogen
- Typ:
- Standards Track
- Erstellt:
- 19-Jun-2020
- Python-Version:
- 3.12
Inhaltsverzeichnis
- Zusammenfassung
- PEP zurückgezogen
- Motivation
- Begründung
- Spezifikation
- Zusammenfassung
- C-API-Headerdateien neu organisieren
- Private Funktionen in die interne C-API verschieben
- Makros in statisch-inline Funktionen umwandeln
- Strukturen opak machen
- Verwendung von Py_TYPE() als l-Wert verbieten
- Neue C-API-Funktionen dürfen keine geliehenen Referenzen zurückgeben
- Vermeiden von Funktionen, die PyObject** zurückgeben
- Neue Header-Datei pythoncapi_compat.h
- Prozess zur Reduzierung der Anzahl fehlerhafter C-Erweiterungen
- Versionshistorie
- Urheberrecht
Zusammenfassung
Inkompatible C-API-Änderungen einführen, um Implementierungsdetails zu verbergen.
Sobald die meisten Implementierungsdetails verborgen sind, wird die Weiterentwicklung von CPython-Interna durch C-API-Abwärtskompatibilitätsprobleme weniger eingeschränkt. Es wird viel einfacher sein, neue Funktionen hinzuzufügen.
Es wird möglich, mit fortgeschritteneren Optimierungen in CPython zu experimentieren als nur mit Mikrooptimierungen, wie z. B. Tagged Pointers.
Einen Prozess definieren, um die Anzahl fehlerhafter C-Erweiterungen zu reduzieren.
Die Implementierung dieses PEP wird voraussichtlich sorgfältig über mehrere Python-Versionen erfolgen. Sie hat bereits in Python 3.7 begonnen und die meisten Änderungen sind bereits abgeschlossen. Der Prozess zur Reduzierung der Anzahl fehlerhafter C-Erweiterungen gibt den Rhythmus vor.
PEP zurückgezogen
Dieser PEP wurde von seinem Autor zurückgezogen, da der Umfang zu breit ist und die Arbeit auf mehrere Python-Versionen verteilt ist, was eine Entscheidung über den gesamten PEP erschwert. Er wurde in neue PEPs mit engeren und besser definierten Umfängen aufgeteilt, wie z. B. PEP 670.
Motivation
Die C-API behindert die Weiterentwicklung von CPython
Das Hinzufügen oder Entfernen von Mitgliedern von C-Strukturen verursacht mehrere Kompatibilitätsprobleme.
Das Hinzufügen eines neuen Mitglieds bricht die stabile ABI (PEP 384), insbesondere für statisch deklarierte Typen (z. B. static PyTypeObject MyType = {...};). In Python 3.4 fügte PEP 442 „Sichere Objektfinalisierung“ das Mitglied tp_finalize am Ende der Struktur PyTypeObject hinzu. Aus Kompatibilitätsgründen der ABI war ein neues Typ-Flag Py_TPFLAGS_HAVE_FINALIZE erforderlich, um anzuzeigen, ob die Typstruktur das Mitglied tp_finalize enthält. Das Flag wurde in Python 3.8 entfernt (bpo-32388).
Das Mitglied PyTypeObject.tp_print, das seit Python 3.0 (veröffentlicht 2009) als veraltet galt, wurde im Entwicklungszyklus von Python 3.8 entfernt. Die Änderung brach jedoch zu viele C-Erweiterungen und musste vor der endgültigen Veröffentlichung von 3.8 rückgängig gemacht werden. Schließlich wurde das Mitglied in Python 3.9 erneut entfernt.
C-Erweiterungen verlassen sich auf die Möglichkeit, auf Strukturmitglieder zuzugreifen, entweder indirekt über die C-API oder sogar direkt. Das Modifizieren von Strukturen wie PyListObject kann nicht einmal in Betracht gezogen werden.
Die Struktur PyTypeObject hat sich am meisten entwickelt, einfach weil es keinen anderen Weg gab, CPython weiterzuentwickeln, als sie zu modifizieren.
Eine C-Erweiterung kann technisch einen PyObject*-Zeiger dereferenzieren und auf PyObject-Mitglieder zugreifen. Dies verhindert Experimente wie Tagged Pointers (Speicherung kleiner Werte als PyObject*, die nicht auf eine gültige PyObject-Struktur zeigen).
Der Ersatz des Python-Garbage Collectors durch einen Tracing-Garbage Collector würde auch die Entfernung des Referenzzählers PyObject.ob_refcnt erfordern, während derzeit die Makros Py_INCREF() und Py_DECREF() direkt auf PyObject.ob_refcnt zugreifen.
Gleiches CPython-Design seit 1990: Strukturen und Referenzzählung
Als das CPython-Projekt erstellt wurde, wurde es nach einem Prinzip geschrieben: die Implementierung sollte einfach genug sein, um von einem einzelnen Entwickler gewartet werden zu können. Die Komplexität von CPython ist stark gestiegen und viele Mikrooptimierungen wurden implementiert, aber das Kern-Design von CPython hat sich nicht geändert.
Mitglieder der Strukturen PyObject und PyTupleObject haben sich seit dem Commit "Initial revision" (1990) nicht geändert.
#define OB_HEAD \
unsigned int ob_refcnt; \
struct _typeobject *ob_type;
typedef struct _object {
OB_HEAD
} object;
typedef struct {
OB_VARHEAD
object *ob_item[1];
} tupleobject;
Nur die Namen haben sich geändert: object wurde in PyObject umbenannt und tupleobject wurde in PyTupleObject umbenannt.
CPython verfolgt immer noch die Lebensdauer von Python-Objekten intern und für externe C-Erweiterungen (über die Python C-API) mithilfe der Referenzzählung.
Alle Python-Objekte müssen auf dem Heap allokiert werden und können nicht verschoben werden.
Warum ist PyPy effizienter als CPython?
Das PyPy-Projekt ist eine Python-Implementierung, die im Durchschnitt 4,2x schneller als CPython ist. PyPy-Entwickler haben sich entschieden, CPython nicht zu forken, sondern von Grund auf neu zu beginnen, um mehr Freiheit bei der Wahl der Optimierungen zu haben.
PyPy verwendet keine Referenzzählung, sondern einen Tracing-Garbage Collector, der Objekte verschiebt. Objekte können auf dem Stack allokiert werden (oder gar nicht), anstatt immer auf dem Heap allokiert werden zu müssen.
Objektlayouts sind auf Leistung ausgelegt. Zum Beispiel speichert eine Listenstrategie Ganzzahlen direkt als Ganzzahlen und nicht als Objekte.
Darüber hinaus verfügt PyPy über einen JIT-Compiler, der dank des effizienten PyPy-Designs schnellen Code erzeugt.
PyPy-Flaschenhals: die Python C-API
Während PyPy zur Ausführung von reinem Python-Code wesentlich effizienter ist als CPython, ist es bei der Ausführung von C-Erweiterungen genauso effizient oder langsamer als CPython.
Da die C-API PyObject* erfordert und direkten Zugriff auf Strukturmitglieder erlaubt, muss PyPy einem PyPy-Objekt ein CPython-Objekt zuordnen und beide konsistent halten. Die Konvertierung eines PyPy-Objekts in ein CPython-Objekt ist ineffizient. Darüber hinaus muss die Referenzzählung auf der Grundlage des PyPy-Tracing-Garbage-Collectors implementiert werden.
Diese Konvertierungen sind erforderlich, da die Python C-API zu nah an der CPython-Implementierung ist: Es gibt keine High-Level-Abstraktion. Zum Beispiel sind Strukturmitglieder Teil der öffentlichen C-API und nichts hindert eine C-Erweiterung daran, PyTupleObject.ob_item[0] (das erste Element eines Tupels) direkt zu erhalten oder zu setzen.
Siehe Inside cpyext: Why emulating CPython C API is so Hard (Sept. 2018) von Antonio Cuni für weitere Details.
Begründung
Implementierungsdetails verbergen
Das Verbergen von Implementierungsdetails vor der C-API hat mehrere Vorteile
- Es wird möglich, mit fortgeschritteneren Optimierungen in CPython zu experimentieren als nur mit Mikrooptimierungen. Zum Beispiel Tagged Pointers und der Ersatz des Garbage Collectors durch einen Tracing-Garbage Collector, der Objekte verschieben kann.
- Das Hinzufügen neuer Funktionen in CPython wird einfacher.
- PyPy sollte in der Lage sein, Konvertierungen zu CPython-Objekten in mehr Fällen zu vermeiden: Beibehaltung effizienter PyPy-Objekte.
- Es wird einfacher, die C-API für eine neue Python-Implementierung zu implementieren.
- Mehr C-Erweiterungen werden mit anderen Python-Implementierungen als CPython kompatibel sein.
Beziehung zur eingeschränkten C-API
PEP 384 „Definieren einer stabilen ABI“ wurde in Python 3.4 implementiert. Sie führt die „eingeschränkte C-API“ ein: eine Teilmenge der C-API. Wenn die eingeschränkte C-API verwendet wird, ist es möglich, eine C-Erweiterung nur einmal zu erstellen und sie mit mehreren Python-Versionen zu verwenden: das ist die stabile ABI.
Die Haupteinschränkung von PEP 384 besteht darin, dass C-Erweiterungen sich für die eingeschränkte C-API entscheiden müssen. Nur sehr wenige Projekte haben diese Wahl getroffen, meistens um die Verteilung von Binärdateien zu erleichtern, insbesondere unter Windows.
Dieser PEP verschiebt die C-API in Richtung der eingeschränkten C-API.
Idealerweise wird die C-API zur eingeschränkten C-API und alle C-Erweiterungen verwenden die stabile ABI, aber das liegt außerhalb des Geltungsbereichs dieses PEP.
Spezifikation
Zusammenfassung
- (Abgeschlossen) C-API-Headerdateien neu organisieren: Unterverzeichnisse
Include/cpython/undInclude/internal/erstellen. - (Abgeschlossen) Private Funktionen, die Implementierungsdetails preisgeben, in die interne C-API verschieben.
- (Abgeschlossen) Makros in statisch-inline Funktionen umwandeln.
- (Abgeschlossen) Neue Funktionen
Py_SET_TYPE(),Py_SET_REFCNT()undPy_SET_SIZE()hinzufügen. Die MakrosPy_TYPE(),Py_REFCNT()undPy_SIZE()werden zu Funktionen, die nicht als l-Wert verwendet werden können. - (Abgeschlossen) Neue C-API-Funktionen dürfen keine geliehenen Referenzen zurückgeben.
- (In Arbeit) Bereitstellung der Header-Datei
pythoncapi_compat.h. - (In Arbeit) Strukturen opak machen, Getter- und Setter-Funktionen hinzufügen.
- (Nicht begonnen) Deprecation von
PySequence_Fast_ITEMS(). - (Nicht begonnen) Umwandlung der Makros
PyTuple_GET_ITEM()undPyList_GET_ITEM()in statisch-inline Funktionen.
C-API-Headerdateien neu organisieren
Der erste Nutzer der C-API war Python selbst. Es gibt keine klare Trennung zwischen APIs, die nicht außerhalb von Python verwendet werden dürfen, und APIs, die bewusst öffentlich sind.
Header-Dateien müssen in 3 APIs neu organisiert werden
- Das Verzeichnis
Include/ist die eingeschränkte C-API: keine Implementierungsdetails, Strukturen sind opak. C-Erweiterungen, die sie verwenden, erhalten eine stabile ABI. - Das Verzeichnis
Include/cpython/ist die CPython C-API: weniger „portable“ API, hängt stärker von der Python-Version ab, gibt einige Implementierungsdetails preis, wenige inkompatible Änderungen können auftreten. - Das Verzeichnis
Include/internal/ist die interne C-API: Implementierungsdetails, inkompatible Änderungen sind bei jeder Python-Version wahrscheinlich.
Die Erstellung des Verzeichnisses Include/cpython/ ist vollständig abwärtskompatibel. Include/cpython/-Header-Dateien können nicht direkt eingebunden werden und werden automatisch von den Include/-Header-Dateien eingebunden, wenn das Makro Py_LIMITED_API nicht definiert ist.
Die interne C-API ist installiert und kann für spezielle Zwecke wie Debugger und Profiler verwendet werden, die ohne Ausführung von Code auf Strukturmitglieder zugreifen müssen. C-Erweiterungen, die die interne C-API verwenden, sind eng an eine Python-Version gekoppelt und müssen bei jeder Python-Version neu kompiliert werden.
STATUS: Abgeschlossen (in Python 3.8)
Die Neuorganisation der Header-Dateien begann in Python 3.7 und wurde in Python 3.8 abgeschlossen.
Private Funktionen in die interne C-API verschieben
Private Funktionen, die Implementierungsdetails preisgeben, müssen in die interne C-API verschoben werden.
Wenn eine C-Erweiterung auf eine private CPython-Funktion angewiesen ist, die CPython-Implementierungsdetails preisgibt, müssen andere Python-Implementierungen diese private Funktion neu implementieren, um diese C-Erweiterung zu unterstützen.
STATUS: Abgeschlossen (in Python 3.9)
Private Funktionen in Python 3.8 in die interne C-API verschoben.
_PyObject_GC_TRACK(),_PyObject_GC_UNTRACK()
Makros und Funktionen, die in Python 3.9 von der eingeschränkten C-API ausgeschlossen sind
_PyObject_SIZE(),_PyObject_VAR_SIZE()PyThreadState_DeleteCurrent()PyFPE_START_PROTECT(),PyFPE_END_PROTECT()_Py_NewReference(),_Py_ForgetReference()_PyTraceMalloc_NewReference()_Py_GetRefTotal()
Private Funktionen, die in Python 3.9 in die interne C-API verschoben wurden
- GC-Funktionen wie
_Py_AS_GC(),_PyObject_GC_IS_TRACKED()und_PyGCHead_NEXT() _Py_AddToAllObjects()(nicht exportiert)_PyDebug_PrintTotalRefs(),_Py_PrintReferences(),_Py_PrintReferenceAddresses()(nicht exportiert)
Öffentliche Funktionen zum „Löschen von Freilisten“ wurden in die interne C-API verschoben und in Python 3.9 in private Funktionen umbenannt
PyAsyncGen_ClearFreeLists()PyContext_ClearFreeList()PyDict_ClearFreeList()PyFloat_ClearFreeList()PyFrame_ClearFreeList()PyList_ClearFreeList()PyTuple_ClearFreeList()- Einfach entfernte Funktionen
PyMethod_ClearFreeList()undPyCFunction_ClearFreeList(): Freiliste für gebundene Methoden in Python 3.9 entfernt.PySet_ClearFreeList(): Set-Freiliste in Python 3.4 entfernt.PyUnicode_ClearFreeList(): Unicode-Freiliste in Python 3.3 entfernt.
Makros in statisch-inline Funktionen umwandeln
Die Umwandlung von Makros in statisch-inline Funktionen hat mehrere Vorteile
- Funktionen haben gut definierte Parametertypen und Rückgabetypen.
- Funktionen können Variablen mit einem gut definierten Geltungsbereich (der Funktion) verwenden.
- Debugger können Haltepunkte auf Funktionen setzen und Profiler können den Funktionsnamen in den Aufrufstapeln anzeigen. In den meisten Fällen funktioniert dies auch, wenn eine statisch-inline Funktion inlined ist.
- Funktionen haben keine Makro-Fallstricke.
Die Umwandlung von Makros in statisch-inline Funktionen sollte nur sehr wenige C-Erweiterungen beeinträchtigen, die Makros auf ungewöhnliche Weise verwenden.
Aus Gründen der Abwärtskompatibilität müssen Funktionen weiterhin jeden Typ akzeptieren, nicht nur PyObject*, um Compiler-Warnungen zu vermeiden, da die meisten Makros ihre Parameter zu PyObject* casten.
Python 3.6 erfordert C-Compiler, die statisch-inline Funktionen unterstützen: PEP 7 erfordert eine Teilmenge von C99.
STATUS: Abgeschlossen (in Python 3.9)
Makros, die in Python 3.8 in statisch-inline Funktionen umgewandelt wurden
Py_INCREF(),Py_DECREF()Py_XINCREF(),Py_XDECREF()PyObject_INIT(),PyObject_INIT_VAR()_PyObject_GC_TRACK(),_PyObject_GC_UNTRACK(),_Py_Dealloc()
Makros, die in Python 3.9 in reguläre Funktionen umgewandelt wurden
Py_EnterRecursiveCall(),Py_LeaveRecursiveCall()(zur eingeschränkten C-API hinzugefügt)PyObject_INIT(),PyObject_INIT_VAR()PyObject_GET_WEAKREFS_LISTPTR()PyObject_CheckBuffer()PyIndex_Check()PyObject_IS_GC()PyObject_NEW()(Alias fürPyObject_New()),PyObject_NEW_VAR()(Alias fürPyObject_NewVar())PyType_HasFeature()(ruft immerPyType_GetFlags()auf)- Die Makros
Py_TRASHCAN_BEGIN_CONDITION()undPy_TRASHCAN_END()rufen jetzt Funktionen auf, die Implementierungsdetails verbergen, anstatt direkt auf Mitglieder derPyThreadState-Struktur zuzugreifen.
Strukturen opak machen
Die folgenden Strukturen der C-API werden opak
PyInterpreterStatePyThreadStatePyGC_HeadPyTypeObjectPyObjectundPyVarObjectPyTypeObject- Alle Typen, die von
PyObjectoderPyVarObjecterben
C-Erweiterungen müssen Getter- oder Setter-Funktionen verwenden, um Strukturmitglieder zu erhalten oder zu setzen. Zum Beispiel muss tuple->ob_item[0] durch PyTuple_GET_ITEM(tuple, 0) ersetzt werden.
Um sich von der Referenzzählung lösen zu können, muss PyObject opak werden. Derzeit ist der Referenzzähler PyObject.ob_refcnt in der C-API freigelegt. Alle Strukturen müssen opak werden, da sie von PyObject erben. Denn PyFloatObject erbt von PyObject.
typedef struct {
PyObject ob_base;
double ob_fval;
} PyFloatObject;
Das vollständige Opakmachen von PyObject erfordert die Umwandlung der Makros Py_INCREF() und Py_DECREF() in Funktionsaufrufe. Diese Änderung wirkt sich auf die Leistung aus. Sie wird wahrscheinlich eine der letzten Änderungen beim Opakmachen von Strukturen sein.
Das Opakmachen der Struktur PyTypeObject bricht C-Erweiterungen, die Typen statisch deklarieren (z. B. static PyTypeObject MyType = {...};). C-Erweiterungen müssen stattdessen PyType_FromSpec() verwenden, um Typen auf dem Heap zu allokieren. Die Verwendung von Heap-Typen hat weitere Vorteile, wie z. B. die Kompatibilität mit Subinterpretern. In Kombination mit PEP 489 „Mehrphasen-Initialisierung von Erweiterungsmodulen“ verhält sich eine C-Erweiterung näher an einem Python-Modul, z. B. indem mehr als eine Modulinstanz erstellt werden kann.
Das Opakmachen der Struktur PyThreadState erfordert das Hinzufügen von Getter- und Setter-Funktionen für Mitglieder, die von C-Erweiterungen verwendet werden.
STATUS: In Arbeit (begonnen in Python 3.8)
Die Struktur PyInterpreterState wurde in Python 3.8 opak gemacht (bpo-35886) und die Struktur PyGC_Head (bpo-40241) wurde in Python 3.9 opak gemacht.
Probleme, die die Arbeit zur Vorbereitung der C-API zur Opakmachung folgender Strukturen verfolgen
PyObject: bpo-39573PyTypeObject: bpo-40170PyFrameObject: bpo-40421- Python 3.9 fügt die Getter-Funktionen
PyFrame_GetCode()undPyFrame_GetBack()hinzu und verschiebtPyFrame_GetLineNumberin die eingeschränkte C-API.
- Python 3.9 fügt die Getter-Funktionen
PyThreadState: bpo-39947- Python 3.9 fügt 3 Getter-Funktionen hinzu:
PyThreadState_GetFrame(),PyThreadState_GetID(),PyThreadState_GetInterpreter().
- Python 3.9 fügt 3 Getter-Funktionen hinzu:
Verwendung von Py_TYPE() als l-Wert verbieten
Die Funktion Py_TYPE() erhält den Typ eines Objekts, sein Mitglied PyObject.ob_type. Sie ist als Makro implementiert, das als l-Wert zum Setzen des Typs verwendet werden kann: Py_TYPE(obj) = new_type. Dieser Code beruht auf der Annahme, dass PyObject.ob_type direkt modifiziert werden kann. Dies verhindert, dass die Struktur PyObject opak gemacht wird.
Neue Setter-Funktionen Py_SET_TYPE(), Py_SET_REFCNT() und Py_SET_SIZE() werden hinzugefügt und müssen stattdessen verwendet werden.
Die Makros Py_TYPE(), Py_REFCNT() und Py_SIZE() müssen in statisch-inline Funktionen umgewandelt werden, die nicht als l-Wert verwendet werden können.
Zum Beispiel das Makro Py_TYPE()
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
wird zu
#define _PyObject_CAST_CONST(op) ((const PyObject*)(op))
static inline PyTypeObject* _Py_TYPE(const PyObject *ob) {
return ob->ob_type;
}
#define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST_CONST(ob))
STATUS: Abgeschlossen (in Python 3.10)
Neue Funktionen Py_SET_TYPE(), Py_SET_REFCNT() und Py_SET_SIZE() wurden in Python 3.9 hinzugefügt.
In Python 3.10 können Py_TYPE(), Py_REFCNT() und Py_SIZE() nicht mehr als l-Wert verwendet werden, und die neuen Setter-Funktionen müssen stattdessen verwendet werden.
Neue C-API-Funktionen dürfen keine geliehenen Referenzen zurückgeben
Wenn eine Funktion eine geliehene Referenz zurückgibt, kann Python nicht verfolgen, wann der Aufrufer die Verwendung dieser Referenz einstellt.
Zum Beispiel, wenn der Python-Typ list für kleine Ganzzahlen spezialisiert ist und „rohe“ Zahlen direkt speichert anstatt Python-Objekte, muss PyList_GetItem() ein temporäres Python-Objekt erstellen. Das Problem ist zu entscheiden, wann es sicher ist, das temporäre Objekt zu löschen.
Die allgemeine Richtlinie ist, die Rückgabe von geliehenen Referenzen für neue C-API-Funktionen zu vermeiden.
Keine Funktion, die geliehene Referenzen zurückgibt, ist für die Entfernung durch diesen PEP geplant.
STATUS: Abgeschlossen (in Python 3.9)
In Python 3.9 geben neue C-API-Funktionen, die Python-Objekte zurückgeben, nur starke Referenzen zurück
PyFrame_GetBack()PyFrame_GetCode()PyObject_CallNoArgs()PyObject_CallOneArg()PyThreadState_GetFrame()
Vermeiden von Funktionen, die PyObject** zurückgeben
Die Funktion PySequence_Fast_ITEMS() ermöglicht direkten Zugriff auf ein Array von PyObject*-Objekten. Die Funktion wird zugunsten von PyTuple_GetItem() und PyList_GetItem() als veraltet erklärt.
PyTuple_GET_ITEM() kann missbraucht werden, um direkt auf das Mitglied PyTupleObject.ob_item zuzugreifen
PyObject **items = &PyTuple_GET_ITEM(0);
Die Makros PyTuple_GET_ITEM() und PyList_GET_ITEM() werden in statisch-inline Funktionen umgewandelt, um dies zu verhindern.
STATUS: Nicht begonnen
Neue Header-Datei pythoncapi_compat.h
Das Opakmachen von Strukturen erfordert die Änderung von C-Erweiterungen, um Getter- und Setter-Funktionen zu verwenden. Das praktische Problem ist, wie die Unterstützung für alte Python-Versionen beibehalten werden kann, die diese Funktionen nicht haben.
Zum Beispiel ist es in Python 3.10 nicht mehr möglich, Py_TYPE() als l-Wert zu verwenden. Die neue Funktion Py_SET_TYPE() muss stattdessen verwendet werden.
#if PY_VERSION_HEX >= 0x030900A4
Py_SET_TYPE(&MyType, &PyType_Type);
#else
Py_TYPE(&MyType) = &PyType_Type;
#endif
Dieser Code mag Entwicklern, die ihre Python-Codebasis von Python 2 auf Python 3 portiert haben, bekannt vorkommen.
Python wird eine neue Header-Datei pythoncapi_compat.h verteilen, die neue C-API-Funktionen für alte Python-Versionen bereitstellt. Beispiel
#if PY_VERSION_HEX < 0x030900A4
static inline void
_Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
{
ob->ob_type = type;
}
#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type)
#endif // PY_VERSION_HEX < 0x030900A4
Mit dieser Header-Datei kann Py_SET_TYPE() auch für alte Python-Versionen verwendet werden.
Entwickler können diese Datei in ihr Projekt kopieren oder sogar nur die wenigen Funktionen kopieren/einfügen, die ihre C-Erweiterung benötigt.
STATUS: In Arbeit (implementiert, aber noch nicht von CPython verteilt)
Die Header-Datei pythoncapi_compat.h wird derzeit entwickelt unter: https://github.com/pythoncapi/pythoncapi_compat
Prozess zur Reduzierung der Anzahl fehlerhafter C-Erweiterungen
Prozess zur Reduzierung der Anzahl fehlerhafter C-Erweiterungen bei Einführung von inkompatiblen C-API-Änderungen, die in diesem PEP aufgeführt sind
- Schätzen Sie ab, wie viele beliebte C-Erweiterungen von der inkompatiblen Änderung betroffen sind.
- Koordinieren Sie sich mit den Maintainern fehlerhafter C-Erweiterungen, um deren Code für zukünftige inkompatible Änderungen vorzubereiten.
- Führen Sie die inkompatiblen Änderungen in Python ein. Die Dokumentation muss erklären, wie bestehender Code portiert werden kann. Es wird empfohlen, solche Änderungen zu Beginn eines Entwicklungszyklus zu integrieren, um mehr Zeit für Tests zu haben.
- Änderungen, die am wahrscheinlichsten eine große Anzahl von C-Erweiterungen brechen, sollten auf der capi-sig Mailingliste angekündigt werden, um C-Erweiterungs-Maintainer zu benachrichtigen, damit sie ihre Projekte für das nächste Python vorbereiten.
- Wenn die Änderung zu viele Projekte beeinträchtigt, sollte eine Rückgängigmachung der Änderung diskutiert werden, wobei die Anzahl der fehlerhaften Pakete, ihre Bedeutung in der Python-Community und die Wichtigkeit der Änderung berücksichtigt werden.
Die Koordination bedeutet normalerweise, Probleme an die Projekte zu melden oder sogar Änderungen vorzuschlagen. Es erfordert kein Warten auf eine neue Veröffentlichung, die Korrekturen für jedes fehlerhafte Projekt enthält.
Da immer mehr C-Erweiterungen mithilfe von Cython und nicht direkt über die C-API geschrieben werden, ist es wichtig sicherzustellen, dass Cython im Voraus für inkompatible Änderungen vorbereitet ist. Dies gibt C-Erweiterungs-Maintainern mehr Zeit, eine neue Version mit Code zu veröffentlichen, der mit dem aktualisierten Cython generiert wurde (für C-Erweiterungen, die den von Cython generierten Code verteilen).
Zukünftige inkompatible Änderungen können durch Deprecation einer Funktion in der Dokumentation und durch Annotation der Funktion mit Py_DEPRECATED() angekündigt werden. Aber das Opakmachen einer Struktur und das Verhindern der Verwendung eines Makros als l-Wert kann nicht mit Py_DEPRECATED() als veraltet gekennzeichnet werden.
Der wichtige Teil ist die Koordination und das Finden eines Gleichgewichts zwischen CPython-Entwicklungen und Abwärtskompatibilität. Zum Beispiel ist das Brechen einer zufälligen, alten, obskuren und unverwalteten C-Erweiterung auf PyPI weniger schwerwiegend als das Brechen von numpy.
Wenn eine Änderung rückgängig gemacht wird, gehen wir zurück zum Koordinationsschritt, um die Änderung besser vorzubereiten. Sobald mehr C-Erweiterungen bereit sind, kann die inkompatible Änderung erneut geprüft werden.
Versionshistorie
- Version 3, Juni 2020: PEP von Grund auf neu geschrieben. Python verteilt jetzt eine neue Header-Datei
pythoncapi_compat.hund ein Prozess wird definiert, um die Anzahl fehlerhafter C-Erweiterungen bei Einführung von inkompatiblen C-API-Änderungen, die in diesem PEP aufgeführt sind, zu reduzieren. - Version 2, April 2020: PEP: Modifizieren der C-API zum Verbergen von Implementierungsdetails.
- Version 1, Juli 2017: PEP: Implementierungsdetails in der C-API verbergen an python-ideas gesendet.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0620.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT