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

Python Enhancement Proposals

PEP 513 – Ein Plattform-Tag für portable Linux-Build-Distributionen

Autor:
Robert T. McGibbon <rmcgibbo at gmail.com>, Nathaniel J. Smith <njs at pobox.com>
BDFL-Delegate:
Alyssa Coghlan <ncoghlan at gmail.com>
Discussions-To:
Distutils-SIG Liste
Status:
Abgelöst
Typ:
Informational
Thema:
Packaging
Erstellt:
19. Jan. 2016
Post-History:
19. Jan. 2016, 25. Jan. 2016, 29. Jan. 2016
Ersetzt-Durch:
600
Resolution:
Distutils-SIG Nachricht

Inhaltsverzeichnis

Zusammenfassung

Dieser PEP schlägt die Erstellung eines neuen Plattform-Tags für kompilierte Python-Paketdistributionen, wie z.B. Wheels, namens manylinux1_{x86_64,i686} vor, wobei externe Abhängigkeiten auf eine standardisierte, eingeschränkte Teilmenge des Linux-Kernels und der Core-Userspace-ABI beschränkt sind. Er schlägt vor, dass PyPI das Hochladen und Verteilen von Wheels mit diesem Plattform-Tag unterstützt und dass pip das Herunterladen und Installieren dieser Pakete auf kompatiblen Plattformen unterstützt.

Begründung

Derzeit ist die Verteilung von binären Python-Erweiterungen für Windows und OS X unkompliziert. Entwickler und Packager erstellen Wheels (PEP 427, PEP 491), denen Plattform-Tags wie win32 oder macosx_10_6_intel zugewiesen werden, und laden diese Wheels auf PyPI hoch. Benutzer können diese Wheels mit Tools wie pip herunterladen und installieren.

Für Linux ist die Situation weitaus heikler. Im Allgemeinen funktionieren kompilierte Python-Erweiterungsmodule, die auf einer Linux-Distribution erstellt wurden, nicht auf anderen Linux-Distributionen oder sogar auf verschiedenen Rechnern, auf denen dieselbe Linux-Distribution mit unterschiedlichen installierten Systembibliotheken läuft.

Build-Tools, die PEP 425-Plattform-Tags verwenden, verfolgen keine Informationen über die spezifische Linux-Distribution oder die installierten Systembibliotheken und weisen stattdessen allen Wheels die zu vagen Tags linux_i686 oder linux_x86_64 zu. Aufgrund dieser Mehrdeutigkeit besteht keine Erwartung, dass auf einer Maschine kompilierte, mit linux getaggte kompilierte Distributionen auf einer anderen Maschine ordnungsgemäß funktionieren. Aus diesem Grund hat PyPI das Hochladen von Wheels für Linux nicht gestattet.

Es wäre ideal, wenn Wheel-Pakete kompiliert werden könnten, die auf *jedem* Linux-System funktionieren. Aber aufgrund der unglaublichen Vielfalt von Linux-Systemen – von PCs über Android bis hin zu eingebetteten Systemen mit benutzerdefinierten Libcs – kann dies im Allgemeinen nicht garantiert werden.

Stattdessen definieren wir eine Standard-Teilmenge der Kernel- und Core-Userspace-ABI, die in der Praxis ausreichend kompatibel ist, sodass Pakete, die diesem Standard entsprechen, auf *vielen* Linux-Systemen funktionieren, einschließlich praktisch aller gängigen Desktop- und Server-Distributionen. Wir wissen das, weil es Unternehmen gibt, die solche weithin portablen, vorkompilierten Python-Erweiterungsmodule für Linux vertreiben – z.B. Enthought mit Canopy [4] und Continuum Analytics mit Anaconda [5].

Aufbauend auf den Kompatibilitätslektionen, die von diesen Unternehmen gelernt wurden, definieren wir daher einen grundlegenden manylinux1-Plattform-Tag für die Verwendung durch binäre Python-Wheels und führen die Implementierung vorläufiger Tools ein, um den Bau dieser manylinux1-Wheels zu unterstützen.

Wesentliche Ursachen für Inkompatibilitäten zwischen Linux-Binärdateien

Um einen Standard zu definieren, der garantiert, dass Wheel-Pakete, die diese Spezifikation erfüllen, auf *vielen* Linux-Plattformen funktionieren, ist es notwendig, die Grundursachen zu verstehen, die oft die Portabilität von vorkompilierten Binärdateien unter Linux verhindern. Die beiden Hauptursachen sind Abhängigkeiten von Shared Libraries, die auf den Systemen der Benutzer nicht vorhanden sind, und Abhängigkeiten von bestimmten Versionen bestimmter Kernbibliotheken wie glibc.

Externe Shared Libraries

Die meisten Desktop- und Server-Linux-Distributionen verfügen über einen Systempaketmanager (Beispiele sind APT auf Debian-basierten Systemen, yum auf RPM-basierten Systemen und pacman auf Arch Linux), der unter anderem die Installation von Shared Libraries verwaltet, die in Systemverzeichnissen wie /usr/lib installiert sind. Die meisten nicht-trivialen Python-Erweiterungen hängen von einer oder mehreren dieser Shared Libraries ab und funktionieren daher nur auf Systemen korrekt, auf denen der Benutzer die richtigen Bibliotheken (und die richtigen Versionen davon) entweder mit seinem Paketmanager installiert hat oder manuell installiert hat, indem er bestimmte Umgebungsvariablen wie LD_LIBRARY_PATH gesetzt hat, um den Laufzeitlinker über den Speicherort der benötigten Shared Libraries zu informieren.

Versionierung von Core Shared Libraries

Selbst wenn die Entwickler eines Python-Erweiterungsmoduls keine externen Shared Libraries verwenden möchten, haben die Module im Allgemeinen eine dynamische Laufzeitabhängigkeit von der GNU C-Bibliothek, glibc. Obwohl dies möglich ist, ist das statische Verlinken von glibc normalerweise eine schlechte Idee, da bestimmte wichtige C-Funktionen wie dlopen() nicht aus Code aufgerufen werden können, der glibc statisch verlinkt. Eine Laufzeitabhängigkeit von Shared Libraries auf einer systemseitig bereitgestellten glibc ist praktisch unvermeidlich.

Die Maintainer der GNU C Library befolgen ein strenges Symbol-Versionierungsschema für Abwärtskompatibilität. Dies stellt sicher, dass Binärdateien, die gegen eine ältere Version von glibc kompiliert wurden, auf Systemen ausgeführt werden können, auf denen eine neuere glibc vorhanden ist. Das Gegenteil ist im Allgemeinen nicht der Fall – Binärdateien, die auf neueren Linux-Distributionen kompiliert wurden, neigen dazu, auf versionierte Funktionen in glibc zurückzugreifen, die auf älteren Systemen nicht verfügbar sind.

Dies verhindert im Allgemeinen, dass Wheels, die auf den neuesten Linux-Distributionen kompiliert wurden, portabel sind.

Die manylinux1-Richtlinie

Aus diesen Gründen müssen Python-Wheels für breite Portabilität

  • nur von einem äußerst begrenzten Satz externer Shared Libraries abhängen; und
  • nur von "alten" Symbolversionen in diesen externen Shared Libraries abhängen; und
  • nur von einer weitgehend kompatiblen Kernel-ABI abhängen.

Um für den manylinux1-Plattform-Tag in Frage zu kommen, muss ein Python-Wheel daher (a) binäre ausführbare Dateien und kompilierten Code enthalten, der *nur* an Bibliotheken mit SONAMEs bindet, die in der folgenden Liste enthalten sind

libpanelw.so.5
libncursesw.so.5
libgcc_s.so.1
libstdc++.so.6
libm.so.6
libdl.so.2
librt.so.1
libc.so.6
libnsl.so.1
libutil.so.1
libpthread.so.0
libresolv.so.2
libX11.so.6
libXext.so.6
libXrender.so.1
libICE.so.6
libSM.so.6
libGL.so.1
libgobject-2.0.so.0
libgthread-2.0.so.0
libglib-2.0.so.0

und (b) auf einem Standard-CentOS 5.11 [6]-System funktionieren, das die vom Systempaketmanager bereitgestellten Versionen dieser Bibliotheken enthält.

libcrypt.so.1 wurde retrospektiv von der Whitelist entfernt, nachdem Fedora 30 mit libcrypt.so.2 anstelle dessen veröffentlicht wurde.

Da CentOS 5 nur für x86_64- und i686-Architekturen verfügbar ist, sind dies die einzigen Architekturen, die derzeit von der manylinux1-Richtlinie unterstützt werden.

Auf Debian-basierten Systemen werden diese Bibliotheken von den Paketen bereitgestellt

libncurses5 libgcc1 libstdc++6 libc6 libx11-6 libxext6
libxrender1 libice6 libsm6 libgl1-mesa-glx libglib2.0-0

Auf RPM-basierten Systemen werden diese Bibliotheken von den Paketen bereitgestellt

ncurses libgcc libstdc++ glibc libXext libXrender
libICE libSM mesa-libGL glib2

Diese Liste wurde durch Überprüfung der externen Shared-Library-Abhängigkeiten der Canopy [4]- und Anaconda [5]-Distributionen zusammengestellt, die beide eine breite Palette der beliebtesten Python-Module enthalten und sich in der Praxis als über eine breite Palette von Linux-Systemen hinweg funktionierend erwiesen haben.

Viele der zulässigen Systembibliotheken, die oben aufgeführt sind, verwenden Symbol-Versionierungsschemata für Abwärtskompatibilität. Die neuesten Symbolversionen, die mit den CentOS 5.11-Versionen dieser Bibliotheken geliefert werden, sind

GLIBC_2.5
CXXABI_3.4.8
GLIBCXX_3.4.9
GCC_4.2.0

Daher darf, als Konsequenz von Anforderung (b), jedes Wheel, das von versionierten Symbolen der obigen Shared Libraries abhängt, nur von Symbolen mit den folgenden Versionen abhängen

GLIBC <= 2.5
CXXABI <= 3.4.8
GLIBCXX <= 3.4.9
GCC <= 4.2.0

Diese Empfehlungen sind das Ergebnis der einschlägigen Diskussionen im Januar 2016 [7], [8].

Beachten Sie, dass wir in unseren Empfehlungen unten nicht vorschlagen, dass pip oder PyPI versuchen, die Details dieser Richtlinie zu überprüfen und durchzusetzen (genauso wie sie die Details bestehender Plattform-Tags wie win32 nicht überprüfen und durchsetzen). Der obige Text wird (a) als Rat für Paketbauer und (b) als Methode zur Schuldzuweisung bereitgestellt, wenn ein bestimmtes Wheel auf einem bestimmten System nicht funktioniert: Wenn es die obige Richtlinie erfüllt, dann ist dies ein Fehler in der Spezifikation oder im Installationswerkzeug; wenn es die obige Richtlinie nicht erfüllt, dann ist es ein Fehler im Wheel. Eine nützliche Konsequenz dieses Ansatzes ist, dass er die Möglichkeit weiterer Aktualisierungen und Anpassungen offen lässt, sobald wir mehr Erfahrung sammeln. Zum Beispiel könnten wir eine "manylinux 1.1"-Richtlinie haben, die auf dieselben Systeme abzielt und denselben manylinux1-Plattform-Tag verwendet (und somit keine weiteren Änderungen an pip oder PyPI erfordert), aber die obige Liste anpasst, um Bibliotheken zu entfernen, die sich als problematisch erwiesen haben, oder Bibliotheken hinzuzufügen, die sich als sicher erwiesen haben.

libpythonX.Y.so.1

Beachten Sie, dass libpythonX.Y.so.1 *nicht* auf der Liste der Bibliotheken steht, zu denen sich eine manylinux1-Erweiterung verlinken darf. Explizites Verlinken mit libpythonX.Y.so.1 ist in fast allen Fällen unnötig: Da ELF-Linking so funktioniert, erhalten Erweiterungsmodule, die in den Interpreter geladen werden, automatisch Zugriff auf alle Symbole des Interpreters, unabhängig davon, ob die Erweiterung selbst explizit gegen libpython verlinkt ist. Darüber hinaus verursacht das explizite Verlinken mit libpython Probleme in der gängigen Konfiguration, bei der Python nicht mit --enable-shared erstellt wird. Insbesondere unter Debian- und Ubuntu-Systemen wird apt install pythonX.Y nicht einmal libpythonX.Y.so.1 installiert, was bedeutet, dass jedes Wheel, das von libpythonX.Y.so.1 abhängt, beim Import fehlschlagen könnte.

Es gibt eine Situation, in der auf diese Weise verlinkte Erweiterungen fehlschlagen können: Wenn ein Host-Programm (z.B. apache2) dlopen() verwendet, um ein Modul (z.B. mod_wsgi) zu laden, das den CPython-Interpreter einbettet, und das Host-Programm das RTLD_GLOBAL-Flag *nicht* an dlopen() übergibt, dann kann der eingebettete CPython keine Erweiterungsmodule laden, die nicht selbst explizit mit libpythonX.Y.so.1 verlinkt sind. Glücklicherweise setzt apache2 das RTLD_GLOBAL-Flag, ebenso wie alle anderen Programme, die CPython über ein dlopened Plugin einbetten, die wir lokalisieren konnten. Dies scheint also in der Praxis kein ernstes Problem zu sein. Die Inkompatibilität mit Debian/Ubuntu ist eher ein Problem als die theoretische Inkompatibilität mit einem eher obskuren Eckfall.

Dies ist ein eher komplexes und subtiles Thema, das über den Geltungsbereich von manylinux1 hinausgeht; weitere Diskussionen hierzu finden Sie unter: [9], [10], [11].

UCS-2 vs. UCS-4 Builds

Alle Versionen von CPython 2.x sowie CPython 3.0-3.2 inclusive können in zwei ABI-inkompatiblen Modi kompiliert werden: Builds mit dem Konfigurationsflag --enable-unicode=ucs2 speichern Unicode-Daten im UCS-2 (oder eigentlich UTF-16) Format, während Builds mit dem Konfigurationsflag --enable-unicode=ucs4 Unicode-Daten im UCS-4 Format speichern. (CPython 3.3 und höher verwenden eine andere Speichermethode, die immer UCS-4 unterstützt.) Wenn wir sicherstellen wollen, dass ucs2-Wheels nicht in ucs4-CPythons und umgekehrt installiert werden, muss etwas unternommen werden.

Eine frühere Version dieses PEP enthielt die Anforderung, dass manylinux1-Wheels, die sich an diese älteren CPython-Versionen richten, immer die ucs4-ABI verwenden sollten. Aber dann, zwischen der anfänglichen Annahme des PEP und seiner Implementierung, erhielten pip und wheel erstklassige Unterstützung für die Verfolgung und Überprüfung dieses Aspekts der ABI-Kompatibilität für die relevanten CPython-Versionen, was eine bessere Lösung ist. Daher erlauben wir nun, dass die manylinux1-Plattform-Tags in Kombination mit jedem ABI-Tag verwendet werden. Um die Kompatibilität zu gewährleisten, ist es jedoch entscheidend, sicherzustellen, dass alle manylinux1-Wheels ein nicht-triviales ABI-Tag enthalten. Zum Beispiel könnte ein Wheel, das gegen einen ucs4-CPython erstellt wurde, einen Namen wie haben

PKG-VERSION-cp27-cp27mu-manylinux1_x86_64.whl
                 ^^^^^^ Good!

Während ein Wheel, das gegen die ucs2-ABI erstellt wurde, einen Namen wie haben könnte

PKG-VERSION-cp27-cp27m-manylinux1_x86_64.whl
                 ^^^^^ Okay!

Aber Sie sollten niemals ein Wheel mit einem Namen wie haben

PKG-VERSION-cp27-none-manylinux1_x86_64.whl
                 ^^^^ BAD! Don't do this!

Dieses Wheel gibt vor, gleichzeitig mit *sowohl* ucs2 als auch ucs4 Builds kompatibel zu sein, was schlecht ist.

Wir stellen zur Information fest, dass die ucs4-ABI bei Linux CPython-Distributoren weitaus verbreiteter zu sein scheint.

fpectl Builds vs. keine fpectl Builds

Alle existierenden Versionen von CPython können entweder mit oder ohne das --with-fpectl-Flag für configure kompiliert werden. Es stellt sich heraus, dass dies die CPython-ABI ändert: Erweiterungen, die gegen einen CPython ohne fpectl kompiliert wurden, sind immer kompatibel mit einem CPython mit fpectl, aber das Gegenteil ist nicht unbedingt der Fall. (Symptom: Fehler beim Import, die über undefined symbol: PyFPE_jbuf klagen.) Siehe: [16].

Für maximale Kompatibilität muss der CPython, der zum Erstellen von manylinux1-Wheels verwendet wird, daher *ohne* das --with-fpectl-Flag kompiliert werden, und manylinux1-Erweiterungen dürfen das Symbol PyFPE_jbuf nicht referenzieren.

Kompilierung konformer Wheels

Die Art und Weise, wie glibc, libgcc und libstdc++ ihre Symbol-Versionierung verwalten, bedeutet, dass die Compiler-Toolchains, die die meisten Entwickler für ihre tägliche Arbeit verwenden, in der Praxis nicht in der Lage sind, manylinux1-konforme Wheels zu erstellen. Daher versuchen wir nicht, das Standardverhalten von pip wheel / bdist_wheel zu ändern: Sie werden weiterhin reguläre linux_*-Plattform-Tags generieren, und Entwickler, die diese zum Generieren von manylinux1-getaggten Wheels verwenden möchten, müssen den Tag als zweiten Nachbearbeitungsschritt ändern.

Zur Unterstützung der Kompilierung von Wheels, die dem manylinux1-Standard entsprechen, stellen wir erste Entwürfe von zwei Tools bereit.

Docker-Image

Das erste Tool ist ein Docker-Image basierend auf CentOS 5.11, das als einfach zu bedienende, in sich geschlossene Build-Umgebung für die Kompilierung von manylinux1-Wheels empfohlen wird [12]. Die Kompilierung auf einer neuer veröffentlichten Linux-Distribution führt im Allgemeinen zu Abhängigkeiten von zu neuen versionierten Symbolen. Das Image wird mit einer vollständigen Compiler-Suite geliefert (inklusive gcc, g++ und gfortran 4.8.2) sowie den neuesten Versionen von Python und pip.

Auditwheel

Das zweite Tool ist ein Kommandozeilen-Executable namens auditwheel [13], das Paketbetreuern bei der Handhabung von externen Drittanbieter-Abhängigkeiten helfen kann.

Es gibt mindestens drei Methoden, um Wheels zu erstellen, die externe Drittanbieter-Bibliotheken auf eine Weise verwenden, die der obigen Richtlinie entspricht.

  1. Die externen Drittanbieter-Bibliotheken können statisch verlinkt werden.
  2. Die externen Drittanbieter-Shared-Libraries können in separaten Paketen auf PyPI verteilt werden, von denen das Wheel abhängt.
  3. Die externen Drittanbieter-Shared-Libraries können innerhalb der Wheel-Bibliotheken gebündelt und mit einem relativen Pfad verlinkt werden.

All dies sind gültige Optionen, die von verschiedenen Paketen und Communities effektiv genutzt werden können. Statisches Verlinken erfordert im Allgemeinen paketspezifische Änderungen am Build-System, und die Verteilung von Drittanbieter-Abhängigkeiten auf PyPI erfordert möglicherweise eine Koordination der Community der Paketnutzer.

Als oft automatische Alternative zu diesen Optionen stellen wir auditwheel vor. Das Tool inspiziert alle ELF-Dateien innerhalb eines Wheels, um auf Abhängigkeiten von versionierten Symbolen oder externen Shared Libraries zu prüfen, und verifiziert die Konformität mit der manylinux1-Richtlinie. Dies beinhaltet die Fähigkeit, den neuen Plattform-Tag zu konformen Wheels hinzuzufügen. Wichtiger ist jedoch, dass auditwheel die Fähigkeit besitzt, Wheels, die von externen Shared Libraries abhängen, automatisch zu modifizieren, indem es diese Shared Libraries vom System in das Wheel selbst kopiert und die entsprechenden RPATH-Einträge modifiziert, sodass diese Bibliotheken zur Laufzeit gefunden werden. Dies erreicht ein ähnliches Ergebnis wie eine statische Verlinkung, ohne Änderungen am Build-System zu erfordern. Paketierer werden darauf hingewiesen, dass das Bündeln, ähnlich wie das statische Verlinken, Urheberrechtsbedenken aufwerfen kann.

Gebündelte Wheels unter Linux

Obwohl wir viele Ansätze für den Umgang mit Drittanbieter-Bibliotheksabhängigkeiten innerhalb von manylinux1-Wheels anerkennen, erkennen wir an, dass die manylinux1-Richtlinie das Bündeln externer Abhängigkeiten fördert, eine Praxis, die gegen die Paketmanagementrichtlinien vieler Systempaketmanager von Linux-Distributionen verstößt [14], [15]. Der Hauptzweck dessen ist die plattformübergreifende Kompatibilität. Darüber hinaus nehmen manylinux1-Wheels auf PyPI eine andere Nische ein als die Python-Pakete, die über den Systempaketmanager verfügbar sind.

Die Entscheidung in diesem PEP, von allgemeinen Unbundling-Richtlinien von Linux-Distributionen abzuweichen, wird von folgenden Überlegungen beeinflusst

  1. In Zeiten automatisierter Continuous Integration und Deployment-Pipelines ist die Veröffentlichung neuer Versionen und die Aktualisierung von Abhängigkeiten einfacher als früher, als diese Richtlinien definiert wurden.
  2. pip-Benutzer können weiterhin die Option "--no-binary" verwenden, wenn sie lokale Builds anstelle von vorkompilierten Wheel-Dateien erzwingen möchten.
  3. Die Popularität moderner containerbasierter Bereitstellungsmodelle und "Immutable Infrastructure"-Modelle beinhaltet ohnehin eine erhebliche Bündelung auf Anwendungsebene.
  4. Die Verteilung gebündelter Wheels über PyPI ist derzeit die Norm für Windows und OS X.
  5. Dieser PEP schließt die Idee nicht aus, in Zukunft gezieltere Binärdateien für spezifische Linux-Distributionen anzubieten.

Das in diesem PEP beschriebene Modell eignet sich am besten für plattformübergreifende Python-Pakete, da es ihnen ermöglicht, einen Großteil der Arbeit, die sie bereits für die Erstellung statischer Windows- und OS X-Wheels leisten, wiederzuverwenden. Wir erkennen an, dass es für Linux-spezifische Pakete weniger optimal ist, die möglicherweise lieber enger mit der einzigartigen Paketmanagementfunktionalität von Linux interagieren und nur daran interessiert sind, eine kleine Gruppe bestimmter Distros anzuzielen.

Sicherheitsimplikationen

Einer der Vorteile von Abhängigkeiten von zentralisierten Bibliotheken unter Linux ist, dass Fehlerbehebungen und Sicherheitsaktualisierungen systemweit bereitgestellt werden können und Anwendungen, die von diesen Bibliotheken abhängen, automatisch von diesen Patches profitieren, wenn die zugrunde liegenden Bibliotheken aktualisiert werden. Dies kann besonders wichtig für Sicherheitsaktualisierungen in Paketen sein, die Netzwerkkommunikation oder Kryptographie betreffen.

manylinux1-Wheels, die über PyPI vertrieben werden und sicherheitskritische Bibliotheken wie OpenSSL bündeln, übernehmen somit die Verantwortung für zeitnahe Updates im Hinblick auf offengelegte Schwachstellen und Patches. Dies ähnelt stark den Sicherheitsimplikationen der Verteilung von Binär-Wheels unter Windows, die, da die Plattform über keinen Systempaketmanager verfügt, ihre Abhängigkeiten im Allgemeinen bündeln. Insbesondere kann OpenSSL aufgrund seiner fehlenden stabilen ABI nicht in das manylinux1-Profil aufgenommen werden.

Plattformerkennung für Installer

Oben haben wir definiert, was es bedeutet, dass ein *Wheel* manylinux1-kompatibel ist. Hier diskutieren wir, was es bedeutet, dass eine *Python-Installation* manylinux1-kompatibel ist. Dies ist insbesondere für Tools wie pip wichtig, wenn sie entscheiden, ob sie manylinux1-getaggte Wheels zur Installation in Betracht ziehen sollen.

Da das manylinux1-Profil bereits für die vielen Tausenden von Benutzern beliebter kommerzieller Python-Distributionen bekannt ist und funktioniert, schlagen wir vor, dass Installationswerkzeuge davon ausgehen sollten, dass ein System *kompatibel* ist, es sei denn, es gibt spezifische Gründe, dies anders anzunehmen.

Wir kennen vier Hauptquellen für potenzielle Inkompatibilitäten, die in der Praxis wahrscheinlich auftreten werden

  • Zukünftig kann es Distributionen geben, die die Kompatibilität mit diesem Profil brechen (z.B. wenn eine der Bibliotheken im Profil ihre ABI auf eine abwärtsinkompatible Weise ändert)
  • Eine zu alte Linux-Distribution (z.B. RHEL 4)
  • Eine Linux-Distribution, die glibc nicht verwendet (z.B. Alpine Linux, das auf musl libc basiert, oder Android)

Um diese zu bewältigen, schlagen wir einen zweigleisigen Ansatz vor. Um potenzielle zukünftige Inkompatibilitäten zu behandeln, standardisieren wir einen Mechanismus, mit dem ein Python-Distributor signalisieren kann, dass eine bestimmte Python-Installation definitiv mit manylinux1 kompatibel ist oder nicht: Dies geschieht durch die Installation eines Moduls namens _manylinux und die Einstellung seines manylinux1_compatible-Attributs. Wir schlagen nicht vor, ein solches Modul zur Standardbibliothek hinzuzufügen – dies ist lediglich ein bekannter Name, mit dem Distributoren und Installationswerkzeuge kommunizieren können. Wenn ein Distributor dieses Modul jedoch hinzufügt, *sollte er es zur Standardbibliothek hinzufügen* und nicht in ein site-packages/-Verzeichnis, da die Standardbibliothek von Virtualenvs (die wir wollen) geerbt wird, site-packages/ im Allgemeinen aber nicht.

Um dann die letzten beiden Fälle für bestehende Python-Distributionen zu behandeln, schlagen wir eine einfache und zuverlässige Methode vor, um die Anwesenheit und Version von glibc zu überprüfen (im Grunde als "Uhr" für das Gesamterscheinungsbild der Distribution).

Speziell ist der von uns vorgeschlagene Algorithmus

def is_manylinux1_compatible():
    # Only Linux, and only x86-64 / i686
    from distutils.util import get_platform
    if get_platform() not in ["linux-x86_64", "linux-i686"]:
        return False

    # Check for presence of _manylinux module
    try:
        import _manylinux
        return bool(_manylinux.manylinux1_compatible)
    except (ImportError, AttributeError):
        # Fall through to heuristic check below
        pass

    # Check glibc version. CentOS 5 uses glibc 2.5.
    return have_compatible_glibc(2, 5)

def have_compatible_glibc(major, minimum_minor):
    import ctypes

    process_namespace = ctypes.CDLL(None)
    try:
        gnu_get_libc_version = process_namespace.gnu_get_libc_version
    except AttributeError:
        # Symbol doesn't exist -> therefore, we are not linked to
        # glibc.
        return False

    # Call gnu_get_libc_version, which returns a string like "2.5".
    gnu_get_libc_version.restype = ctypes.c_char_p
    version_str = gnu_get_libc_version()
    # py2 / py3 compatibility:
    if not isinstance(version_str, str):
        version_str = version_str.decode("ascii")

    # Parse string and check against requested version.
    version = [int(piece) for piece in version_str.split(".")]
    assert len(version) == 2
    if major != version[0]:
        return False
    if minimum_minor > version[1]:
        return False
    return True

