PEP 399 – Anforderungen an die Kompatibilität von reinen Python/C-Beschleunigermodulen
- Autor:
- Brett Cannon <brett at python.org>
- Status:
- Final
- Typ:
- Informational
- Erstellt:
- 04-Apr-2011
- Python-Version:
- 3.3
- Post-History:
- 04-Apr-2011, 12-Apr-2011, 17-Jul-2011, 15-Aug-2011, 01-Jan-2013
Inhaltsverzeichnis
Zusammenfassung
Die Python-Standardbibliothek unter CPython enthält verschiedene Instanzen von Modulen, die sowohl in reinem Python als auch in C implementiert sind (entweder vollständig oder teilweise). Diese PEP schreibt vor, dass in diesen Instanzen der C-Code die Testsuite, die für den reinen Python-Code verwendet wird, **bestehen muss**, um so weit wie vernünftigerweise möglich als Drop-in-Ersatz zu fungieren (C- und VM-spezifische Tests sind ausgenommen). Außerdem ist vorgeschrieben, dass neue C-basierte Module, denen eine reine Python-Äquivalentimplementierung fehlt, eine spezielle Genehmigung erhalten müssen, um zur Standardbibliothek hinzugefügt zu werden.
Begründung
Python ist über die CPython-virtuelle Maschine (VM) hinausgewachsen. IronPython, Jython und PyPy sind derzeit allesamt praktikable Alternativen zur CPython-VM. Das VM-Ökosystem, das sich um die Python-Programmiersprache entwickelt hat, hat dazu geführt, dass Python in vielen verschiedenen Bereichen eingesetzt wird, in denen CPython nicht verwendet werden kann, z. B. ermöglicht Jython die Verwendung von Python in Java-Anwendungen.
Ein Problem, mit dem alle VMs außer CPython konfrontiert sind, ist die Handhabung von Modulen aus der Standardbibliothek, die (bis zu einem gewissen Grad) in C implementiert sind. Da andere VMs typischerweise nicht die gesamte C-API von CPython unterstützen, können sie den Code, der zur Erstellung des Moduls verwendet wird, nicht verwenden. Oftmals führt dies dazu, dass diese anderen VMs die Module entweder in reinem Python oder in der Programmiersprache, mit der die VM selbst implementiert ist (z. B. in C# für IronPython), neu implementieren. Diese Doppelarbeit zwischen CPython, PyPy, Jython und IronPython ist äußerst bedauerlich, da die Implementierung eines Moduls **zumindest** in reinem Python helfen würde, diese Doppelarbeit zu mildern.
Der Zweck dieser PEP ist es, diese Doppelarbeit zu minimieren, indem vorgeschrieben wird, dass alle neuen Module, die der Python-Standardbibliothek hinzugefügt werden, **eine reine Python-Implementierung haben müssen**, es sei denn, es wird eine spezielle Ausnahmeregelung gewährt. Dies stellt sicher, dass ein Modul in der Standardbibliothek für alle VMs verfügbar ist und nicht nur für CPython (bereits existierende Module, die diese Anforderung nicht erfüllen, sind ausgenommen, obwohl es niemanden daran hindert, nachträglich eine reine Python-Implementierung hinzuzufügen).
Die Neuentwicklung von Teilen (oder des gesamten) Moduls in C (im Falle von CPython) ist aus Leistungsgründen weiterhin zulässig, aber jeder solche beschleunigte Code muss dieselbe Testsuite (ohne VM- oder C-spezifische Tests) bestehen, um die Semantik zu überprüfen und Abweichungen zu verhindern. Um dies zu erreichen, muss die Testsuite für das Modul eine umfassende Abdeckung der reinen Python-Implementierung aufweisen, bevor der Beschleunigungscode hinzugefügt werden darf.
Details
Ab Python 3.3 müssen alle Module, die der Standardbibliothek hinzugefügt werden, eine reine Python-Implementierung haben. Diese Regel kann nur ignoriert werden, wenn das Python-Entwicklungsteam eine spezielle Ausnahmeregelung für das Modul gewährt. Typischerweise wird die Ausnahmeregelung nur gewährt, wenn ein Modul eine spezifische C-basierte Bibliothek umschließt (z. B. sqlite3). Bei der Gewährung einer Ausnahmeregelung wird anerkannt, dass das Modul als exklusiv für CPython betrachtet wird und nicht Teil der Python-Standardbibliothek ist, die andere VMs voraussichtlich unterstützen werden. Die Verwendung von ctypes zur Bereitstellung einer API für eine C-Bibliothek wird weiterhin missbilligt, da ctypes keine Compilergarantien bietet, auf die sich C-Code typischerweise verlässt, um bestimmte Fehler zu vermeiden (z. B. API-Änderungen).
Auch wenn eine reine Python-Implementierung durch diese PEP vorgeschrieben wird, schließt dies die Verwendung eines begleitenden Beschleunigermoduls nicht aus. Wenn ein Beschleunigermodul bereitgestellt wird, soll es denselben Namen wie das zu beschleunigende Modul tragen, mit einem Unterstrich als Präfix, z. B. _warnings für warnings. Das übliche Muster, um von der reinen Python-Implementierung auf den beschleunigten Code zuzugreifen, ist der Import mit einem import *, z. B. from _warnings import *. Dies geschieht typischerweise am Ende des Moduls, damit es bestimmte Python-Objekte durch ihre beschleunigten Entsprechungen überschreiben kann. Diese Art von Import kann auch vor dem Ende des Moduls erfolgen, wenn es erforderlich ist, z. B. wenn eine beschleunigte Basisklasse bereitgestellt wird, die dann von Python-Code abgeleitet wird. Diese PEP schreibt nicht vor, dass bereits existierende Module in der Standardbibliothek, denen eine reine Python-Entsprechung fehlt, eine solche erhalten. Aber wenn sich Personen freiwillig bereit erklären, eine reine Python-Entsprechung bereitzustellen und zu pflegen (z. B. das PyPy-Team, das seine reine Python-Implementierung des csv-Moduls anbietet und pflegt), dann wird solcher Code akzeptiert. In diesen Fällen gilt die C-Version als Referenzimplementierung hinsichtlich der erwarteten Semantik.
Jeder neue beschleunigte Code muss als Drop-in-Ersatz fungieren, der der reinen Python-Implementierung so nahe wie vernünftigerweise möglich kommt. Technische Details der VM, die den beschleunigten Code bereitstellt, dürfen bei Bedarf abweichen, z. B. kann eine Klasse eine type sein, wenn sie in C implementiert ist. Um zu überprüfen, ob der Python- und der entsprechende C-Code so ähnlich wie möglich funktionieren, müssen beide Codebasen mit denselben Tests getestet werden, die für den reinen Python-Code gelten (Tests, die spezifisch für den C-Code oder eine VM sind, fallen nicht unter diese Anforderung). Es wird erwartet, dass die Testsuite umfangreich ist, um die erwartete Semantik zu überprüfen.
Das Funktionieren als Drop-in-Ersatz bedeutet auch, dass kein öffentliches API im beschleunigten Code bereitgestellt werden darf, das nicht im reinen Python-Code existiert. Ohne diese Anforderung könnten sich Leute versehentlich auf ein Detail im beschleunigten Code verlassen, das für andere VMs, die die reine Python-Implementierung verwenden, nicht verfügbar ist. Um zu helfen, die Einhaltung des Vertrags über semantische Äquivalenz sicherzustellen, muss ein Modul sowohl mit als auch ohne seinen beschleunigten Code so gründlich wie möglich getestet werden.
Als Beispiel kann zur Erstellung von Tests, die sowohl die reine Python- als auch die C-beschleunigte Version eines Moduls ausführen, ein grundlegendes Idiom befolgt werden.
from test.support import import_fresh_module
import unittest
c_heapq = import_fresh_module('heapq', fresh=['_heapq'])
py_heapq = import_fresh_module('heapq', blocked=['_heapq'])
class ExampleTest:
def test_example(self):
self.assertTrue(hasattr(self.module, 'heapify'))
class PyExampleTest(ExampleTest, unittest.TestCase):
module = py_heapq
@unittest.skipUnless(c_heapq, 'requires the C _heapq module')
class CExampleTest(ExampleTest, unittest.TestCase):
module = c_heapq
if __name__ == '__main__':
unittest.main()
Das Testmodul definiert eine Basisklasse (ExampleTest) mit Testmethoden, die über ein Klassenattribut self.heapq auf das heapq-Modul zugreifen, und zwei Unterklassen, die dieses Attribut entweder auf die Python- oder die C-Version des Moduls setzen. Beachten Sie, dass nur die beiden Unterklassen von unittest.TestCase erben – dies verhindert, dass die Klasse ExampleTest von der unittest-Testsuche als TestCase-Unterklasse erkannt wird. Ein skipUnless-Decorator kann zur Klasse hinzugefügt werden, die den C-Code testet, damit diese Tests übersprungen werden, wenn das C-Modul nicht verfügbar ist.
Wenn dieser Test eine umfassende Abdeckung für heapq.heappop() in der reinen Python-Implementierung bieten würde, dann könnte der beschleunigte C-Code der CPython-Standardbibliothek hinzugefügt werden. Wenn dies nicht der Fall wäre, müsste die Testsuite aktualisiert werden, bis eine ordnungsgemäße Abdeckung bereitgestellt war, bevor der beschleunigte C-Code hinzugefügt werden konnte.
Um auch die Kompatibilität zu gewährleisten, sollte C-Code abstrakte APIs für Objekte verwenden, um eine versehentliche Abhängigkeit von spezifischen Typen zu verhindern. Wenn eine Funktion beispielsweise eine Sequenz akzeptiert, sollte der C-Code standardmäßig PyObject_GetItem() verwenden und nicht etwas wie PyList_GetItem(). C-Code darf einen Fast-Path haben, wenn die korrekte PyList_CheckExact()-Prüfung verwendet wird, aber ansonsten sollten APIs mit jedem Objekt arbeiten, das sich durch Duck-Typing als das richtige Interface ausgibt, anstatt eines spezifischen Typs.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0399.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT