PEP 668 – Markieren von Python-Basisumgebungen als „extern verwaltet“
- Autor:
- Geoffrey Thomas <geofft at ldpreload.com>, Matthias Klose <doko at ubuntu.com>, Filipe Laíns <lains at python.org>, Donald Stufft <donald at stufft.io>, Tzu-ping Chung <uranusjr at gmail.com>, Stefano Rivera <stefanor at debian.org>, Elana Hashman <ehashman at debian.org>, Pradyun Gedam <pradyunsg at gmail.com>
- PEP-Delegate:
- Paul Moore <p.f.moore at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Akzeptiert
- Typ:
- Standards Track
- Thema:
- Packaging
- Erstellt:
- 18. Mai 2021
- Post-History:
- 28. Mai 2021
- Resolution:
- Discourse-Nachricht
Zusammenfassung
Ein seit langem bestehendes praktisches Problem für Python-Benutzer sind Konflikte zwischen OS-Paketmanagern und Python-spezifischen Paketmanagement-Tools wie pip. Diese Konflikte umfassen sowohl Python-API-Inkompatibilitäten als auch Konflikte um den Besitz von Dateien.
Historisch gesehen haben Python-spezifische Paketmanagement-Tools standardmäßig Pakete in einen impliziten globalen Kontext installiert. Mit der Standardisierung und Popularität virtueller Umgebungen ist eine bessere Lösung für die meisten (aber nicht alle) Anwendungsfälle, Python-spezifische Paketmanagement-Tools nur innerhalb einer virtuellen Umgebung zu verwenden.
Dieses PEP schlägt einen Mechanismus vor, mit dem eine Python-Installation Tools wie pip mitteilen kann, dass ihr globaler Paketinstallationskontext durch externe Mittel verwaltet wird, wie z. B. einen OS-Paketmanager. Es spezifiziert, dass Python-spezifische Paketmanagement-Tools standardmäßig weder Pakete in den globalen Kontext des Interpreters installieren noch daraus entfernen sollten und stattdessen den Endbenutzer zur Verwendung einer virtuellen Umgebung leiten sollten.
Es standardisiert auch eine Interpretation der sysconfig Schemata, sodass, wenn ein Python-spezifischer Paketmanager dabei ist, ein Paket im Interpreter-weiten Kontext zu installieren, dies so erfolgen kann, dass Konflikte mit dem externen Paketmanager vermieden werden und das Risiko, von externen Paketmanagern ausgelieferte Software zu beschädigen, reduziert wird.
Terminologie
Einige Begriffe, die in diesem PEP verwendet werden, haben in den von ihm abgedeckten Kontexten mehrere Bedeutungen. Zur Klarheit verwendet dieses PEP die folgenden Begriffe in spezifischen Weisen
- distro
- Kurzform für „Distribution“. Eine Sammlung verschiedener Softwarearten, die idealerweise so konzipiert ist, dass sie richtig zusammenarbeitet, einschließlich (in relevanten Kontexten für dieses Dokument) des Python-Interpreters selbst, in Python geschriebener Software und in anderen Sprachen geschriebener Software. Das heißt, dies ist der Sinn, der in Phrasen wie „Linux distro“ oder „Berkeley Software Distribution“ verwendet wird.
Eine Distro kann ein eigenes Betriebssystem (OS) sein, wie z. B. Debian, Fedora oder FreeBSD. Sie kann auch eine Overlay-Distribution sein, die auf einem vorhandenen OS installiert wird, wie z. B. Homebrew oder MacPorts.
Dieses Dokument verwendet die Kurzform „Distro“, da der Begriff „Distribution“ in Python-Packaging-Kontexten eine andere Bedeutung hat: eine Quell- oder Binärdistributionspaket eines einzelnen Stücks Python-Software. Das heißt, im Sinne von
setuptools.dist.Distributionoder „sdist“. Um Verwirrung zu vermeiden, verwendet dieses Dokument den einfachen Begriff „Distribution“ überhaupt nicht. Im Python-Packaging-Sinne verwendet es den vollständigen Ausdruck „Distributionspaket“ oder einfach nur „Paket“ (siehe unten).Der Anbieter einer Distro – das Team oder Unternehmen, das die Software sammelt, veröffentlicht und alle notwendigen Modifikationen vornimmt – ist sein **Distributor**.
- Paket
- Eine Einheit von Software, die in Python installiert und verwendet werden kann. Das heißt, dies bezieht sich darauf, was Python-spezifische Packaging-Tools als „Distributionspaket“ oder einfach nur als „Distribution“ bezeichnen; die umgangssprachliche Abkürzung „Paket“ wird im Sinne des Python Package Index verwendet.
Dieses Dokument verwendet „Paket“ nicht im Sinne eines importierbaren Namens, der Python-Module enthält, obwohl in vielen Fällen ein Distributionspaket aus einem einzelnen importierbaren Paket desselben Namens besteht.
Dieses Dokument verwendet den Begriff „Paket“ im Allgemeinen nicht, um Einheiten zu bezeichnen, die vom Paketmanager einer Distro installiert werden (wie z. B.
.deboder.rpmDateien). Wenn nötig, wird die Formulierung „ein Paket der Distro“ verwendet. (Auch hier wird in vielen Fällen ein Python-Paket innerhalb eines Pakets der Distro mit einem Namen wiepython-gefolgt vom Python-Paketnamen ausgeliefert.) - Python-spezifischer Paketmanager
- Ein Werkzeug zur Installation, Aktualisierung und/oder Deinstallation von Python-Paketen, das den Python-Packaging-Standards (wie PEP 376 und PEP 427) entspricht. Der beliebteste Python-spezifische Paketmanager ist pip [1]; andere Beispiele sind der alte Easy Install-Befehl [2] sowie die direkte Verwendung eines
setup.py-Befehls.(Conda [3] ist eine Sonderfall, da der
conda-Befehl mehr als nur Python-Pakete installieren kann und damit in gewisser Hinsicht einem Distro-Paketmanager ähnelt. Da derconda-Befehl im Allgemeinen nur auf von Conda erstellte Umgebungen angewendet wird, gelten die meisten Bedenken dieses Dokuments nicht fürconda, wenn es als Python-spezifischer Paketmanager agiert.) - Distro-Paketmanager
- Ein Werkzeug zur Installation, Aktualisierung und/oder Deinstallation von Paketen einer Distro in einer installierten Instanz dieser Distro, das in der Lage ist, sowohl Python-Pakete als auch Nicht-Python-Pakete zu installieren und daher im Allgemeinen seine eigene Datenbank mit installierter Software hat, die unabhängig von PEP 376 ist. Beispiele hierfür sind
apt,dpkg,dnf,rpm,pacmanundbrew. Das hervorstechendste Merkmal ist, dass die Deinstallation oder Aktualisierung eines von einem Distro-Paketmanager installierten Pakets auf eine Weise, die einen Python-spezifischen Paketmanager zufriedenstellen würde, den Distro-Paketmanager im Allgemeinen in einem inkonsistenten Zustand hinterlassen würde.Dieses Dokument verwendet auch Phrasen wie „externer Paketmanager“ oder „System-Paketmanager“, um in bestimmten Kontexten einen Distro-Paketmanager zu bezeichnen.
- schatten
- Ein installiertes Python-Paket zu „schatten“ bedeutet, dass ein anderes Paket für Importe bevorzugt wird, ohne Dateien aus dem schattierten Paket zu entfernen. Dies erfordert mehrere Einträge in
sys.path: Wenn Paket A 2.0 das Modula.pyin einemsys.pathEintrag installiert, und Paket A 1.0 das Modula.pyin einem späterensys.pathEintrag installiert, dann gibtimport adas Modul aus dem ersteren zurück, und wir sagen, dass A 2.0 A 1.0 schattet.
Motivation
Dank der immensen Popularität von Python liefern Software-Distros (womit wir sowohl Linux- und andere OS-Distros als auch Overlay-Distros wie Homebrew und MacPorts meinen) Python im Allgemeinen für zwei Zwecke aus: als Softwarepaket, das von Endbenutzern selbst verwendet wird, und als Sprachabhängigkeit für andere Software in der Distro.
Zum Beispiel liefern Fedora und Debian (und ihre Downstream-Distros sowie viele andere) ein /usr/bin/python3-Binärprogramm aus, das den für Endbenutzer verfügbaren Befehl python3 sowie den #!/usr/bin/python3 Shebang für in der Distro enthaltene Python-Sprachsoftware bereitstellt. Da es keine offiziellen Binärreleases von Python für Linux/UNIX gibt, verwenden fast alle Python-Endbenutzer auf diesen Betriebssystemen den von ihrer Distro gebauten und ausgelieferten Python-Interpreter.
Die für die Benutzer der Distro verfügbare python3-Ausführbare Datei und die als Abhängigkeit für andere Software in der Distro verfügbare python3-Ausführbare Datei sind typischerweise dasselbe Binärprogramm. Das bedeutet, wenn ein Endbenutzer ein Python-Paket mit einem Tool wie pip außerhalb des Kontexts einer virtuellen Umgebung installiert, ist dieses Paket für in der Distro ausgelieferte Python-Sprachsoftware sichtbar. Wenn das neu installierte Paket (oder eine seiner Abhängigkeiten) eine neuere, abwärtsinkompatible Version eines Pakets ist, das über die Distro installiert wurde, kann dies Software brechen, die von der Distro ausgeliefert wird.
Dies kann ein kritisches Problem für die Integrität von Distros darstellen, die oft Paketmanagement-Tools haben, die selbst in Python geschrieben sind. Zum Beispiel ist es möglich, den dnf-Befehl von Fedora unbeabsichtigt mit einem pip install-Befehl zu brechen, was eine Wiederherstellung erschwert.
Dies gilt sowohl für systemweite Installationen (sudo pip install) als auch für Installationen im Home-Verzeichnis des Benutzers (pip install --user), da Pakete an beiden Orten auf dem sys.path von /usr/bin/python3 erscheinen.
Bei systemweiten Installationen gibt es ein schlimmeres Problem: Wenn Sie versuchen, diese Situation mit sudo pip uninstall zu beheben, können Sie Pakete entfernen, die vom Systempaketmanager ausgeliefert werden. Tatsächlich kann dies auch passieren, wenn Sie einfach ein Paket aktualisieren – pip versucht, die alte Version des Pakets zu entfernen, wie sie vom Betriebssystem ausgeliefert wurde. An diesem Punkt ist es möglicherweise nicht mehr möglich, das System mit der auf dem System verbleibenden Software in einen konsistenten Zustand zurückzuversetzen.
In den letzten vielen Jahren hat sich ein Konsens herausgebildet, dass der beste Weg zur Installation von Python-Bibliotheken oder -Anwendungen (wenn kein Paket der Distro verwendet wird) die Verwendung einer virtuellen Umgebung ist. Dieser Ansatz wurde durch das PyPA virtualenv-Projekt populär, und eine einfache Version dieses Ansatzes ist jetzt in der Python-Standardbibliothek als venv verfügbar. Die Installation eines Python-Pakets in eine virtuelle Umgebung verhindert, dass es für den nicht qualifizierten /usr/bin/python3-Interpreter sichtbar ist und verhindert, dass Systemsoftware beschädigt wird.
In einigen Fällen ist es jedoch nützlich und beabsichtigt, ein Python-Paket außerhalb der Distro zu installieren, das das Verhalten von Distro-ausgelieferten Befehlen beeinflusst. Dies ist üblich bei Software wie Sphinx oder Ansible, die einen Mechanismus zum Schreiben von Python-Sprach-Erweiterungen haben. Ein Benutzer möchte möglicherweise die Version der Basissoftware seiner Distro verwenden (aus Gründen der bezahlten Unterstützung oder Sicherheitsupdates), aber eine kleine Erweiterung von PyPI installieren, und er möchte, dass diese Erweiterung von der Software in seinem Basissystem importierbar ist.
Während dies weiterhin das Risiko birgt, eine neuere Version einer Abhängigkeit zu installieren, als das Betriebssystem erwartet, oder das Verhalten einer Anwendung anderweitig negativ zu beeinflussen, muss es nicht das Risiko mit sich bringen, Dateien vom Betriebssystem zu entfernen. Ein Tool wie pip sollte in der Lage sein, Pakete in einem Verzeichnis auf dem Standard- sys.path zu installieren, wenn dies ausdrücklich angefordert wird, ohne Dateien zu löschen, die dem Systempaketmanager gehören.
Daher schlägt dieses PEP zwei Dinge vor.
Erstens schlägt es **einen Weg für Distributoren eines Python-Interpreters vor, diesen Interpreter so zu kennzeichnen, dass seine Pakete extern zu Python verwaltet werden**, sodass Python-spezifische Tools wie pip die installierten Pakete im globalen sys.path des Interpreters nicht (hinzufügen, aktualisieren/herabstufen oder entfernen) ändern sollten, es sei denn, dies wird ausdrücklich überschrieben. Es bietet auch ein Mittel für den Distributor, anzugeben, wie eine virtuelle Umgebung als Alternative verwendet werden kann.
Dies ist ein Opt-in-Mechanismus: Standardmäßig wird der aus Upstream-Quellen kompilierte Python-Interpreter nicht so gekennzeichnet, und daher funktioniert das Ausführen von pip install mit einem selbst kompilierten Interpreter oder mit einer Distro, die ihren Interpreter nicht explizit gekennzeichnet hat, wie bisher.
Zweitens legt es die Regel fest, dass bei der Installation von Paketen in den globalen Kontext eines Interpreters (entweder in einen nicht gekennzeichneten Interpreter oder bei Überschreibung der Kennzeichnung) **Python-spezifische Paketmanager nur Dateien innerhalb der Verzeichnisse des sysconfig-Schemas ändern oder löschen sollten, in denen sie Dateien erstellen würden**. Dies ermöglicht es einem Distributor eines Python-Interpreters, zwei Verzeichnisse einzurichten, eines für seine eigenen verwalteten Pakete und eines für nicht verwaltete Pakete, die vom Endbenutzer installiert werden, und sicherzustellen, dass die Installation von nicht verwalteten Paketen keine Dateien löscht (oder überschreibt), die dem externen Paketmanager gehören.
Begründung
Wie im nächsten Abschnitt detailliert beschrieben, beinhaltet die erste Verhaltensänderung die Erstellung einer Markerdatei namens EXTERNALLY-MANAGED, deren Vorhandensein anzeigt, dass Paketinstallationen außerhalb virtueller Umgebungen durch externe Mittel wie einen Distro-Paketmanager verwaltet werden. Diese Datei ist so spezifiziert, dass sie sich im stdlib-Verzeichnis im Standard- sysconfig-Schema befindet, was den Interpreter/die Installation als Ganzes markiert und nicht einen bestimmten Speicherort auf sys.path. Der Grund dafür ist, dass es, wie oben identifiziert, zwei verwandte Probleme gibt, die das Brechen eines extern verwalteten Pythons riskieren: Sie können eine inkompatible neue Version eines Pakets systemweit installieren (z. B. mit sudo pip install) und Sie können eine in Ihrem Benutzerkonto allein installieren, aber an einem Speicherort, der sich auf dem sys.path des Standard-Python-Befehls befindet (z. B. mit pip install --user). Wenn sich die Markerdatei im systemweiten Verzeichnis site-packages befände, würde sie sich nicht klar auf den zweiten Fall beziehen. Der Abschnitt Alternativen enthält weitere Diskussionen zu möglichen Speicherorten.
Die zweite Verhaltensänderung nutzt die vorhandene sysconfig-Einrichtung in Distros, die diese Art von Problem bereits aufgetreten ist, und befasst sich speziell mit dem Problem, dass ein Python-spezifischer Paketmanager Dateien löscht oder überschreibt, die einem externen Paketmanager gehören.
Anwendungsfälle
Das geänderte Verhalten in diesem PEP soll „das Richtige tun“ für so viele Anwendungsfälle wie möglich. In diesem Abschnitt betrachten wir die durch dieses PEP spezifizierten Änderungen für mehrere repräsentative Anwendungsfälle / Kontexte. Insbesondere fragen wir nach den beiden Verhaltensweisen, die durch dieses PEP geändert werden könnten
- Wird ein Python-spezifisches Installationswerkzeug wie
pip installstandardmäßig Installationen zulassen, nachdem dieses PEP implementiert wurde? - Wenn Sie ein solches Werkzeug ausführen, sollte es dann erwünscht sein, von externen (nicht Python-spezifischen) Paketmanagern für diesen Kontext ausgelieferte Pakete zu löschen, wie z. B. einen Distro-Paketmanager?
(Der Einfachheit halber diskutiert dieser Abschnitt pip als das Python-spezifische Installationswerkzeug, obwohl die Analyse gleichermaßen auf jedes andere Python-spezifische Paketmanagement-Tool anwendbar sein sollte.)
Diese Tabelle fasst die unten detailliert diskutierten Anwendungsfälle zusammen
| Fall | Description | pip install erlaubt |
Löschen von extern installierten Paketen erlaubt |
|---|---|---|---|
| 1 | Ungepatchtes CPython | Aktuell ja; bleibt ja | Aktuell ja; bleibt ja |
| 2 | Distro /usr/bin/python3 |
Aktuell ja; wird nein (vorausgesetzt, die Distro fügt eine Markerdatei hinzu) | Aktuell ja (außer unter Debian); wird nein |
| 3 | Distro Python in venv | Aktuell ja; bleibt ja | Es gibt keine extern installierten Pakete |
| 4 | Distro Python in venv mit --system-site-packages |
Aktuell ja; bleibt ja | Aktuell nein; bleibt nein |
| 5 | Distro Python in Docker | Aktuell ja; wird nein (vorausgesetzt, die Distro fügt eine Markerdatei hinzu) | Aktuell ja; wird nein |
| 6 | Conda-Umgebung | Aktuell ja; bleibt ja | Aktuell ja; bleibt ja |
| 7 | Dev-orientierte Distro | Aktuell ja; wird nein (vorausgesetzt, sie fügen eine Markerdatei hinzu) | Aktuell oft ja; wird nein (vorausgesetzt, sie konfigurieren sysconfig nach Bedarf) |
| 8 | Distro baut Pakete | Aktuell ja; kann ja bleiben | Aktuell ja; wird nein |
| 9 | PYTHONHOME kopiert aus einer Distro-Python-Stdlib |
Aktuell ja; wird nein | Aktuell ja; wird nein |
| 10 | PYTHONHOME kopiert aus Upstream-Python-Stdlib |
Aktuell ja; bleibt ja | Aktuell ja; bleibt ja |
Im Detail sind die obigen Anwendungsfälle
- Ein Standard-ungepatchtes CPython, ohne spezielle Konfiguration oder Patches für
sysconfigund ohne Markerdatei. Dieses PEP ändert sein Verhalten nicht.Ein solches CPython sollte (unabhängig von diesem PEP) nicht so installiert werden, dass es sich mit einem von der Distro installierten Python im selben System überschneidet. Zum Beispiel sollten Sie auf einem OS, das Python in
/usr/binausliefert, kein benutzerdefiniertes CPython installieren, das mit./configure --prefix=/usrgebaut wurde, da dies einige Dateien der Distro überschreiben würde und die Distro schließlich einige Dateien Ihrer Installation überschreiben würde. Stattdessen sollte Ihre Installation in einem separaten Verzeichnis erfolgen (vielleicht/usr/local,/optoder Ihrem Home-Verzeichnis).Daher können wir davon ausgehen, dass ein solches CPython über ein eigenes
stdlib-Verzeichnis und eigenesysconfig-Schemas verfügt, die sich nicht mit einem von der Distro installierten Python überschneiden. Daher sind alle von der OS installierten Pakete hier nicht sichtbar oder relevant.Wenn in diesem Fall ein Konzept von „extern installierten“ Paketen existiert, dann ist es etwas außerhalb des OS und wird im Allgemeinen von demjenigen verwaltet, der dieses CPython gebaut und installiert hat. Da der Installer sich dafür entschieden hat, keine Markerdatei hinzuzufügen oder die
sysconfig-Schemas zu ändern, wählt er das aktuelle Verhalten, undpip installkann alle in diesem CPython verfügbaren Pakete entfernen. - Ein
/usr/bin/python3einer Distro, entweder wennpip installals Root ausgeführt wird oderpip install --user, gemäß unseren Empfehlungen für Distros.Diese Empfehlungen beinhalten das Ausliefern einer Markerdatei im
stdlib-Verzeichnis, umpip installstandardmäßig zu verhindern, und das Platzieren von von der Distro ausgelieferten Paketen an einem Ort, der vom Standard-sysconfig-Schema abweicht, sodasspipals Root nicht in diesen Speicherort schreibt.Viele Distros (einschließlich Debian, Fedora und ihrer Derivate) tun bereits Letzteres.
Unter Debian und Derivaten löscht
pip installderzeit keine von der Distro installierten Pakete, da Debian einen Patch für pip hat, um dies zu verhindern. Für diese Distros ist dieses PEP also keine Verhaltensänderung; es standardisiert dieses Verhalten lediglich auf eine Weise, die nicht mehr Debian-spezifisch ist und in das Upstream-pip integriert werden kann.(Wir haben Benutzerberichte gesehen, dass extern installierte Pakete unter Debian oder einem Derivat gelöscht wurden. Wir vermuten, dass dies daran liegt, dass der Benutzer zuvor
sudo pip install --upgrade pipausgeführt hat und daher nun eine Version von/usr/bin/pipohne den Debian-Patch hat; die Standardisierung dieses Verhaltens in Upstream-Paketinstallern würde dieses Problem lösen.) - Ein Distro-Python, wenn es innerhalb einer virtuellen Umgebung verwendet wird (entweder von
venvodervirtualenv).Innerhalb einer virtuellen Umgebung gehören alle Pakete dieser Umgebung. Selbst wenn
pip,setuptoolsusw. in die Umgebung installiert werden, werden sie von Tools verwaltet, die für diese Umgebung spezifisch sind, und sollten es auch sein; sie sind nicht systemverwaltet. - Ein Distro-Python, wenn es innerhalb einer virtuellen Umgebung mit
--system-site-packagesverwendet wird. Dies ist wie der vorherige Fall, aber es lohnt sich, es ausdrücklich zu erwähnen, da alles auf dem globalensys.pathsichtbar ist.Derzeit lautet die Antwort auf die Frage „Löscht
pipextern installierte Pakete“ nein, da pip einen Sonderfall für die Ausführung in einer virtuellen Umgebung und den Versuch, Pakete außerhalb davon zu löschen, hat. Nach diesem PEP bleibt die Antwort nein, aber die Begründung wird allgemeiner: System-Site-Pakete werden außerhalb allersysconfig-Schemas liegen, die für die Paketverwaltung in der Umgebung verwendet werden. - Ein Distro-Python, wenn es in einem Single-Application-Container-Image (z. B. einem Docker-Container) verwendet wird. In diesem Anwendungsfall ist das Risiko, Systemsoftware zu beschädigen, geringer, da im Allgemeinen nur eine einzige Anwendung im Container läuft, und die Auswirkungen sind geringer, da Sie den Container neu erstellen können und nicht mit der Wiederherstellung einer laufenden Maschine kämpfen müssen. Es gibt auch eine große Anzahl bestehender Dockerfiles mit einer nicht qualifizierten
RUN pip install ...-Anweisung usw., und es wäre gut, diese nicht zu brechen. Daher möchten die Ersteller von Basis-Container-Images möglicherweise sicherstellen, dass die Markerdatei nicht vorhanden ist, selbst wenn das zugrunde liegende Betriebssystem eine standardmäßig ausliefert.Es gibt eine kleine Verhaltensänderung: Derzeit löscht
pipals Root extern installierte Pakete, aber nach diesem PEP wird es dies nicht mehr tun. Wir schlagen keine Möglichkeit vor, dies zu überschreiben. Da das Basis-Image jedoch im Allgemeinen minimal ist, sollte es keinen großen Anwendungsfall für die einfache Deinstallation von Paketen geben (insbesondere ohne die eigenen Werkzeuge der Distro zu verwenden). Der häufigste Fall ist, wenn pip ein Paket aktualisieren möchte, was zuvor die alte Version gelöscht hätte (außer unter Debian). Nach dieser Änderung ist die alte Version noch auf der Festplatte, aber pip wird extern installierte Pakete weiterhin **schatten**, und wir glauben, dass dies ausreicht, damit dies in der Praxis keine Breaking Change darstellt – eine Python-import-Anweisung führt Sie immer noch zum neu installierten Paket.Wenn es notwendig wird, eine solche Möglichkeit zu haben, schlagen wir vor, dass die Distro einen Weg dokumentiert, wie das Installationswerkzeug auf das von der Distro selbst verwendete
sysconfig-Schema zugreifen kann. Siehe den Abschnitt Empfehlungen für Distros für weitere Diskussionen.Nach Ansicht der Autoren dieses PEP ist es immer noch eine gute Idee, virtuelle Umgebungen mit von der Distro installierten Python-Interpretern zu verwenden, selbst in Single-Application-Container-Images. Auch wenn sie eine einzige *Anwendung* ausführen, kann diese Anwendung Befehle des Betriebssystems ausführen, die in Python implementiert sind, und wenn Sie die von der Distro ausgelieferten Python-Pakete mit Python-spezifischen Tools installiert oder aktualisiert haben, können diese Befehle brechen.
- Conda unterstützt ausdrücklich die Verwendung von Nicht-
conda-Tools wie pip zur Installation von Software, die nicht in den Conda-Repositorys verfügbar ist. In diesem Kontext fungiert Conda als externer Paketmanager/Distro und pip als der Python-spezifische.In gewisser Weise ist dies dem ersten Fall ähnlich, da Conda eine eigene Installation des Python-Interpreters bereitstellt.
Wir glauben nicht, dass dieses PEP Änderungen an Conda erfordert, und Versionen von pip, die die Änderungen in diesem PEP implementiert haben, werden sich weiterhin wie bisher innerhalb von Conda-Umgebungen verhalten. (Dennoch könnte es sich lohnen zu überlegen, ob separate
sysconfig-Schemas für pip-installierte und Conda-installierte Software verwendet werden sollen, aus denselben Gründen, aus denen es für andere Distros eine gute Idee ist.) - Mit einer „entwicklerorientierten Distro“ meinen wir eine spezifische Art von Distro, bei der von direkten Benutzern von Python oder anderen Sprachen in der Distro erwartet oder ermutigt wird, Änderungen an der Distro selbst vorzunehmen, wenn sie Bibliotheken hinzufügen möchten. Häufige Beispiele sind private „Monorepos“ in Softwareentwicklungsunternehmen, in denen ein einziges Repository sowohl Drittanbieter- als auch Inhouse-Software erstellt und die direkten Benutzer des Python-Interpreters der Distro im Allgemeinen Softwareentwickler sind, die besagte Inhouse-Software schreiben. Benutzerdefinierte Paketmanager wie Nixpkgs können ebenfalls dazugehören, da sie Benutzer von Nix, die Python-Entwickler sind, dazu ermutigen, ihre Software für Nix zu verpacken.
In diesen Fällen möchte die Distro möglicherweise auf einen Versuch von
pip installmit Anleitungen reagieren, die die Verwendung der eigenen Einrichtungen der Distro zum Hinzufügen neuer Pakete fördern, zusammen mit einem Link zur Dokumentation.Wenn die Distro das Erstellen einer virtuellen Umgebung aus dem Python-Interpreter der Distro unterstützt/ermutigt, kann es auch benutzerdefinierte Anweisungen geben, wie eine virtuelle Umgebung richtig eingerichtet wird (wie z. B. Nixpkgs es tut).
- Beim Erstellen von Distro-Python-Paketen für eine Distro-Python (Fall 2) kann es nützlich sein,
pip installals Teil des Paketbauprozesses der Distro verwenden zu können. (Denken Sie zum Beispiel daran, einpython-xyzRPM zu bauen, indem Siepip install .innerhalb eines sdist / Source-Tarballs fürxyzverwenden.) Die Distro möchte möglicherweise auch ein gezielteres, aber immer noch Python-spezifisches Installationswerkzeug wie installer verwenden.Für diesen Fall muss der Bauprozess einen Weg finden, die Markerdatei zu unterdrücken, um
pip installzu ermöglichen, und wird wahrscheinlich das Python-spezifische Werkzeug auf dassysconfig-Schema der Distro anstelle des ausgelieferten Standards verweisen müssen. Siehe den Abschnitt Empfehlungen für Distros für weitere Diskussionen zur Implementierung.Als Ergebnis dieses PEP kann pip bereits installierte Pakete nicht mehr entfernen. Diese Verhaltensänderung ist jedoch in Ordnung, da ein Paketbauprozess keine Anweisungen zum Löschen anderer Systemdateien enthalten sollte (und im Allgemeinen auch nicht kann); er kann nur seine eigenen Dateien verpacken.
- Ein Distro-Python, das mit
PYTHONHOMEverwendet wird, um eine alternative Python-Umgebung einzurichten (im Gegensatz zu einer virtuellen Umgebung), wobeiPYTHONHOMEauf ein Verzeichnis gesetzt wird, das direkt aus dem Distro-Python kopiert wurde (z. B.cp -a /usr/lib/python3.x pyhome/lib).Unter der Annahme, dass es keine Modifikationen gibt, verhält es sich genau wie das zugrunde liegende Distro-Python (Fall 2). Es gibt also Verhaltensänderungen – Sie können standardmäßig nicht mehr
pip install, und wenn Sie es überschreiben, werden keine extern installierten Pakete mehr gelöscht (d. h. Python-Pakete, die vom OS kopiert wurden und sich im OS-verwaltetensys.path-Eintrag befinden).Diese Verhaltensänderung scheint vertretbar zu sein, da, wenn Ihr
PYTHONHOMEeine exakte Kopie des Distro-Pythons ist, es sich wie das Distro-Python verhalten sollte. - Ein Distro-Python (oder ein beliebiger Python-Interpreter), der mit einem
PYTHONHOMEverwendet wird, das von einem kompatiblen, unveränderten Upstream-Python stammt.Da die Verhaltensänderungen in diesem PEP an Dateien in der Standardbibliothek geknüpft sind (die Markerdatei in
stdlibund das Verhalten dessysconfig-Moduls), ist das Verhalten genau wie bei einem unveränderten Upstream-CPython (Fall 1).
Spezifikation
Markieren eines Interpreters als extern verwaltet
Bevor ein Python-spezifischer Paketinstaller (das heißt, ein Tool wie pip – kein externes Tool wie apt) ein Paket in einen bestimmten Python-Kontext installiert, sollte er standardmäßig die folgenden Prüfungen durchführen
- Läuft er außerhalb einer virtuellen Umgebung? Dies kann er feststellen, indem er prüft, ob
sys.prefix == sys.base_prefixist (aber siehe Rückwärtskompatibilität). - Gibt es eine Datei
EXTERNALLY-MANAGEDin dem Verzeichnis, das vonsysconfig.get_path("stdlib", sysconfig.get_default_scheme())identifiziert wird?
Wenn beide Bedingungen erfüllt sind, sollte der Installer mit einer Fehlermeldung beendet werden, die darauf hinweist, dass die Paketinstallation in das Verzeichnis dieses Python-Interpreters außerhalb einer virtuellen Umgebung deaktiviert ist.
Der Installer sollte eine Möglichkeit für den Benutzer haben, diese Regeln zu überschreiben, z. B. durch ein Befehlszeilenflag --break-system-packages. Diese Option sollte nicht standardmäßig aktiviert sein und sollte eine gewisse Konnotation tragen, dass ihre Verwendung riskant ist.
Die Datei EXTERNALLY-MANAGED ist eine Metadatendatei im INI-Stil, die vom Standardbibliotheksmodul configparser geparst werden kann. Wenn die Datei mit configparser.ConfigParser(interpolation=None) unter Verwendung der UTF-8-Kodierung geparst werden kann und eine Sektion [externally-managed] enthält, sollte der Installer eine in der Datei angegebene Fehlermeldung suchen und diese als Teil seines Fehlers ausgeben. Wenn das erste Element des von locale.getlocale(locale.LC_MESSAGES) zurückgegebenen Tupels, d. h. der Sprachcode, nicht None ist, sollte er nach der Fehlermeldung als Wert eines Schlüssels namens Error- gefolgt vom Sprachcode suchen. Wenn dieser Schlüssel nicht existiert und der Sprachcode Unterstriche oder Bindestriche enthält, sollte er nach einem Schlüssel namens Error- gefolgt vom Teil des Sprachcodes vor dem Unterstrich oder Bindestrich suchen. Wenn keiner von beiden gefunden wird oder der Sprachcode None ist, sollte er nach einem Schlüssel namens Error suchen.
Wenn der Installer keine Fehlermeldung in der Datei findet (entweder weil die Datei nicht geparst werden kann oder weil kein geeigneter Fehlerschlüssel existiert), sollte der Installer eine vordefinierte Fehlermeldung verwenden, die den Benutzer auffordert, eine virtuelle Umgebung zum Installieren von Paketen zu erstellen.
Software-Distributoren, die einen nicht-Python-spezifischen Paketmanager haben, der Bibliotheken im sys.path ihres Python-Pakets verwaltet, sollten im Allgemeinen eine EXTERNALLY-MANAGED-Datei in ihrem Standardbibliotheksverzeichnis bereitstellen. Zum Beispiel kann Debian eine Datei in /usr/lib/python3.9/EXTERNALLY-MANAGED bereitstellen, die etwas wie Folgendes enthält:
[externally-managed]
Error=To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.
See /usr/share/doc/python3.9/README.venv for more information.
was nützliche und distrubutionsspezifische Informationen für einen Benutzer liefert, der versucht, ein Paket zu installieren. Optional können Übersetzungen in derselben Datei bereitgestellt werden.
Error-de_DE=Wenn ist das Nunstück git und Slotermeyer?
Ja! Beiherhund das Oder die Virtualenvironment gersput!
In bestimmten Kontexten, wie z. B. bei Container-Images für einzelne Anwendungen, die nach der Erstellung nicht aktualisiert werden, kann ein Distributor entscheiden, keine EXTERNALLY-MANAGED-Datei bereitzustellen, damit Benutzer alles installieren können, was sie möchten (wie heute möglich), ohne diese Regel manuell außer Kraft setzen zu müssen.
Schreiben nur in das Ziel sysconfig Schema
Normalerweise installiert ein Python-Paket-Installer in Verzeichnisse eines Schemas, das vom Standardbibliotheks-Paket sysconfig zurückgegeben wird. Normalerweise ist dies das von sysconfig.get_default_scheme() zurückgegebene Schema, aber basierend auf der Konfiguration (z. B. pip install --user) kann ein anderes Schema verwendet werden.
Immer wenn der Installer in ein sysconfig-Schema installiert, legt diese PEP fest, dass der Installer niemals Dateien außerhalb dieses Schemas ändern oder löschen darf. Wenn er beispielsweise ein Paket aktualisiert und das Paket bereits in einem Verzeichnis außerhalb dieses Schemas installiert ist (möglicherweise in einem Verzeichnis eines anderen Schemas), sollte er die vorhandenen Dateien unangetastet lassen.
Wenn der Installer während einer Aktualisierung eine bestehende Installation überschattet, empfehlen wir, dass er am Ende seines Laufs eine Warnung ausgibt.
Wenn der Installer an einen Ort außerhalb eines sysconfig-Schemas installiert (z. B. pip install --target), dann gilt dieser Unterabschnitt nicht.
Empfehlungen für Distros
Dieser Abschnitt ist nicht normativ. Er enthält Best Practices, von denen wir glauben, dass Distributoren sie befolgen sollten, es sei denn, sie haben einen spezifischen Grund, dies nicht zu tun.
Die Installation als extern verwaltet markieren
Distributoren sollten eine EXTERNALLY-MANAGED-Datei in ihrem stdlib-Verzeichnis erstellen.
Benutzer zu virtuellen Umgebungen leiten
Die Datei sollte eine nützliche und distributionsspezifische Fehlermeldung enthalten, die sowohl anzeigt, wie systemweite Pakete über den Paketmanager der Distribution installiert werden, als auch wie eine virtuelle Umgebung eingerichtet wird. Wenn Ihre Distribution häufig von Benutzern in einem Zustand verwendet wird, in dem der Befehl python3 verfügbar ist (und insbesondere, wenn pip oder get-pip verfügbar ist), python3 -m venv jedoch nicht funktioniert, sollte die Meldung klar angeben, wie python3 -m venv ordnungsgemäß funktioniert.
Erwägen Sie, pipx zu verpacken, ein Werkzeug zur Installation von Anwendungen in Python-Sprache, und es in der Fehlermeldung zu empfehlen. pipx erstellt automatisch eine virtuelle Umgebung nur für diese Anwendung, was eine viel bessere Standardeinstellung für Endbenutzer ist, die Python-Sprachsoftware (die in der Distribution nicht verfügbar ist) installieren möchten, aber selbst keine Python-Benutzer sind. Das Verpacken von pipx in der Distribution vermeidet die Ironie, Benutzer aufzufordern, pip install --user --break-system-packages pipx zu verwenden, um Systempakete *zu vermeiden* zu beschädigen. Erwägen Sie, Ihre Distribution so zu arrangieren, dass ihr Paket/Umgebung für Python für Endbenutzer (z. B. python3 unter Fedora oder python3-full unter Debian) von pipx abhängt.
Die Markerdatei in Container-Images beibehalten
Distributoren, die offizielle Images für Single-Application-Container (z. B. Docker-Container-Images) erstellen, sollten die EXTERNALLY-MANAGED-Datei beibehalten, vorzugsweise so, dass sie nicht verschwindet, wenn ein Benutzer dieses Images Paketaktualisierungen innerhalb seines Images installiert (denken Sie an RUN apt-get dist-upgrade).
Separate Distro- und lokale Verzeichnisse erstellen
Distributoren sollten zwei separate Pfade zum sys.path des Systeminterpreters legen, einen für von der Distribution installierte Pakete und einen für von lokalen Systemadministratoren installierte Pakete, und sysconfig.get_default_scheme() so konfigurieren, dass es auf den letzteren Pfad zeigt. Dies stellt sicher, dass Tools wie pip keine von der Distribution installierten Pakete ändern. Der Pfad für den lokalen Systemadministrator sollte vor dem Pfad der Distribution in sys.path stehen, damit lokale Installationen Vorrang vor Distributionspaketen haben.
Zum Beispiel implementieren Fedora und Debian (und ihre Derivate) diese Trennung, indem sie /usr/local für lokal installierte Pakete und /usr für von der Distribution installierte Pakete verwenden. Fedora verwendet /usr/local/lib/python3.x/site-packages im Vergleich zu /usr/lib/python3.x/site-packages. (Debian verwendet /usr/local/lib/python3/dist-packages im Vergleich zu /usr/lib/python3/dist-packages als zusätzliche Trennung von einem lokal kompilierten Python-Interpreter: Wenn Sie Upstream CPython in /usr/local/bin erstellen und installieren, wird /usr/local/lib/python3/site-packages betrachtet, und Debian möchte sicherstellen, dass über den lokal erstellten Interpreter installierte Pakete nicht im sys.path für den Distributionsinterpreter erscheinen.)
Beachten Sie, dass die Trennung /usr/local vs. /usr analog dazu ist, wie die Umgebungsvariable PATH typischerweise /usr/local/bin:/usr/bin enthält und Nicht-Distributionssoftware standardmäßig unter /usr/local installiert wird. Diese Trennung wird vom Filesystem Hierarchy Standard empfohlen.
Es gibt zwei Möglichkeiten, dies zu tun. Eine Möglichkeit ist, wenn Sie Python-Bibliotheken direkt bauen und verpacken (z. B. Ihre Verpackungshelfer entpacken ein mit PEP 517 erstelltes Wheel oder rufen setup.py install auf), dafür zu sorgen, dass diese Tools ein Verzeichnis verwenden, das nicht in einem sysconfig-Schema liegt, aber dennoch auf sys.path steht.
Die andere Möglichkeit ist, sicherzustellen, dass das Standard-sysconfig-Schema beim Ausführen innerhalb eines Paketbaus anders ist als bei einem installierten System. Die sysconfig-Anpassungshaken aus bpo-43976 sollten dies einfach machen (sobald sie akzeptiert und implementiert sind): Ihr Verpackungstool setzt eine Umgebungsvariable oder eine andere erkennbare Konfiguration und definiert eine Funktion get_preferred_schemes, die ein anderes Schema zurückgibt, wenn sie aus einem Paketbau aufgerufen wird. Dann können Sie pip install als Teil Ihrer Distributionsverpackung verwenden.
Wir schlagen vor, eine Option --scheme=... hinzuzufügen, um pip anzuweisen, gegen ein bestimmtes Schema zu laufen. (Siehe Implementierungsnotizen unten, wie pip Schemata aktuell ermittelt.) Sobald dies verfügbar ist, können Sie für lokale Tests und möglicherweise für die eigentliche Verpackung etwas wie pip install --scheme=posix_distro ausführen, um ein Paket explizit in den Speicherort Ihrer Distribution zu installieren (wobei get_preferred_schemes umgangen wird). Man könnte auch, wenn absolut notwendig, pip uninstall --scheme=posix_distro verwenden, um Pakete aus dem systemverwalteten Verzeichnis mit pip zu entfernen, was die (hoffentlich theoretische) Regression im Anwendungsfall 5 in Begründung behandelt.
Um Pakete mit pip zu installieren, müssten Sie entweder die EXTERNALLY-MANAGED-Markierungsdatei unterdrücken, damit pip ausgeführt werden kann, oder sie auf der Befehlszeile außer Kraft setzen. Möglicherweise möchten Sie dieselben Mittel zur Unterdrückung der Markierungsdatei in Build-Chroots wie in Container-Images verwenden.
Der Vorteil der automatischen Einrichtung dieser Dinge (Unterdrückung der Markierungsdatei in Ihrer Build-Umgebung und automatische Rückgabe Ihres Distributionsschemas durch get_preferred_schemes) ist, dass ein unmodifiziertes pip install innerhalb eines Paketbaus funktioniert, was im Allgemeinen bedeutet, dass ein unverändertes Upstream-Build-Skript, das intern pip install aufruft, das Richtige tut. Sie können natürlich auch sicherstellen, dass Ihr Verpackungsprozess immer pip install --scheme=posix_distro --break-system-packages aufruft, was ebenfalls funktionieren würde.
Der beste Ansatz hier hängt stark von den Konventionen und Mechanismen Ihrer Distribution für die Verpackung ab.
Ähnlich sollten die sysconfig-Pfade, die nicht für importierbaren Python-Code bestimmt sind – d. h. include, platinclude, scripts und data – ebenfalls zwei Varianten haben, eine für von der Distribution verpackte Software und eine für lokal installierte Software, und die Distribution sollte so eingerichtet sein, dass beide nutzbar sind. Zum Beispiel verwendet eine typische FHS-konforme Distribution /usr/local/include für das include des Standard-Schemas und /usr/include für von der Distribution verpackte Header und legt beide auf den Suchpfad des Compilers; und sie verwendet /usr/local/bin für die scripts des Standard-Schemas und /usr/bin für von der Distribution verpackte Einstiegspunkte und legt beide auf $PATH.
Abwärtskompatibilität
Alle diese Mechanismen sind für neue Distributionen und neue Versionen von Tools wie pip vorgesehen.
Insbesondere empfehlen wir dringend, dass Distributoren mit einem Konzept von Hauptversionen die Markierungsdatei oder die Änderung von sysconfig-Schemata nur in einer neuen Hauptversion hinzufügen; andernfalls besteht das Risiko, dass auf einem bestehenden System Software, die über einen Python-spezifischen Paketmanager installiert wurde, nun nicht mehr verwaltbar ist (ohne eine Überschreibungsoption). Für eine Rolling-Release-Distribution sollten, wenn möglich, die Markierungsdatei nur hinzugefügt oder sysconfig-Schemata in einer neuen Python-Nebenversion geändert werden.
Eine besondere Kompatibilitätsschwierigkeit für Paketinstallationstools dürfte die Verwaltung von Umgebungen sein, die von alten Versionen von virtualenv erstellt wurden und die neueste Version des Tools installiert haben. Eine „virtuelle Umgebung“ hat nun eine ziemlich präzise Definition: Sie verwendet den pyvenv.cfg-Mechanismus, der bewirkt, dass sys.base_prefix != sys.prefix ist. Es ist jedoch möglich, dass ein Benutzer eine alte virtuelle Umgebung hat, die von einer älteren Version von virtualenv erstellt wurde; zum Zeitpunkt des Schreibens unterstützt pip Python 3.6 und neuer, was wiederum von virtualenv 15.1.0 und neuer unterstützt wird, so dass dieses Szenario möglich ist. In älteren Versionen von virtualenv wird stattdessen ein neues Attribut, sys.real_prefix, gesetzt, und es wird nicht die Standardbibliothek zur Unterstützung virtueller Umgebungen verwendet, sodass sys.base_prefix dasselbe ist wie sys.prefix. Daher ist die Logik zur robusten Erkennung einer virtuellen Umgebung etwa so:
def is_virtual_environment():
return sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")
Sicherheitsimplikationen
Der Zweck dieser Funktion ist nicht die Implementierung einer Sicherheitsgrenze; er dient dazu, gut gemeinte Änderungen davon abzuhalten, die Umgebung eines Benutzers unerwartet zu beschädigen. Das heißt, der Grund, warum diese PEP pip install außerhalb einer virtuellen Umgebung einschränkt, ist nicht, dass es ein Sicherheitsrisiko darstellt, dies tun zu können; sondern dass „Es sollte einen – und vorzugsweise nur einen – offensichtlichen Weg geben, dies zu tun“, und dieser Weg sollte die Verwendung einer virtuellen Umgebung sein. pip install außerhalb einer virtuellen Umgebung ist für das, was fast immer der falsche Weg ist, eher zu offensichtlich.
Wenn es einen Fall gibt, in dem ein Benutzer aus *Sicherheitsgründen* nicht in der Lage sein sollte, sudo pip install oder pip install --user auszuführen und Dateien zu sys.path hinzuzufügen, dann muss dies entweder über Zugriffssteuerungsregeln für Dateien, auf die der Benutzer schreiben darf, oder über ein explizit gesichertes sys.path für das betreffende Programm implementiert werden. Keiner der Mechanismen in dieser PEP sollte als Möglichkeit interpretiert werden, ein solches Szenario zu adressieren.
Aus diesen Gründen stellt ein versuchter Installationsversuch mit einer vorhandenen Markierungsdatei keinen Sicherheitsvorfall dar, und es ist nicht notwendig, ein Audit-Ereignis dafür auszulösen. Wenn der aufrufende Benutzer berechtigterweise Zugriff auf sudo pip install oder pip install --user hat, kann er dieselbe Installation vollständig außerhalb von Python durchführen; wenn er solchen Zugriff nicht legitim hat, ist das ein Problem außerhalb des Geltungsbereichs dieser PEP.
Die Markierungsdatei selbst befindet sich im Verzeichnis der Standardbibliothek, was ein vertrauenswürdiger Speicherort ist (d. h. jeder, der in die von einem bestimmten Installer verwendete Markierungsdatei schreiben kann, könnte vermutlich beliebigen Code innerhalb des Installers ausführen). Daher ist es im Allgemeinen nicht notwendig, Terminal-Escape-Sequenzen oder andere potenziell bösartige Inhalte in der Fehlermeldung herauszufiltern.
Alternativen
Es gibt eine Reihe ähnlicher Vorschläge, die wir in Betracht gezogen haben und die diese PEP ablehnt oder verschiebt, hauptsächlich um das Verhalten in der Fall-zu-Fall-Analyse in Begründung beizubehalten.
Markerdatei
Sollte die Markierungsdatei in sys.path liegen und ein bestimmtes Verzeichnis als nicht zu beschreibend durch einen Python-spezifischen Paketmanager markieren? Dies würde beim zweiten von dieser PEP behandelten Problem (nicht Überschreiben oder Löschen von Distributions-eigenen Dateien) helfen, aber nicht beim ersten (inkompatible Installationen). Eine Verzeichnis-spezifische Markierung in /usr/lib/python3.x/site-packages würde Installationen in /usr/local/lib/python3.x/site-packages oder ~/.local/lib/python3.x/site-packages nicht entmutigen, die beide auf sys.path für /usr/bin/python3 stehen. Mit anderen Worten, die Markierungsdatei sollte nicht als Kennzeichnung eines einzelnen *Verzeichnisses* als extern verwaltet interpretiert werden (obwohl sie sich zufällig in einem Verzeichnis auf sys.path befindet); sie kennzeichnet die gesamte *Python-Installation* als extern verwaltet.
Eine weitere Variante des Obigen: Sollte die Markierungsdatei in sys.path liegen, so dass, wenn sie in einem Verzeichnis in sys.path gefunden wird, sie die Installation als extern verwaltet markiert? Ein scheinbarer Vorteil dieses Ansatzes ist, dass er sich in virtuellen Umgebungen automatisch deaktiviert. Leider hat dies das falsche Verhalten bei einer virtuellen Umgebung mit --system-site-packages, bei der der systemweite sys.path sichtbar ist, aber Paketinstallationen erlaubt sind. (Es könnte funktionieren, wenn die Regel zur Befreiung von virtuellen Umgebungen beibehalten wird, aber das scheint keinen Vorteil gegenüber dem aktuellen Schema zu haben.)
Sollte die Markierung nur ein neues Attribut eines sysconfig-Schemas sein? Dies hat eine gewisse konzeptionelle Sauberkeit, außer dass es schwer zu überschreiben ist. Wir möchten es für Container-Images, Paketbauumgebungen usw. einfach machen, die Markierungsdatei zu unterdrücken. Eine Datei, die Sie entfernen können, ist einfach; Code in sysconfig ist viel schwieriger zu ändern.
Sollte die Datei in /etc liegen? Nein, denn auch hier bezieht sie sich auf eine bestimmte Python-Installation. Ein Benutzer, der sein eigenes Python installiert, möchte möglicherweise Pakete im globalen Kontext dieses Interpreters installieren.
Sollte die Konfigurationseinstellung in pip.conf oder distutils.cfg erfolgen? Abgesehen von den oben genannten Einwänden gegen die Kennzeichnung einer Installation ist dieser Mechanismus nicht spezifisch für eines dieser Tools. (Es erscheint vernünftig, dass pip *auch* ein Konfigurationsflag für Benutzer implementiert, um sie daran zu hindern, versehentlich Nicht-Virtual-Environment-Installationen in jeder Python-Installation durchzuführen, aber das liegt außerhalb des Rahmens dieser PEP.)
Sollte die Datei TOML sein? TOML gewinnt für die Verpackung an Popularität (siehe z. B. PEP 517), hat aber noch keine Implementierung in der Standardbibliothek. Streng genommen ist dies kein Hindernis – Distributoren müssen die Datei nur schreiben, nicht lesen, also brauchen sie keine TOML-Bibliothek (die Datei wird wahrscheinlich unabhängig vom Format von Hand geschrieben), und Verpackungstools haben wahrscheinlich bereits einen TOML-Reader. Das INI-Format wird jedoch derzeit für verschiedene andere Formen von Verpackungsmetadaten verwendet (z. B. pydistutils.cfg und setup.cfg), erfüllt unsere Anforderungen, kann von der Standardbibliothek geparst werden, und die pip-Wartungspersonen sprachen sich dafür aus, TOML hierfür noch nicht zu verwenden.
Sollte die Datei im email.message-Stil sein? Obwohl dieses Format auch für Verpackungsmetadaten verwendet wird (z. B. sdist- und Wheel-Metadaten) und auch von der Standardbibliothek geparst werden kann, werden mehrzeilige Einträge nicht ganz so klar gehandhabt, und das ist unser primärer Anwendungsfall.
Sollte die Markierungsdatei ausführbarer Python-Code sein, der bewertet, ob die Installation erlaubt sein soll oder nicht? Abgesehen von den oben genannten Bedenken, die Datei in sys.path zu haben, haben wir Bedenken, dass die Ausführbarkeit zu mächtig eine API festlegt und das Verhalten schwerer verständlich machen könnte. (Beachten Sie, dass der Hook get_default_scheme von bpo-43976 tatsächlich ausführbar ist, aber dieser Code muss beim Bau des Interpreters bereitgestellt werden; er ist nicht für die Bereitstellung nach dem Bau vorgesehen.)
Wenn die Markierung überschrieben wird, sollte ein Python-spezifischer Paketmanager daran gehindert werden, ein vom externen Paketmanager installiertes Paket zu überschatten (d. h. Module mit demselben Namen zu installieren)? Dies würde das Risiko, Systemsoftware zu beschädigen, minimieren, aber es ist nicht klar, ob es die zusätzliche Komplexität der Benutzererfahrung wert ist. Es gibt legitime Anwendungsfälle für das Überschatten von Systempaketen, und eine zusätzliche Befehlszeilenoption, um dies zu erlauben, wäre verwirrender. Unterdessen würde das Nicht-Übergeben dieser Option das Risiko, Systemsoftware zu beschädigen, nicht beseitigen, die sich möglicherweise auf ein try: import xyz verlassen, das fehlschlägt, eine begrenzte Menge an Einstiegspunkten findet usw. Die Kommunikation dieser Unterscheidung scheint schwierig. Wir denken, es ist gut, wenn Python-spezifische Paketmanager eine Warnung ausgeben, wenn sie ein Paket überschatten, aber wir denken nicht, dass es sich lohnt, dies standardmäßig zu deaktivieren.
Warum wird die INSTALLER-Datei aus PEP 376 nicht verwendet, um festzustellen, wer ein Paket installiert hat und ob es entfernt werden kann? Erstens ist sie spezifisch für ein bestimmtes Paket (sie befindet sich im dist-info-Verzeichnis des Pakets), so dass sie, wie einige der oben genannten Alternativen, keine Informationen über eine gesamte Umgebung und die Zulässigkeit von Paketinstallationen liefert. PEP 627 aktualisiert auch PEP 376, um die programmatische Verwendung von INSTALLER zu verhindern, und legt fest, dass die Datei „nur zu Informationszwecken verwendet wird. [...] Unser Ziel ist die Unterstützung von Interoperabilitätstools, und jede Aktion basierend darauf, welches Tool ein Paket installiert hat, widerspricht diesem Ziel.“ Schließlich, wie PEP 627 vorsieht, gibt es legitime Anwendungsfälle dafür, dass ein Tool weiß, wie es von einem anderen Tool installierte Pakete behandeln kann; zum Beispiel kann conda ein von pip in einer Conda-Umgebung installiertes Paket sicher entfernen.
Warum gibt die Spezifikation keine Möglichkeit, Paketinstallationen innerhalb einer virtuellen Umgebung zu deaktivieren? Wir können keinen besonders starken Anwendungsfall dafür erkennen (zumindest keinen, der mit den Zielen dieser PEP zusammenhängt). Wenn Sie ihn brauchen, ist es einfach genug, pip uninstall pip innerhalb dieser Umgebung auszuführen, was zumindest unbeabsichtigte Änderungen an der Umgebung verhindern sollte (und diese Spezifikation sieht keine Vorkehrungen vor, um *absichtliche* Änderungen zu deaktivieren, da die Markierungsdatei schließlich leicht entfernt werden kann).
System-Python
Sollte die Software der Distribution einfach mit dem site-packages-Verzeichnis der Distribution allein auf sys.path laufen und das site-packages des lokalen Systemadministrators sowie das benutzer-spezifische ignorieren? Das ist eine lohnenswerte Idee, und verschiedene Versionen davon kursieren schon seit einiger Zeit unter dem Namen „System Python“ oder „Plattform Python“ (mit einem separaten „Benutzer Python“ für Endbenutzer, die Python schreiben oder Python-Software getrennt vom System installieren). Es ist jedoch eine wesentlich komplexere Änderung. Erstens wäre es eine abwärts inkompatible Änderung. Wie im Abschnitt Motivation erwähnt, gibt es gültige Anwendungsfälle für die Ausführung von von der Distribution installierten Python-Anwendungen wie Sphinx oder Ansible mit lokal installierten Python-Bibliotheken, die auf ihrem sys.path verfügbar sind. Ein vollständiger Wechsel zum Ignorieren lokaler Pakete würde diese Anwendungsfälle brechen, und eine Distribution müsste eine Fall-zu-Fall-Analyse durchführen, ob eine Anwendung lokal installierte Bibliotheken sehen soll oder nicht.
Darüber hinaus hat Fedora versucht, diese Änderung vorzunehmen, und sie wieder rückgängig gemacht, und dabei ironischerweise festgestellt, dass ihre Implementierung der Änderung ihren Paketmanager beschädigt hat. Angesichts dieser Erfahrung gibt es eindeutig Details zu klären, bevor Distributoren diesen Ansatz zuverlässig implementieren können, und eine PEP, die ihn empfiehlt, wäre verfrüht.
Diese PEP ist als vollständige und in sich geschlossene Änderung gedacht, die unabhängig von der Entscheidung eines Distributors für oder gegen „System Python“ oder ähnliche Vorschläge ist. Sie ist nicht inkompatibel mit einer Distribution, die „System Python“ in Zukunft implementiert, und obwohl beide Vorschläge dieselbe Klasse von Problemen lösen, gibt es immer noch Argumente für die Implementierung von etwas wie „System Python“, selbst nachdem diese PEP implementiert wurde. Gleichzeitig versucht diese PEP jedoch ausdrücklich, eine gezieltere und minimalere Änderung vorzunehmen, so dass sie von Distributoren implementiert werden kann, die „System Python“ nicht einführen (oder nicht sofort einführen) wollen. Die Änderungen in dieser PEP stehen für sich allein und sind kein Zwischenschritt für einen zukünftigen Vorschlag. Diese PEP reduziert (aber eliminiert nicht) das Risiko, Systemsoftware zu beschädigen, während sie gleichzeitig *nicht* brechende Änderungen minimiert (aber nicht vollständig vermeidet), was daher viel einfacher zu implementieren sein sollte als die vollständige Idee von „System Python“, die die oben genannten Nachteile mit sich bringt.
Wir gehen davon aus, dass die Anleitungen in dieser PEP – dass Benutzer nach Möglichkeit virtuelle Umgebungen verwenden sollten und dass Distributoren getrennte sys.path-Verzeichnisse für von der Distribution verwaltete und lokal verwaltete Module haben sollten – zukünftige Experimente erleichtern werden. Dazu gehören die Verteilung von völlig separaten „System“- und „Benutzer“-Python-Interpretern, die Ausführung von Systemsoftware aus einer von der Distribution besessenen virtuellen Umgebung oder PYTHONHOME (aber die Bereitstellung eines einzigen Interpreters) oder die Änderung der Einstiegspunkte für bestimmte Software (wie den Paketmanager der Distribution), um einen sys.path zu verwenden, der nur von der Distribution verwaltete Verzeichnisse sieht. Diese Ideen selbst bleiben jedoch außerhalb des Rahmens dieser PEP.
Implementierungs-Hinweise
Dieser Abschnitt ist nicht normativ und enthält Anmerkungen, die sowohl für die Spezifikation als auch für potenzielle Implementierungen relevant sind.
Derzeit bietet pip keine direkte Möglichkeit, ein Ziel-sysconfig-Schema auszuwählen, aber es gibt drei Möglichkeiten, Schemata bei der Installation zu finden
pip install- Ruft
sysconfig.get_default_scheme()auf, was normalerweise (in CPython und den meisten aktuellen Distributionen) dasselbe ist wieget_preferred_scheme('prefix'). pip install --prefix=/some/path- Ruft
sysconfig.get_preferred_scheme('prefix')auf. pip install --user- Ruft
sysconfig.get_preferred_scheme('user')auf.
Schließlich schreibt pip install --target=/some/path direkt nach /some/path, ohne nach Schemata zu suchen.
Debian trägt derzeit einen Patch zur Änderung des Standard-Installationsorts in einer virtuellen Umgebung, wobei einige Heuristiken (einschließlich der Überprüfung der Umgebungsvariable VIRTUAL_ENV) verwendet werden, hauptsächlich damit das in einer virtuellen Umgebung verwendete Verzeichnis site-packages und nicht dist-packages bleibt. Dies beeinflusst diesen Vorschlag nicht besonders, da die Implementierung dieses Patches das Standard-sysconfig-Schema nicht tatsächlich ändert und insbesondere nicht das Ergebnis von sysconfig.get_path("stdlib") ändert.
Fedora trägt derzeit einen Patch zur Änderung des Standard-Installationsorts, wenn nicht innerhalb von rpmbuild ausgeführt wird, den sie zur Implementierung des Zwei-System-weiten-Verzeichnis-Ansatzes verwenden. Dies ist konzeptionell die Art von Hook, die von bpo-43976 erwartet wird, außer dass sie als Code-Patch für distutils anstelle eines geänderten sysconfig-Schemas implementiert ist.
Die Implementierung von is_virtual_environment oben, sowie die Logik zum Laden der Datei EXTERNALLY-MANAGED und zum Finden der Fehlermeldung daraus, könnte genauso gut in die Standardbibliothek (sys bzw. sysconfig) aufgenommen werden, um ihre Implementierungen zu zentralisieren, aber sie müssen noch nicht hinzugefügt werden.
Referenzen
Für zusätzlichen Hintergrund zu diesen Problemen und früheren Lösungsversuchen siehe Debian-Bug 771794 "pip silently removes/updates system provided python packages" von 2014, den Fedora-Artikel von 2018 Making sudo pip safe über die Weiterleitung von sudo pip nach /usr/local (der anerkennt, dass die Änderungen sudo pip immer noch nicht vollständig sicher machen), pip-Probleme 5605 ("Disable upgrades to existing python modules which were not installed via pip") und 5722 ("pip should respect /usr/local") von 2018 und den Diskussionsfaden nach der PyCon US 2019 Playing nice with external package managers.
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0668.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT