PEP 582 – Python lokales Paketverzeichnis
- Autor:
- Kushal Das <mail at kushaldas.in>, Steve Dower <steve.dower at python.org>, Donald Stufft <donald at stufft.io>, Alyssa Coghlan <ncoghlan at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Abgelehnt
- Typ:
- Standards Track
- Thema:
- Packaging
- Erstellt:
- 16-May-2018
- Python-Version:
- 3.12
- Post-History:
- 01-Mar-2019
- Resolution:
- Discourse-Nachricht
Inhaltsverzeichnis
Zusammenfassung
Dieses PEP schlägt vor, den bestehenden Mechanismus zur Einrichtung von sys.path zu erweitern, um zusätzlich zu den bestehenden Speicherorten ein neues Verzeichnis __pypackages__ aufzunehmen. Das neue Verzeichnis wird am Anfang von sys.path hinzugefügt, nach dem aktuellen Arbeitsverzeichnis und knapp vor den System-Site-Packages, um dort installierten Paketen Vorrang vor anderen Speicherorten zu geben.
Dies ähnelt dem bestehenden Mechanismus zum Hinzufügen des aktuellen Verzeichnisses (oder des Verzeichnisses, in dem sich das Skript befindet), aber durch die Verwendung eines Unterverzeichnisses bleiben zusätzliche Bibliotheken von der Arbeit des Benutzers getrennt.
Motivation
Neue Python-Programmierer können davon profitieren, den Wert der Isolierung der Abhängigkeiten eines einzelnen Projekts von ihrer Systemumgebung zu vermitteln. Der bestehende Mechanismus hierfür, virtuelle Umgebungen, ist jedoch dafür bekannt, für Anfänger komplex und fehleranfällig zu sein. Die Erklärung virtueller Umgebungen ist oft eine Ablenkung, wenn man versucht, eine Gruppe von Anfängern einzurichten – Unterschiede in der Plattform- und Shell-Umgebung erfordern individuelle Unterstützung, und die Notwendigkeit der Aktivierung in jeder neuen Shell-Sitzung macht es für Studenten leicht, Fehler zu machen, wenn sie nach einer Pause zur Arbeit zurückkehren. Dieser Vorschlag bietet eine leichtgewichtige Lösung, die Isolation bietet, ohne dass der Benutzer fortgeschrittenere Konzepte verstehen muss.
Darüber hinaus benötigen eigenständige Python-Anwendungen normalerweise 3rd-Party-Bibliotheken, um zu funktionieren. Typischerweise werden sie entweder so konzipiert, dass sie aus einer virtuellen Umgebung ausgeführt werden, in der die Abhängigkeiten zusammen mit der Anwendung in der Umgebung installiert werden, oder sie bündeln ihre Abhängigkeiten in einem Unterverzeichnis und modifizieren sys.path beim Anwendungsstart. Virtuelle Umgebungen sind zwar eine gängige und effektive Lösung (z. B. vom Tool pipx verwendet), aber etwas umständlich einzurichten und zu verwalten und nicht verschiebbar. Auf der anderen Seite ist die manuelle Manipulation von sys.path Boilerplate, die Entwickler richtig machen müssen, und (als Laufzeitverhalten) wird sie von Werkzeugen wie Linter und Typ-Checkern nicht verstanden. Der Vorschlag __pypackages__ formalisiert die Idee eines Speicherorts für "gebündelte Abhängigkeiten", vermeidet den Boilerplate-Code und bietet einen Standard-Speicherort, den Entwicklungswerkzeuge erkennen können.
Es ist zu beachten, dass Python-Bibliotheken im Allgemeinen nicht einfach zwischen Maschinen, Plattformen oder sogar unbedingt zwischen Python-Versionen kopiert werden können. Dieser Vorschlag ändert nichts an dieser Tatsache, und obwohl es verlockend ist anzunehmen, dass das Bündeln eines Skripts und seiner __pypackages__ ein Mechanismus für die Verteilung von Anwendungen ist, ist dies ausdrücklich *nicht* das Ziel dieses Vorschlags. Entwickler bleiben für die Portabilität ihres Codes verantwortlich.
Begründung
Während sys.path zur Laufzeit manipuliert werden kann, ist der Standardwert wichtig, da er eine gemeinsame Basis festlegt, auf die sich Benutzer und Werkzeuge einigen können. Der aktuelle Standard enthält keinen Speicherort, der als "privat für das aktuelle Projekt" angesehen werden könnte, und doch ist dies ein nützliches Konzept.
Dies ähnelt dem npm-Verzeichnis node_modules, das in der JavaScript-Community beliebt ist und von Entwicklern, die mit diesem Ökosystem vertraut sind, oft von Python verlangt wird.
Spezifikation
Dieses PEP schlägt vor, einen neuen Schritt im Prozess der Berechnung von sys.path beim Start hinzuzufügen.
Wenn der interaktive Interpreter startet und ein Verzeichnis __pypackages__ im aktuellen Arbeitsverzeichnis gefunden wird, dann wird es nach dem Eintrag für das aktuelle Arbeitsverzeichnis und knapp vor den System-Site-Packages in sys.path aufgenommen.
Wenn der Interpreter ein Skript ausführt, versucht Python, __pypackages__ im selben Verzeichnis wie das Skript zu finden. Wenn es gefunden wird (zusammen mit dem Verzeichnis der aktuellen Python-Version darin), dann wird es verwendet, andernfalls verhält sich Python wie bisher.
Das Verhalten sollte genau so funktionieren, wie der bestehende Mechanismus zum Hinzufügen des aktuellen Arbeitsverzeichnisses oder Skriptverzeichnisses zu sys.path funktioniert. Zum Beispiel wird __pypackages__ ignoriert, wenn die Option -P oder die Umgebungsvariable PYTHONSAFEPATH gesetzt ist.
Um erkannt zu werden, muss das Verzeichnis __pypackages__ gemäß einem neuen localpackages-Schema im `sysconfig`-Modul aufgebaut sein. Insbesondere müssen sowohl die purelib als auch die platlib-Verzeichnisse vorhanden sein, wobei der folgende Code verwendet wird, um die Speicherorte dieser Verzeichnisse zu bestimmen.
scheme = "localpackages"
purelib = sysconfig.get_path("purelib", scheme, vars={"base": "__pypackages__", "platbase": "__pypackages__"})
platlib = sysconfig.get_path("platlib", scheme, vars={"base": "__pypackages__", "platbase": "__pypackages__"})
Diese beiden Speicherorte werden zu sys.path hinzugefügt; andere Verzeichnisse oder Dateien im Verzeichnis __pypackages__ werden stillschweigend ignoriert. Die Pfade basieren auf Python-Versionen.
Hinweis
Es gibt eine mögliche Option, eine separate neue API zu haben; sie ist unter Issue #3013 dokumentiert.
Beispiel
Das Folgende zeigt eine beispielhafte Projektverzeichnisstruktur und verschiedene Verhaltensweisen des Python-Executables und jedes Skripts. Das Beispiel ist für Unix-ähnliche Systeme – unter Windows sind die Unterverzeichnisse anders.
foo
__pypackages__
lib
python3.10
site-packages
bottle
myscript.py
/> python foo/myscript.py
sys.path[0] == 'foo'
sys.path[1] == 'foo/__pypackages__/lib/python3.10/site-packages/'
cd foo
foo> /usr/bin/ansible
#! /usr/bin/env python3
foo> python /usr/bin/ansible
foo> python myscript.py
foo> python
sys.path[0] == '.'
sys.path[1] == './__pypackages__/lib/python3.10/site-packages'
foo> python -m bottle
Wir haben ein Projektverzeichnis namens foo und darin ein __pypackages__. Wir haben bottle in __pypackages__/lib/python3.10/site-packages/ installiert und eine Datei myscript.py im Projektverzeichnis. Wir haben das Tool verwendet, das wir normalerweise verwenden, um bottle an diesem Ort zu installieren.
Beim Aufrufen eines Skripts versucht Python, __pypackages__ im Verzeichnis zu finden, in dem sich das Skript befindet [1], und nicht im Verzeichnis /usr/bin. Das Gleiche geschieht im letzten Beispiel, wo wir /usr/bin/ansible aus dem Verzeichnis foo heraus ausführen. In beiden Fällen wird das __pypackages__ im aktuellen Arbeitsverzeichnis *nicht* verwendet.
Ebenso, wenn wir myscript.py aus dem ersten Beispiel aufrufen, wird das Verzeichnis __pypackages__ aus dem Verzeichnis foo verwendet.
Wenn wir in das Verzeichnis foo wechseln und den Python-Interpreter starten, findet er das Verzeichnis __pypackages__ im aktuellen Arbeitsverzeichnis und verwendet es in sys.path. Das Gleiche passiert, wenn wir versuchen, -m zu verwenden und ein Modul zu nutzen. In unserem Beispiel wird das Modul bottle im Verzeichnis __pypackages__ gefunden.
Die beiden obigen Beispiele sind die einzigen Fälle, in denen __pypackages__ aus dem aktuellen Arbeitsverzeichnis verwendet wird.
In einem anderen Beispiel-Szenario könnte ein Trainer einer Python-Klasse sagen: „Heute lernen wir, wie man Twisted benutzt! Zuerst checken Sie unser Beispielprojekt aus, wechseln Sie in dieses Verzeichnis und führen Sie dann einen gegebenen Befehl aus, um Twisted zu installieren.“
Dadurch wird Twisted in ein Verzeichnis installiert, das von python3 getrennt ist. Es gibt keinen Bedarf, virtuelle Umgebungen, globale oder Benutzerinstallationen usw. zu diskutieren, da die Installation standardmäßig lokal erfolgt. Der Trainer kann dann einfach weiterhin sagen, dass sie python3 ohne Aktivierungsschritt usw. verwenden sollen.
Beziehung zu virtuellen Umgebungen
Im Kern ist dieser Vorschlag einfach eine Modifikation der Berechnung des Standardwerts von sys.path und hat keinerlei Bezug zum Mechanismus virtueller Umgebungen. __pypackages__ kann jedoch als Bereitstellung einer Isolationsfähigkeit angesehen werden, und in diesem Sinne "konkurriert" es mit virtuellen Umgebungen.
Es gibt jedoch signifikante Unterschiede.
- Virtuelle Umgebungen sind vom Systemumfeld isoliert, während
__pypackages__einfach zum Systemumfeld hinzugefügt wird.- Virtuelle Umgebungen enthalten ein vollständiges "Installationsschema" mit Verzeichnissen für Binärdateien, C-Header-Dateien usw., während
__pypackages__ausschließlich für Python-Bibliotheks-Code bestimmt ist.- Virtuelle Umgebungen funktionieren am besten, wenn sie "aktiviert" werden. Dieser Vorschlag benötigt keine Aktivierung.
Dieser Vorschlag sollte als unabhängig von virtuellen Umgebungen betrachtet werden und nicht mit ihnen konkurrieren. Im besten Fall können einige Anwendungsfälle, die derzeit nur von virtuellen Umgebungen bedient werden, auch (möglicherweise besser) von __pypackages__ bedient werden.
Es ist zu beachten, dass in __pypackages__ installierte Bibliotheken in einer virtuellen Umgebung sichtbar sein werden. Dies bricht die Isolation virtueller Umgebungen zwar argumentativ, unterscheidet sich aber prinzipiell nicht von der Anwesenheit des aktuellen Verzeichnisses in sys.path (oder Mechanismen wie die Umgebungsvariable PYTHONPATH). Der einzige Unterschied liegt im Ausmaß, da erwartet wird, dass Leute häufiger Pakete in __pypackages__ installieren. Die Alternative wäre, virtuelle Umgebungen explizit zu erkennen und __pypackages__ in diesem Fall zu deaktivieren – dies würde jedoch Skripte mit gebündelten Abhängigkeiten brechen. Die PEP-Autoren glauben, dass Entwickler, die virtuelle Umgebungen nutzen, erfahren genug sein sollten, um das Problem zu verstehen und Probleme zu antizipieren und zu vermeiden.
Sicherheitsüberlegungen
Theoretisch ist es möglich, eine Bibliothek zum Verzeichnis __pypackages__ hinzuzufügen, die ein stdlib-Modul oder eine installierte 3rd-Party-Bibliothek überschreibt. Für das __pypackages__, das mit einem Skript verbunden ist, wird dies als kein signifikantes Problem angesehen, da es unwahrscheinlich ist, dass jemand in __pypackages__ schreiben kann, es sei denn, er hätte auch die Fähigkeit, in das Skript selbst zu schreiben.
Für ein Verzeichnis __pypackages__ im aktuellen Arbeitsverzeichnis könnte der interaktive Interpreter betroffen sein. Dies ist jedoch nicht wesentlich anders als das bestehende Problem, dass jemand ein Modul math.py in seinem aktuellen Verzeichnis hat, und obwohl es (genau wie in diesem Fall) zu Benutzerverwirrung führen kann, führt es keine neuen Sicherheitsimplikationen ein.
Beim Ausführen eines Skripts wird jedes Verzeichnis __pypackages__ im aktuellen Arbeitsverzeichnis ignoriert. Dies ist der gleiche Ansatz, den Python verwendet, um das aktuelle Arbeitsverzeichnis zu sys.path hinzuzufügen, und stellt sicher, dass das Verhalten eines Skripts nicht durch das Ändern von Dateien im aktuellen Verzeichnis geändert werden kann.
Außerdem wird ein Verzeichnis __pypackages__ nur im aktuellen (oder Skript-)Verzeichnis erkannt. Der Interpreter scannt *nicht* nach __pypackages__ in übergeordneten Verzeichnissen. Dies würde das Risiko von Sicherheitsproblemen eröffnen, wenn Verzeichnisberechtigungen auf übergeordneten Ebenen unterschiedlich sind. Insbesondere Skripte im Verzeichnis bin oder __pypackages__ (der scripts-Speicherort in sysconfig-Begriffen) haben keinen besonderen Zugriff auf die in __pypackages__ installierten Bibliotheken. Das Platzieren ausführbarer Skripte in einem Verzeichnis bin wird von diesem Vorschlag nicht unterstützt.
Wie man das lehrt
Die ursprüngliche Motivation für diesen Vorschlag war, das Unterrichten von Python für Anfänger zu erleichtern. Zu diesem Zweck muss es einfach zu erklären und einfach zu verwenden sein.
Auf der grundlegendsten Ebene ähnelt dies dem bestehenden Mechanismus, bei dem das Skriptverzeichnis zu sys.path hinzugefügt wird und auf ähnliche Weise gelehrt werden kann. Für seinen beabsichtigten Verwendungszweck der "leichtgewichtigen Isolation" würde es jedoch wahrscheinlich im Sinne von "Dinge, die Sie in ein Verzeichnis __pypackages__ legen, sind privat für Ihr Skript" gelehrt werden. Die Erfahrung der PEP-Autoren legt nahe, dass dies signifikant einfacher zu lehren wäre als die aktuelle Alternative der Einführung virtueller Umgebungen.
Auswirkungen auf Werkzeuge
Da der beabsichtigte Verwendungszweck der Funktion die Installation von 3rd-Party-Bibliotheken im neuen Verzeichnis ist, ist es wichtig, dass Werkzeuge, insbesondere Installer, verstehen, wie __pypackages__ verwaltet wird.
Es wird gehofft, dass Werkzeuge einen dedizierten "pypackages"-Installationsmodus einführen, der in allen Fällen garantiert dem erwarteten Layout entspricht. Die Frage, wie das __pypackages__-Layout am besten unterstützt wird, liegt jedoch letztendlich bei den einzelnen Werkzeughaltern, die es berücksichtigen und entscheiden.
Werkzeuge, die Pakete lokalisieren, ohne tatsächlich Python-Code auszuführen (IDEs, Linter, Typ-Checker usw.), müssten aktualisiert werden, um __pypackages__ zu erkennen. Ohne solche Updates würde das Verzeichnis __pypackages__ ähnlich wie Verzeichnisse funktionieren, die derzeit zur Laufzeit zu sys.path hinzugefügt werden (d. h. das Werkzeug würde es wahrscheinlich ignorieren).
Abwärtskompatibilität
Der Verzeichnisname __pypackages__ wurde gewählt, da er unwahrscheinlich ist, in allgemeinem Gebrauch zu sein. Es stimmt, dass Benutzer, die diesen Namen für ihre eigenen Zwecke gewählt haben, betroffen sein werden, aber zum Zeitpunkt der Erstellung dieses PEP wurde dies als relativ geringes Risiko angesehen.
Leider haben im Laufe der Diskussion dieses PEP eine Reihe von Werkzeugen Variationen dessen implementiert, was hier vorgeschlagen wird, die nicht alle mit der endgültigen Form des PEP kompatibel sind. Infolgedessen ist das Risiko von Konflikten nun höher als ursprünglich erwartet.
Es wäre möglich, dies zu mildern, indem ein *anderer* Name gewählt wird, hoffentlich so unüblich, wie __pypackages__ ursprünglich war. Aber realistischerweise können Kompatibilitätsprobleme einfach als Folgen davon angesehen werden, dass Leute versuchen, Entwurfsentwürfe zu implementieren, ohne sich die Mühe zu machen, Änderungen am Entwurf zu verfolgen. Daher scheint es vernünftig, den Namen __pypackages__ beizubehalten und die Last der Bewältigung des Kompatibilitätsproblems auf die Werkzeuge zu legen, die die Entwurfsversion implementiert haben.
Auswirkungen auf andere Python-Implementierungen
Andere Python-Implementierungen müssen das neue Verhalten des Interpreter-Bootstraps nachbilden, einschließlich des Auffindens des Verzeichnisses __pypackages__ und dessen Hinzufügens zu sys.path knapp vor den Site-Paketen, falls es vorhanden ist. Dies unterscheidet sich nicht von jeder anderen Python-Änderung.
Referenzimplementierung
Hier ist ein kleines Skript, das die Implementierung für Cpython & in PyPy ermöglichen wird.
Abgelehnte Ideen
- Alternative Namen wie
__pylocal__undpython_modules. Letztendlich ist der Name willkürlich und der gewählte Name ist gut genug. - Zusätzliche Funktionen virtueller Umgebungen. Dieser Vorschlag ist kein Ersatz für virtuelle Umgebungen, und solche Funktionen liegen daher außerhalb des Geltungsbereichs.
- Wir werden kein übergeordnetes Verzeichnis scannen, um
__pypackages__zu finden. Wenn wir Skripte im Verzeichnis~/bin/ausführen wollen, dann muss das Verzeichnis__pypackages__im Verzeichnis~/bin/liegen. Jedes solche Scannen von__pypackages__(für den Interpreter oder ein Skript) hat Sicherheitsimplikationen und erhöht auch die Startzeit. - Einen Fehler auslösen, wenn unerwartete Dateien oder Verzeichnisse in
__pypackages__vorhanden sind. Dies wird als zu streng angesehen, insbesondere da Übergangsansätze wiepip install --prefixzusätzliche Dateien in__pypackages__erstellen können. - Verwendung eines anderen
sysconfig-Schemas oder eines dediziertenpypackages-Schemas. Obwohl dies theoretisch attraktiv ist, erschwert es den Übergang, da es keine leicht verfügbare Möglichkeit geben wird, in__pypackages__zu installieren, bis Werkzeuge explizite Unterstützung implementieren. Und obwohl die PEP-Autoren hoffen und davon ausgehen, dass eine solche Unterstützung hinzugefügt würde, erscheint es als ein inakzeptables Risiko, den Vorschlag von einer solchen Unterstützung abhängig zu machen, um nutzbar zu sein.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0582.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT