PEP 382 – Namespace Packages
- Autor:
- Martin von Löwis <martin at v.loewis.de>
- Status:
- Abgelehnt
- Typ:
- Standards Track
- Erstellt:
- 02. April 2009
- Python-Version:
- 3.2
- Post-History:
Ablehnungsbescheid
Am ersten Tag der Sprints auf der US PyCon 2012 hatten wir eine lange und fruchtbare Diskussion über PEP 382 und PEP 402. Wir haben beide abgelehnt, aber eine neue PEP wird geschrieben werden, um im Geiste der PEP 402 fortzufahren. Martin von Löwis hat eine Zusammenfassung verfasst: [2].
Zusammenfassung
Namespace packages sind ein Mechanismus, um ein einzelnes Python-Paket über mehrere Verzeichnisse auf der Festplatte zu verteilen. In aktuellen Python-Versionen muss ein Algorithmus zur Berechnung des __path__ der Pakete formuliert werden. Mit der hier vorgeschlagenen Erweiterung wird die Import-Maschinerie selbst die Liste der Verzeichnisse konstruieren, aus denen das Paket besteht. Eine Implementierung dieser PEP ist verfügbar unter [1].
Terminologie
Innerhalb dieser PEP bezieht sich der Begriff Paket auf Python-Pakete, wie sie durch die Python-Importanweisung definiert sind. Der Begriff Distribution bezieht sich auf separat installierbare Sätze von Python-Modulen, wie sie im Python Package Index gespeichert und von distutils oder setuptools installiert werden. Der Begriff Vendor-Paket bezieht sich auf Gruppen von Dateien, die von einem Paketierungsmechanismus des Betriebssystems installiert werden (z. B. Debian- oder Redhat-Pakete, die auf Linux-Systemen installiert werden).
Der Begriff Portion bezieht sich auf eine Sammlung von Dateien in einem einzelnen Verzeichnis (möglicherweise in einer Zip-Datei gespeichert), die zu einem Namespace-Paket beitragen.
Namespace packages today
Python stellt derzeit pkgutil.extend_path zur Verfügung, um ein Paket als Namespace-Paket zu kennzeichnen. Die empfohlene Verwendung ist die Platzierung von
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
in der __init__.py des Pakets. Jede Distribution muss den gleichen Inhalt in ihrer __init__.py bereitstellen, damit extend_path unabhängig davon aufgerufen wird, welche Portion des Pakets zuerst importiert wird. Infolgedessen kann die __init__.py des Pakets praktisch keine Namen definieren, da sie von der Reihenfolge der Paketfragmente in sys.path abhängt, je nachdem, welche Portion zuerst importiert wird. Als Besonderheit liest extend_path Dateien namens <packagename>.pkg, die zusätzliche Portionen deklarieren.
setuptools bietet eine ähnliche Funktion pkg_resources.declare_namespace, die in der Form verwendet wird
import pkg_resources
pkg_resources.declare_namespace(__name__)
In der __init__.py der Portion ist keine Zuweisung zu __path__ erforderlich, da declare_namespace das __path__ des Pakets über sys.modules modifiziert. Als Besonderheit unterstützt declare_namespace auch Zip-Dateien und registriert den Paketnamen intern, sodass zukünftige Ergänzungen zu sys.path durch setuptools zusätzliche Portionen zu jedem Paket ordnungsgemäß hinzufügen können.
setuptools erlaubt die Deklaration von Namespace-Paketen in der setup.py einer Distribution, so dass Distributionsentwickler die magische __path__-Modifikation nicht selbst in die __init__.py einfügen müssen.
Begründung
Der aktuelle imperative Ansatz für Namespace-Pakete hat zu mehreren leicht inkompatiblen Mechanismen für die Bereitstellung von Namespace-Paketen geführt. Zum Beispiel unterstützt pkgutil *.pkg-Dateien; setuptools nicht. Ebenso unterstützt setuptools das Inspizieren von Zip-Dateien und das Hinzufügen von Portionen zur Variable _namespace_packages, während pkgutil dies nicht tut.
Zusätzlich verursacht der aktuelle Ansatz Probleme für Systemanbieter. Vendor-Pakete dürfen normalerweise keine überlappenden Dateien bereitstellen, und der Versuch, ein Vendor-Paket zu installieren, das eine bereits vorhandene Datei enthält, schlägt fehl oder führt zu unvorhersehbarem Verhalten. Da Anbieter Distributionspakete so wählen könnten, dass sie alle in einem einzigen Verzeichnis für das Namespace-Paket landen, würden alle Portionen widersprüchliche __init__.py-Dateien enthalten.
Spezifikation
Anstelle eines imperativen Mechanismus für den Import von Paketen wird hier ein deklarativer Ansatz vorgeschlagen: Ein Verzeichnis, dessen Name mit .pyp (für Python package) endet, enthält eine Portion eines Pakets.
Die Importanweisung wird erweitert, so dass sie das __path__-Attribut eines Pakets namens P berechnet, das optional aus einem einzelnen Verzeichnisnamen P mit einer Datei __init__.py besteht, zuzüglich aller Verzeichnisse namens P.pyp, in der Reihenfolge, in der sie im __path__ des übergeordneten Pakets (oder in sys.path) gefunden werden. Wenn eines davon gefunden wird, wird die Suche nach zusätzlichen Portionen des Pakets fortgesetzt.
Ein Verzeichnis kann sowohl ein Paket im Format P/__init__.py als auch im Format P.pyp enthalten.
Keine weitere Änderung am Importmechanismus wird vorgenommen; die Suche nach Modulen (einschließlich __init__.py) wird weiterhin beim ersten gefundenen Modul gestoppt. Zusammenfassend funktioniert der Prozess des Imports eines Pakets foo wie folgt:
- sys.path wird nach Verzeichnissen foo oder foo.pyp oder einer Datei foo.<ext> durchsucht. Wenn eine Datei gefunden wird und kein Verzeichnis, wird sie als Modul behandelt und importiert.
- Wenn ein Verzeichnis foo gefunden wird, wird geprüft, ob es __init__.py enthält. Wenn ja, wird der Speicherort von __init__.py gemerkt. Andernfalls wird das Verzeichnis übersprungen. Sobald eine __init__.py gefunden wurde, werden weitere Verzeichnisse namens foo übersprungen.
- Für beide Verzeichnisse, foo und foo.pyp, werden die Verzeichnisse zum __path__ des Pakets hinzugefügt.
- Wenn ein __init__-Modul gefunden wurde, wird es importiert, wobei __path__ auf den berechneten Pfad aller
.pyp-Verzeichnisse initialisiert wird.
Auswirkungen auf Import Hooks
Sowohl Loader als auch Finder, wie in PEP 302 definiert, müssen geändert werden, um Namespace-Pakete zu unterstützen. Wenn das untenstehende Protokoll nicht eingehalten wird, wird ein Paket möglicherweise nicht als Namespace-Paket erkannt; Loader und Finder, die dieses Protokoll nicht unterstützen, müssen AttributeError auslösen, wenn auf die untenstehenden Funktionen zugegriffen wird.
Finder müssen in Schritt 1 des obigen Algorithmus nach *.pth-Dateien suchen können. Dazu muss ein Finder, der als Pfad-Hook verwendet wird, eine Methode unterstützen:
finder.find_package_portion(fullname)
Diese Methode wird auf dieselbe Weise wie find_module aufgerufen und muss eine Zeichenkette zurückgeben, die zum __path__ des Pakets hinzugefügt werden soll. Wenn der Finder keine Portion des Pakets findet, soll er None zurückgeben. Das Auslösen von AttributeError aus dem obigen Aufruf wird als Nichteinhaltung dieser PEP behandelt, und die Ausnahme wird ignoriert. Alle anderen Ausnahmen werden gemeldet.
Ein Finder kann sowohl Erfolg von find_module als auch von find_package_portion melden, was sowohl ein Paket mit einer __init__.py als auch eine Portion desselben Pakets ermöglicht.
Alle von find_package_portion zurückgegebenen Zeichenketten werden zusammen mit allen Pfadnamen von .pyp-Verzeichnissen zum neuen __path__ des Pakets hinzugefügt.
Diskussion
Ursprüngliche Versionen dieser Spezifikation schlugen die Hinzufügung von *.pth-Dateien vor, ähnlich wie diese Dateien auf sys.path verwendet werden. Mit einem Wildcard-Marker (*) könnte ein Paket angeben, dass der gesamte Pfad durch die Suche auf dem übergeordneten Pfad und die Suche nach richtig benannten Unterverzeichnissen abgeleitet wird.
Leute beobachteten dann, dass die Unterstützung für die vollständige .pth-Syntax unangemessen ist, und die .pth-Dateien wurden geändert, um bloße Markierungsdateien zu sein, die angeben, dass ein Verzeichnis ein Paket ist. Peter Tröger schlug vor, dass .pth eine ungeeignete Dateierweiterung sei, da alle dateierweiterungen im Zusammenhang mit Python mit .py beginnen sollten. Daher wurde die Markierungsdatei in .pyp umbenannt.
Dinu Gherman beobachtete dann, dass die Verwendung einer Markierungsdatei nicht notwendig sei und dass eine Verzeichniserweiterung gut als solche dienen könne. Dies schlägt diese PEP derzeit vor.
Phillip Eby entwarf PEP 402 als alternativen Ansatz zu dieser PEP, nachdem er die Python-Paketsyntax mit der in anderen Sprachen gefundenen verglichen hatte. PEP 402 schlägt vor, überhaupt keine Markierungsdatei zu verwenden. Auf der Diskussion auf der PyCon DE 2011 bemerkten die Leute, dass eine explizite Deklaration eines Verzeichnisses als Beitrag zu einem Paket eine wünschenswerte Eigenschaft sei, anstatt ein Hindernis. Insbesondere Jython-Entwickler bemerkten, dass Jython ein Verzeichnis, das ein Java-Paket ist, leicht als Python-Paket verwechseln könnte, wenn es keine Notwendigkeit gäbe, Python-Pakete zu deklarieren.
Pakete können aufhören, die __init__.py des Namespace-Pakets auszufüllen. Infolgedessen werden extend_path und declare_namespace obsolet.
Namespace-Pakete können beginnen, nicht-triviale __init__.py-Implementierungen bereitzustellen; um dies zu tun, wird empfohlen, dass eine einzelne Distribution eine Portion mit nur der __init__.py des Namespace-Pakets (und möglicherweise anderen Modulen, die zum Namespace-Paket selbst gehören) bereitstellt.
Der Mechanismus ist weitgehend kompatibel mit den bestehenden Namespace-Mechanismen. extend_path wird an diese Spezifikation angepasst; jeder andere Mechanismus könnte dazu führen, dass Portionen zweimal zu __path__ hinzugefügt werden.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Source: https://github.com/python/peps/blob/main/peps/pep-0382.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT