Defining extension modules¶
Ein C-Erweiterungsmodul für CPython ist eine gemeinsam genutzte Bibliothek (z. B. eine .so-Datei unter Linux, eine .pyd-DLL unter Windows), die in den Python-Prozess geladen werden kann (z. B. sie wurde mit kompatiblen Compiler-Einstellungen kompiliert) und die eine Initialisierungsfunktion exportiert.
Um standardmäßig importierbar zu sein (d. h. durch importlib.machinery.ExtensionFileLoader), muss die gemeinsam genutzte Bibliothek in sys.path verfügbar sein und nach dem Modulnamen zuzüglich einer in importlib.machinery.EXTENSION_SUFFIXES aufgeführten Erweiterung benannt sein.
Hinweis
Das Erstellen, Verpacken und Verteilen von Erweiterungsmodulen wird am besten mit Drittanbieter-Tools durchgeführt und liegt außerhalb des Rahmens dieses Dokuments. Ein geeignetes Werkzeug ist Setuptools, dessen Dokumentation unter https://setuptools.pypa.io/en/latest/setuptools.html zu finden ist.
Normalerweise gibt die Initialisierungsfunktion eine Moduldefinition zurück, die mit PyModuleDef_Init() initialisiert wurde. Dies ermöglicht die Aufteilung des Erstellungsprozesses in mehrere Phasen.
Bevor wesentlicher Code ausgeführt wird, kann Python feststellen, welche Fähigkeiten das Modul unterstützt, und die Umgebung anpassen oder das Laden eines inkompatiblen Erweiterungsmoduls verweigern.
Standardmäßig erstellt Python selbst das Modulobjekt – das heißt, es führt das Äquivalent von
object.__new__()für Klassen aus. Es setzt auch anfängliche Attribute wie__package__und__loader__.Anschließend wird das Modulobjekt mit erweiterungsspezifischem Code initialisiert – dem Äquivalent von
__init__()für Klassen.
Dies wird als Multi-Phasen-Initialisierung bezeichnet, um sie von dem älteren (aber immer noch unterstützten) Single-Phasen-Initialisierungs-Schema zu unterscheiden, bei dem die Initialisierungsfunktion ein vollständig erstelltes Modul zurückgibt. Siehe den Abschnitt Single-Phasen-Initialisierung unten für Details.
Geändert in Version 3.5: Unterstützung für Multi-Phasen-Initialisierung hinzugefügt (PEP 489).
Mehrere Modulinstanzen¶
Standardmäßig sind Erweiterungsmodule keine Singletons. Wenn beispielsweise der Eintrag in sys.modules entfernt und das Modul erneut importiert wird, wird ein neues Modulobjekt erstellt und typischerweise mit neuen Methoden- und Typobjekten gefüllt. Das alte Modul unterliegt der normalen Garbage Collection. Dies spiegelt das Verhalten von reinen Python-Modulen wider.
Zusätzliche Modulinstanzen können in Sub-Interpretern oder nach einer Reinitialisierung der Python-Laufzeitumgebung (Py_Finalize() und Py_Initialize()) erstellt werden. In diesen Fällen würde das Teilen von Python-Objekten zwischen Modulinstanzen wahrscheinlich zu Abstürzen oder undefiniertem Verhalten führen.
Um solche Probleme zu vermeiden, sollte jede Instanz eines Erweiterungsmoduls *isoliert* sein: Änderungen an einer Instanz sollten sich nicht implizit auf andere auswirken, und aller vom Modul besessene Zustand, einschließlich Referenzen auf Python-Objekte, sollte spezifisch für eine bestimmte Modulinstanz sein. Weitere Details und eine praktische Anleitung finden Sie unter Isolating Extension Modules.
Ein einfacherer Weg, diese Probleme zu vermeiden, ist das Auslösen eines Fehlers bei wiederholter Initialisierung.
Alle Module sollen Sub-Interpreter unterstützen oder andernfalls explizit mangelnde Unterstützung signalisieren. Dies geschieht normalerweise durch Isolierung oder das Blockieren wiederholter Initialisierung, wie oben beschrieben. Ein Modul kann auch auf den Hauptinterpreter beschränkt werden, indem der Py_mod_multiple_interpreters-Slot verwendet wird.
Initialisierungsfunktion¶
Die von einem Erweiterungsmodul definierte Initialisierungsfunktion hat die folgende Signatur:
Ihr Name sollte PyInit_<name> lauten, wobei <name> durch den Namen des Moduls ersetzt wird.
Für Module mit reinen ASCII-Namen muss die Funktion stattdessen PyInit_<name> heißen, wobei <name> durch den Namen des Moduls ersetzt wird. Bei Verwendung der Multi-Phasen-Initialisierung sind Nicht-ASCII-Modulnamen zulässig. In diesem Fall lautet der Name der Initialisierungsfunktion PyInitU_<name>, wobei <name> mit Pythons *punycode*-Kodierung mit durch Unterstriche ersetzten Bindestrichen kodiert ist. In Python
def initfunc_name(name):
try:
suffix = b'_' + name.encode('ascii')
except UnicodeEncodeError:
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
return b'PyInit' + suffix
Es wird empfohlen, die Initialisierungsfunktion mit einem Hilfsmakro zu definieren:
-
PyMODINIT_FUNC¶
Deklariert eine Initialisierungsfunktion für Erweiterungsmodule. Dieses Makro
gibt den Rückgabetyp PyObject* an,
fügt alle vom System erforderlichen speziellen Linkage-Deklarationen hinzu und
deklariert für C++ die Funktion als
extern "C".
Zum Beispiel würde ein Modul namens spam wie folgt definiert werden:
static struct PyModuleDef spam_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "spam",
...
};
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Es ist möglich, mehrere Module aus einer einzigen gemeinsam genutzten Bibliothek zu exportieren, indem mehrere Initialisierungsfunktionen definiert werden. Der Import erfordert jedoch die Verwendung von symbolischen Links oder eines benutzerdefinierten Importeurs, da standardmäßig nur die Funktion, die dem Dateinamen entspricht, gefunden wird. Weitere Einzelheiten finden Sie im Abschnitt Multiple modules in one library in PEP 489.
Die Initialisierungsfunktion ist typischerweise das einzige Nicht-static-Element, das im C-Quellcode des Moduls definiert wird.
Multi-Phasen-Initialisierung¶
Normalerweise gibt die Initialisierungsfunktion (PyInit_modulename) eine PyModuleDef-Instanz mit einem nicht-NULL m_slots zurück. Bevor sie zurückgegeben wird, muss die PyModuleDef-Instanz mit der folgenden Funktion initialisiert werden:
-
PyObject *PyModuleDef_Init(PyModuleDef *def)¶
- Teil der Stable ABI seit Version 3.5.
Stellt sicher, dass eine Moduldefinition ein korrekt initialisiertes Python-Objekt ist, das seinen Typ und eine Referenzanzahl korrekt angibt.
Gibt *def* zurück, das nach
PyObject*gecastet wurde, oderNULL, wenn ein Fehler aufgetreten ist.Das Aufrufen dieser Funktion ist für die Multi-Phasen-Initialisierung erforderlich. Sie sollte in anderen Kontexten nicht verwendet werden.
Beachten Sie, dass Python davon ausgeht, dass
PyModuleDef-Strukturen statisch alloziert sind. Diese Funktion kann eine neue Referenz oder eine geliehene Referenz zurückgeben; diese Referenz darf nicht freigegeben werden.Hinzugefügt in Version 3.5.
Veraltete Single-Phasen-Initialisierung¶
Aufmerksamkeit
Die Single-Phasen-Initialisierung ist ein veralteter Mechanismus zur Initialisierung von Erweiterungsmodulen mit bekannten Nachteilen und Designfehlern. Autoren von Erweiterungsmodulen werden ermutigt, stattdessen die Multi-Phasen-Initialisierung zu verwenden.
Bei der Single-Phasen-Initialisierung sollte die Initialisierungsfunktion (PyInit_modulename) ein Modulobjekt erstellen, füllen und zurückgeben. Dies geschieht typischerweise mit PyModule_Create() und Funktionen wie PyModule_AddObjectRef().
Die Single-Phasen-Initialisierung unterscheidet sich von der Standardeinstellung in folgenden Punkten:
Single-Phasen-Module sind oder vielmehr *enthalten* "Singletons".
Wenn das Modul zum ersten Mal initialisiert wird, speichert Python den Inhalt des
__dict__des Moduls (also typischerweise die Funktionen und Typen des Moduls).Bei nachfolgenden Importen ruft Python die Initialisierungsfunktion nicht erneut auf. Stattdessen erstellt es ein neues Modulobjekt mit einem neuen
__dict__und kopiert die gespeicherten Inhalte dorthin. Zum Beispiel, gegeben ein Single-Phasen-Modul_testsinglephase[1], das eine Funktionsumund eine Ausnahmeklasseerrordefiniert:>>> import sys >>> import _testsinglephase as one >>> del sys.modules['_testsinglephase'] >>> import _testsinglephase as two >>> one is two False >>> one.__dict__ is two.__dict__ False >>> one.sum is two.sum True >>> one.error is two.error True
Das genaue Verhalten sollte als Implementierungsdetail von CPython betrachtet werden.
Um das Problem zu umgehen, dass
PyInit_modulenamekein *spec*-Argument entgegennimmt, wird ein Teil des Zustands der Import-Maschinerie gespeichert und auf das erste geeignete Modul angewendet, das während des Aufrufs vonPyInit_modulenameerstellt wird. Insbesondere, wenn ein Untermodul importiert wird, fügt dieser Mechanismus dem Modulnamen den Namen des übergeordneten Pakets voran.Eine Single-Phasen-
PyInit_modulename-Funktion sollte ihr Modulobjekt so früh wie möglich erstellen, bevor andere Modulobjekte erstellt werden können.Nicht-ASCII-Modulnamen (
PyInitU_modulename) werden nicht unterstützt.Single-Phasen-Module unterstützen Modul-Lookup-Funktionen wie
PyState_FindModule().