Abgelehnte Alternativen: Wir haben auch eine Konfigurationsdatei erwogen, z.B. /etc/python/compatibility.cfg. Das Problem dabei ist, dass ein einzelnes Dateisystem viele verschiedene Interpreterumgebungen mit jeweils eigenem ABI-Profil enthalten kann – die manylinux1-Kompatibilität eines systeminstallierten x86_64 CPython sagt uns möglicherweise nicht viel über die manylinux1-Kompatibilität eines benutzerinstallierten i686 PyPy aus. Das Platzieren dieser Konfigurationsinformationen innerhalb der Python-Umgebung selbst stellt sicher, dass sie an die richtige Binärdatei angehängt bleibt, und vereinfacht den Lookup-Code drastisch.

Wir haben auch eine elaboriertere Struktur in Betracht gezogen, wie eine Liste aller Plattform-Tags, die als kompatibel gelten sollten, zusammen mit ihrer bevorzugten Reihenfolge, zum Beispiel: _binary_compat.compatible = ["manylinux1_x86_64", "centos5_x86_64", "linux_x86_64"]. Dies führt jedoch zu mehreren Komplikationen. Zum Beispiel wollen wir zwischen dem Zustand "unterstützt manylinux1 nicht" (oder später manylinux2 usw.) und "gibt keine klare Aussage darüber, ob es manylinux1 unterstützt" unterscheiden, was im obigen Format nicht ganz offensichtlich ist; und es ist überhaupt nicht klar, welche Funktionen wirklich im Hinblick auf die Präferenzreihenfolge benötigt werden, da es derzeit nur die Plattform-Tags manylinux1 und linux gibt. Daher verschieben wir eine vollständigere Lösung hierfür auf einen separaten PEP, wenn / falls Linux weitere Plattform-Tags erhält.

Für die Prüfung der Bibliothekskompatibilität haben wir auch wesentlich elaboriertere Prüfungen in Betracht gezogen (z.B. Überprüfung der Kernel-Version, Suche und Überprüfung der Versionen aller einzelnen Bibliotheken, die im manylinux1-Profil aufgeführt sind usw.), aber letztendlich entschieden, dass dies eher zu verwirrenden Fehlern führen würde als dem Benutzer tatsächlich zu helfen. (Zum Beispiel: Verschiedene Distributionen unterscheiden sich darin, wo sie diese Bibliotheken tatsächlich ablegen, und wenn unser Prüfcode den falschen Pfad nicht korrekt sucht, könnte er leicht falsche Antworten liefern.)

PyPI-Unterstützung

PyPI sollte das Hochladen von Wheels mit dem manylinux1-Plattform-Tag zulassen. PyPI sollte nicht versuchen, formell zu überprüfen, ob Wheels mit dem manylinux1-Plattform-Tag der in diesem Dokument beschriebenen manylinux1-Richtlinie entsprechen. Diese Verifizierungsaufgabe sollte anderen Werkzeugen überlassen werden, wie z.B. auditwheel, die separat entwickelt werden.

Abgelehnte Alternativen

Eine Alternative wäre, separate Plattform-Tags für jede Linux-Distribution (und jede Version davon) bereitzustellen, z.B. RHEL6, ubuntu14_10, debian_jessie, usw. Nichts in diesem Vorschlag schließt die Möglichkeit aus, solche Plattform-Tags in Zukunft hinzuzufügen, oder weitere Erweiterungen der Wheel-Metadaten, die es Wheels erlauben würden, Abhängigkeiten von externen, systeminstallierten Paketen zu deklarieren. Solche Erweiterungen würden jedoch erheblich mehr Arbeit erfordern als dieser Vorschlag, und Paketentwickler, die nicht mehrere Build-Umgebungen pflegen und mehrere Wheels erstellen möchten, um alle gängigen Linux-Distributionen abzudecken, würden dies möglicherweise immer noch nicht schätzen. Daher betrachten wir solche Vorschläge als außerhalb des Geltungsbereichs dieses PEP.

Zukünftige Aktualisierungen

Wir gehen davon aus, dass es zu einem späteren Zeitpunkt eine manylinux2 geben wird, die eine modernere Baseline-Umgebung spezifiziert (vielleicht basierend auf CentOS 6), und irgendwann eine manylinux3 und so weiter, aber wir verschieben die Spezifikation dieser, bis wir mehr Erfahrung mit dem ursprünglichen manylinux1-Vorschlag haben.

Referenzen


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

Zuletzt geändert: 2025-02-01 08:59:27 GMT