PEP 771 – Standardmäßige Extras für Python-Softwarepakete
- Autor:
- Thomas Robitaille <thomas.robitaille at gmail.com>, Jonathan Dekhtiar <jonathan at dekhtiar.com>
- Sponsor:
- Pradyun Gedam <pradyunsg at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Entwurf
- Typ:
- Standards Track
- Thema:
- Packaging
- Erstellt:
- 13. Jan. 2025
- Post-History:
- 15. Jan. 2025, 06. Feb. 2025, 09. Jun. 2025
Inhaltsverzeichnis
- Zusammenfassung
- Motivation
- Begründung
- Spezifikation
- Abwärtskompatibilität
- Sicherheitsimplikationen
- Wie man dies lehrt
- Referenzimplementierung
- Abgelehnte Ideen
- Urheberrecht
Zusammenfassung
PEP 508 definiert eine Minisprache zur Deklaration von Paketabhängigkeiten. Eine Funktion dieser Sprache ist die Möglichkeit, Extras zu spezifizieren, bei denen es sich um optionale Komponenten einer Distribution handelt, die bei Verwendung zusätzliche Abhängigkeiten installieren. Dieses PEP schlägt einen Mechanismus vor, der es ermöglicht, ein oder mehrere Extras standardmäßig zu installieren, wenn keine explizit angegeben werden.
Motivation
Verschiedene Anwendungsfälle für Standard-Extras und mögliche Lösungen in diesem PEP wurden ausführlich in diesem DPO-Thread diskutiert. Diese fallen in zwei breite Fälle, die die Motivation für das vorliegende PEP liefern.
Empfohlene, aber nicht erforderliche Abhängigkeiten
Paketbetreuer verwenden oft Extras, um optionale Abhängigkeiten zu deklarieren, die die Funktionalität oder Leistung eines Pakets erweitern. In einigen Fällen kann es schwierig sein zu bestimmen, welche Abhängigkeiten erforderlich sein sollten und welche als Extras kategorisiert werden sollten. Es muss ein Gleichgewicht zwischen den Bedürfnissen typischer Benutzer, die wahrscheinlich die meisten Funktionen standardmäßig verfügbar haben möchten, und Benutzern, die minimale Installationen ohne große, optionale Abhängigkeiten wünschen, gefunden werden. Eine Lösung mit der vorhandenen Python-Packaging-Infrastruktur besteht darin, dass Paketbetreuer ein Extra definieren, das beispielsweise recommended genannt wird und alle nicht-essenziellen, aber vorgeschlagenen Abhängigkeiten enthält. Benutzer werden dann angewiesen, das Paket mit package[recommended] zu installieren, während diejenigen, die mehr Kontrolle wünschen, package verwenden können. In der Praxis sind sich jedoch viele Benutzer der [recommended]-Syntax nicht bewusst, was die Last auf sie legt, dies für eine typische Installation zu wissen. Eine Möglichkeit, empfohlene Abhängigkeiten standardmäßig installieren zu lassen und gleichzeitig eine Möglichkeit für Benutzer zu bieten, eine minimalistischere Installation anzufordern, würde diesen Anwendungsfall erfüllen, und dieses PEP beschreibt eine Lösung dafür.
Beispiele für Pakete, die dieses Muster demonstrieren, indem sie Benutzer ermutigen, zusätzliche Abhängigkeiten standardmäßig einzubeziehen, sind:
Pakete, die mehrere Backends oder Frontends unterstützen
Ein weiterer häufiger Anwendungsfall für die Verwendung von Extras ist die Definition verschiedener Backends oder Frontends und der Abhängigkeiten, die für jedes Backend oder Frontend installiert werden müssen. Ein Paket benötigt möglicherweise mindestens ein Backend oder Frontend, um funktionsfähig zu sein, ist aber möglicherweise flexibel, welches Backend oder Frontend dies ist. Konkrete Beispiele für solche Frontends oder Backends sind:
- Die Qt-Frontendbibliothek, die von PyQt5, PyQt6, PySide2 oder PySide6 bereitgestellt werden kann
- Backend-Datenbanken, wie z.B. MySQL, PostgreSQL, SQLite
Mit den aktuellen Packaging-Standards müssen Betreuer entweder eines der Backends oder Frontends anfordern oder Benutzer auffordern, immer Extras anzugeben, z.B. package[backend], und riskieren daher, dass Benutzer eine unbrauchbare Installation erhalten, wenn sie nur package installieren. Eine Möglichkeit, ein oder mehrere Standard-Backends oder -Frontends zu spezifizieren und eine Möglichkeit zur Überschreibung dieser Standardwerte bereitzustellen, würde eine deutlich bessere Benutzererfahrung bieten, und der in diesem PEP beschriebene Ansatz wird dies ermöglichen.
Beachten Sie, dass dieses PEP nicht darauf abzielt, das Problem der Verweigerung von widersprüchlichen oder inkompatiblen Extras zu lösen, z.B. wenn ein Paket genau ein Frontend- oder Backend-Paket benötigt. Derzeit gibt es keinen Mechanismus in der Python-Packaging-Infrastruktur, um widersprüchliche oder inkompatible Extras bei der Installation zu verweigern, und dieses PEP ändert daran nichts.
Beispiele für Pakete, die mindestens ein Backend oder Frontend zum Arbeiten benötigen und ein Standard-Extra zur Installation eines Backends oder Frontends empfehlen, sind:
In allen drei Fällen führt die Installation des Pakets ohne Extras zu einer fehlerhaften Installation, und dies ist ein häufig gemeldetes Supportproblem für einige dieser Pakete.
Begründung
Eine Reihe von möglichen Lösungen wurden von der Community über mehrere Jahre hinweg ausführlich diskutiert, unter anderem in diesem DPO-Thread sowie in zahlreichen Issues und Pull Requests. Die unten dargestellte Lösung
- ist eine Opt-in-Lösung, das heißt, Paketbetreuer können wählen, ob sie sie verwenden möchten oder nicht
- ist flexibel genug, um beide Hauptanwendungsfälle zu berücksichtigen, die in Motivation beschrieben werden
- verwendet die Syntax von PEP 508 neu
Es ist die einzige Lösung von allen diskutierten, die alle drei Kriterien erfüllt.
Spezifikation
Default-Extra Metadatenfeld
Ein neues, mehrfach verwendbares Metadatenfeld, Default-Extra, wird zu den Core Package Metadata hinzugefügt. Für dieses Feld muss jeder Eintrag eine Zeichenkette sein, die ein Extra angibt, das automatisch enthalten ist, wenn das Paket ohne explizit angegebene Extras installiert wird.
Nur Einträge, die bereits in einem Provides-Extra-Eintrag spezifiziert sind, können in einem Default-Extra-Eintrag verwendet werden.
Beispiele
Default-Extra: recommended
Default-Extra: backend1
Default-Extra: backend2
Default-Extra: backend3
Da dies ein neues Feld in den Core Package Metadata einführt, erfordert dies eine Erhöhung der Metadata-Version auf die nächste Minor-Version (derzeit 2.5).
Neuer Schlüssel in der Metadatentabelle [project]
Ein neuer Schlüssel wird der Tabelle [project] in den Projektmetadaten hinzugefügt, wie ursprünglich in PEP 621 definiert und jetzt in den PyPA-Spezifikationen definiert. Dieser Schlüssel wird default-optional-dependency-keys heißen und hat folgende Beschreibung:
- TOML-Typ: Array von Zeichenketten
- Entsprechendes Feld in den Core Metadata:
Default-Extra
Jede Zeichenkette in default-optional-dependency-keys muss der Name eines Extras sein, das in optional-dependencies definiert ist, und jedes Extra in diesem Array wird in einen entsprechenden Default-Extra-Eintrag in den Core Package Metadata umgewandelt. Beispiele für gültige Verwendung, die die in der vorherigen Sektion dargestellten Beispiel-Default-Extra-Einträge erzeugen würden, sind:
[project]
default-optional-dependency-keys = [
"recommended",
]
und
[project]
default-optional-dependency-keys = [
"backend1",
"backend2",
"backend3"
]
Überschreiben von Standard-Extras
Wenn Extras in einer Abhängigkeitsspezifikation explizit angegeben werden, werden die Standard-Extras ignoriert. Andernfalls werden die Standard-Extras installiert.
Wenn beispielsweise ein Paket ein extra1 als Standard-Extra definiert, sowie ein nicht-standardmäßiges extra2-Extra, dann wird bei einer Installation durch den Benutzer mit
$ pip install package
die standardmäßige extra1-Abhängigkeit enthalten sein. Wenn der Benutzer stattdessen das Paket mit installiert
$ pip install package[extra2]
dann wird das extra2-Extra installiert, aber das Standard-extra1-Extra wird ignoriert.
Wenn dasselbe Paket mehrmals in einem Installationsbefehl oder einem Abhängigkeitsbaum angegeben wird, müssen die Standard-Extras installiert werden, wenn eine der Instanzen des Pakets ohne Extras angegeben wird. Zum Beispiel, wenn ein Paket spam installiert wird, wobei package mehrmals im Abhängigkeitsbaum vorkommt
spam
├── tomato
│ ├── package[extra2]
└── egg
└── package
dann sollte das Standard-Extra installiert werden, da package mindestens einmal ohne angegebene Extras vorkommt.
Ein leerer Satz von Extras, wie z.B. package[], sollte so interpretiert werden, dass das Paket *ohne* jegliche Standard-Extras installiert werden soll (es sei denn, package kommt an anderer Stelle im Abhängigkeitsbaum vor, in welchem Fall das Standard-Extra wie oben erwähnt *installiert* würde). Dies würde eine universelle Möglichkeit bieten, eine minimale Installation eines Pakets zu erhalten.
Wir stellen auch fest, dass einige Werkzeuge wie pip derzeit nicht erkannte Extras ignorieren und eine Warnung an den Benutzer ausgeben, um anzuzeigen, dass das Extra nicht erkannt wurde, z.B.
$ pip install package[non-existent-extra]
WARNING: package 3.0.0 does not provide the extra 'non-existent-extra'
...
Für Werkzeuge, die sich so verhalten (anstatt einen Fehler auszugeben), sollten sie, wenn ein Extra in einer Abhängigkeitsspezifikation als ungültig erkannt wird, ignoriert werden. Wenn alle angegebenen Extras ungültig sind, sollte dies als äquivalent zu package[] (anstatt zu package) betrachtet werden und *keine* Standard-Extras installieren.
Schließlich weisen wir darauf hin (wie auch in Verlassen auf Werkzeuge zum Abwählen von Standard-Extras diskutiert), dass Paketinstallern erlaubt ist, ihre eigenen Optionen zu implementieren, um das oben genannte Verhalten zu steuern, z.B. eine Option, die Standard-Extras für einige oder alle Pakete deaktiviert, unabhängig davon, wo diese Pakete im Abhängigkeitsbaum erscheinen. Wenn solche werkzeugspezifischen Optionen implementiert werden, sollten Tool-Entwickler diese als Opt-in gestalten, und Benutzer sollten das obige PEP 771-Verhalten standardmäßig erleben.
Beispiele
In diesem Abschnitt betrachten wir die im Abschnitt Motivation beschriebenen Anwendungsfälle und wie diese nun durch die Verwendung der oben beschriebenen Spezifikation adressiert werden können.
Empfohlene Abhängigkeiten und minimale Installationen
Zunächst betrachten wir den Fall von Paketen, die empfohlene, aber nicht zwingend erforderliche Abhängigkeiten standardmäßig installiert haben möchten, während sie gleichzeitig eine Möglichkeit bieten, nur die erforderlichen Abhängigkeiten zu installieren.
Um dies zu tun, würde ein Paketbetreuer ein Extra namens recommended definieren, das die empfohlenen, aber nicht erforderlichen Abhängigkeiten enthält, und es als Standard-Extra auswählen lassen.
[project]
default-optional-dependency-keys = [
"recommended"
]
[project.optional-dependencies]
recommended = [
"package1",
"package2"
]
Wenn dieses Paket package hieße, würden Benutzer, die package installieren, die Entsprechung von package[recommended] erhalten. Benutzer könnten alternativ package[] installieren, was das Paket ohne die Standard-Extras installieren würde.
Um eines der konkreten Paketbeispiele aus dem Abschnitt Motivation zu nehmen, definiert das Paket astropy ein recommended-Extra, dessen Installation den Benutzern derzeit in den Standardinstallationsanweisungen empfohlen wird. Mit diesem PEP könnte das recommended-Extra als Standard-Extra deklariert werden.
[project]
default-optional-dependency-keys = [
"recommended"
]
[project.optional-dependencies]
recommended = [
"scipy",
"..."
]
was bedeutet, dass die Installation von
$ pip install astropy
dann optionale, aber empfohlene Abhängigkeiten wie scipy installieren würde. Fortgeschrittene Benutzer, die eine minimale Installation wünschen, könnten dann verwenden:
$ pip install astropy[]
Pakete, die mindestens ein Backend oder Frontend benötigen
Wie in Motivation beschrieben, können einige Pakete mehrere Backends und/oder Frontends unterstützen, und in einigen Fällen kann es wünschenswert sein, sicherzustellen, dass immer mindestens ein Backend oder Frontend installiert ist, da das Paket sonst nicht nutzbar wäre. Konkrete Beispiele hierfür könnten eine GUI-Anwendung sein, die eine GUI-Bibliothek benötigt, um nutzbar zu sein, aber verschiedene unterstützen kann, oder ein Paket, das auf verschiedene Rechen-Backends angewiesen ist, aber mindestens eines installiert haben muss.
In diesem Fall könnten Paketbetreuer die Entscheidung treffen, ein Extra für jedes Backend oder Frontend zu definieren und ein Standard-Extra bereitzustellen, z.B.:
[project]
default-optional-dependency-keys = [
"backend1"
]
[project.optional-dependencies]
backend1 = [
"package1",
"package2"
]
backend2 = [
"package3"
]
Wenn Pakete z.B. mehrere Backends gleichzeitig unterstützen können und einige der Backends immer installiert werden sollen, dann müssen die Abhängigkeiten dafür als erforderliche Abhängigkeiten angegeben werden und nicht über den Mechanismus für Standard-Extras.
Um eines der konkreten Beispiele aus Motivation zu nehmen, kann das Paket napari eines der folgenden verwenden: PyQt5, PyQt6, PySide2 oder PySide6. Benutzer müssen derzeit explizit napari[all] angeben, um eines davon installieren zu lassen, oder z.B. napari[pyqt5], um eines der Frontend-Pakete explizit anzugeben. Die Installation von napari ohne Extras führt zu einem nicht funktionsfähigen Paket. Mit diesem PEP könnte napari die folgende Konfiguration definieren:
[project]
default-optional-dependency-keys = [
"pyqt5"
]
[project.optional-dependencies]
pyqt5 = [
"PyQt5",
"..."
]
pyside2 = [
"PySide2",
"..."
]
pyqt6 = [
"PyQt6",
"..."
]
pyside6 = [
"PySide6",
"..."
]
was bedeutet, dass
$ pip install napari
sofort funktionieren würde, aber es gäbe immer noch einen Mechanismus für Benutzer, ein Frontend explizit anzugeben, z.B.:
$ pip install napari[pyside6]
Extras unterstützen, die Standard-Extras nicht entfernen sollen
Ein weiterer Fall, den wir hier betrachten, ist, dass ein Paketbetreuer die Möglichkeit unterstützen möchte, dass Benutzer sich für nicht-standardmäßige Extras entscheiden können, ohne die Standard-Extras zu entfernen. Im Wesentlichen möchten sie:
package[]für eine Installation ohne Extraspackagefür die Installation empfohlener Abhängigkeiten (in einemrecommended-Extra)package[alternative], um keine Standard-Extras zu installieren, sondern einen alternativen Satz optionaler Abhängigkeiten (in einemalternative-Extra) zu installierenpackage[additional], um sowohl empfohlene als auch zusätzliche Abhängigkeiten zu installieren (in einemadditional-Extra)
Dies könnte mit z.B. erreicht werden:
[project]
default-optional-dependency-keys = [
"recommended"
]
[project.optional-dependencies]
recommended = [
"package1",
"package2"
]
alternative = [
"package3"
]
additional = [
"package[recommended]",
"package4"
]
Die Möglichkeit für ein Paket, sich selbst in den Extras zu referenzieren, wird von bestehenden Python-Packaging-Werkzeugen unterstützt.
Noch einmal, unter Berücksichtigung eines konkreten Beispiels, könnte astropy mit diesem PEP ein recommended-Extra definieren (wie in Empfohlene Abhängigkeiten und minimale Installationen beschrieben). Es definiert jedoch auch andere Extras, darunter zum Beispiel jupyter, das Pakete hinzufügt, die die Benutzererfahrung in Jupyter-basierten Umgebungen verbessern. Es ist möglich, dass Benutzer, die sich für dieses Extra entscheiden, immer noch möchten, dass empfohlene Abhängigkeiten installiert werden. In diesem Fall würde die folgende Konfiguration diesen Fall lösen:
[project]
default-optional-dependency-keys = [
"recommended"
]
[project.optional-dependencies]
recommended = [
"scipy",
"..."
]
jupyter = [
"astropy[recommended]",
"ipywidgets",
"..."
]
Benutzer, die installieren
$ pip install astropy[jupyter]
würden dann dasselbe erhalten wie
$ pip install astropy[recommended, jupyter]
Pakete mit mehreren Arten von Standardwerten
In einigen Fällen kann es vorkommen, dass Pakete mehrere Arten von Standardwerten benötigen. Als Beispiel haben wir in Pakete, die mindestens ein Backend oder Frontend benötigen, den Fall von Paketen betrachtet, die *entweder* Backends oder Frontends haben, aber in einigen Fällen müssen Pakete Backends *und* Frontends unterstützen und möchten ein oder mehrere Standard-Frontends und ein oder mehrere Standard-Backends angeben.
Idealerweise möchte man vielleicht das folgende Verhalten:
$ pip install package # installs default backend and frontend
$ pip install package[] # installs no backends or frontends
$ pip install package[backend1] # installs backend1 and default frontend
$ pip install package[frontend2] # installs frontend2 and default backend
$ pip install package[backend1, frontend2] # installs backend1 and frontend2
Dieses PEP wählt jedoch die Bereitstellung eines Mechanismus ab, um z.B. zu bewirken, dass, wenn backend1 angegeben wird, das Standard-Backend deaktiviert wird, aber das Standard-Frontend aktiviert wird, da dies die Komplexität erhöht.
Betreuer sollten stattdessen vorerst dokumentieren, dass, wenn ein Backend oder Frontend explizit angegeben wird, sowohl Backend als auch Frontend angegeben werden müssen. Die Auffindbarkeit für Benutzer, die dies tun möchten, sollte jedoch kein Problem sein, da Benutzer ohnehin die Dokumentation lesen müssen, um herauszufinden, welche Backends oder Frontends verfügbar sind, so dass sie gleichzeitig erfahren, wie sie die Extras für Backends und Frontends richtig verwenden können.
Eine Möglichkeit zur Erhöhung der Benutzerfreundlichkeit ist, dass Betreuer Extras erstellen können, die z.B. defaultbackend und defaultfrontend genannt werden und die das Standard-Backend und -Frontend installieren. Sie können dann Benutzern empfehlen, Folgendes zu tun:
$ pip install package # installs default backend and frontend
$ pip install package[] # installs no backends or frontends
$ pip install package[backend1, defaultfrontend] # installs backend1 and default frontend
$ pip install package[defaultbackend, frontend2] # installs frontend2 and default backend
$ pip install package[backend1, frontend2] # installs backend1 and frontend2
Dies würde (wenn gewünscht) Benutzern ermöglichen, dann das empfohlene Backend zu erhalten, selbst wenn sich dieses Standard-Backend im Laufe der Zeit ändert.
Wenn in Zukunft der Wunsch besteht, eine bessere Lösung zu implementieren, glauben wir, dass dieses PEP dies nicht ausschließen sollte. Man könnte sich zum Beispiel vorstellen, in Zukunft die Möglichkeit hinzuzufügen, dass ein Extra bestimmt, welche Standard-Extras es deaktiviert, und wenn dies nicht angegeben wird, dann würden explizit angegebene Extras alle Standard-Extras deaktivieren (konsistent mit dem aktuellen PEP).
Abwärtskompatibilität
Pakete, die keine Standard-Extras verwenden
Sobald die Unterstützung für dieses PEP in Werkzeugen des Packaging-Ökosystems hinzugefügt wurde, werden Pakete, die keine Standard-Extras verwenden, wie bisher funktionieren und es sollte keine Kompatibilitätsbrüche geben.
Pakete, die Standard-Extras verwenden
Sobald Pakete mit der Definition von Standard-Extras beginnen, werden diese Standards nur mit neueren Versionen von Packaging-Werkzeugen eingehalten, die dieses PEP implementieren, aber diese Pakete bleiben mit älteren Packaging-Werkzeugen installierbar – wobei der Hauptunterschied darin besteht, dass die Standard-Extras nicht automatisch installiert werden, wenn ältere Packaging-Werkzeuge verwendet werden.
Wie in Wie man das vermittelt beschrieben, müssen Paketautoren sorgfältig abwägen, wann und wie sie die Funktion der Standard-Extras übernehmen, abhängig von ihrer Benutzerbasis, da einige Aktionen (wie die Verschiebung einer erforderlichen Abhängigkeit zu einem Standard-Extra) wahrscheinlich zu Brüchen für Benutzer führen werden, wenn ein signifikanter Teil von ihnen noch ältere Paketinstallierer verwendet, die keine Standard-Extras unterstützen. In diesem Sinne sollten sich Paketautoren bewusst sein, dass diese Funktion, wenn sie auf bestimmte Weise verwendet wird, Rückwärtskompatibilitätsprobleme für Benutzer verursachen kann, und sie sind daher dafür verantwortlich, sicherzustellen, dass sie die Auswirkungen auf die Benutzer minimieren.
Sicherheitsimplikationen
Für dieses PEP sind keine bekannten Sicherheitsprobleme bekannt.
Wie man dies lehrt
Dieser Abschnitt enthält Informationen, die verschiedenen Gruppen in der Community im Zusammenhang mit der Implementierung dieses PEP zur Verfügung gestellt werden sollten. Einige der unten beschriebenen Aspekte sind auch vor der vollständigen Implementierung des PEP in Packaging-Werkzeugen relevant, da es einige Vorbereitungen geben kann, die im Voraus getroffen werden können, um einen potenziellen Übergang später zu erleichtern. Die unten behandelten Gruppen sind:
Paketendbenutzer
Paketbenutzern sollten klare Installationsanweisungen zur Verfügung gestellt werden, die zeigen, welche Extras für Pakete verfügbar sind und wie sie funktionieren, z.B. indem erklärt wird, dass standardmäßig einige empfohlene Abhängigkeiten oder ein bestimmtes Frontend oder Backend installiert werden, und wie man sich davon abmeldet oder Standardeinstellungen überschreibt, je nachdem, was verfügbar ist.
Betreuer von Paket-Repositories
Die Auswirkungen auf Einzelpersonen, die Python-Bibliotheken für verschiedene Distributionen neu verpacken, wie z. B. conda, Homebrew, Linux-Paketinstallationsprogramme (wie apt und yum) usw., müssen berücksichtigt werden. Nicht alle Paketdistributionen verfügen über Mechanismen, die dem beschriebenen Ansatz entsprechen würden. Tatsächlich haben einige Distributionen wie conda nicht einmal das Konzept von Extras.
Hier sind zwei Fälle zu berücksichtigen
- In Fällen, in denen die Neuverpackung von Hand erfolgt, z. B. für eine Reihe von conda-forge-Rezepten, und insbesondere, wenn es kein Äquivalent zu Extras gibt, sollte die Einführung von Standard-Extras keinen großen Einfluss haben, da manuelle Entscheidungen bereits getroffen werden müssen, welche Abhängigkeiten einzuschließen sind (zum Beispiel enthält das conda-forge-Rezept für das Paket astropy, das im Abschnitt Motivation erwähnt wird, standardmäßig alle
recommendedAbhängigkeiten, da Benutzer sie anderweitig nicht explizit anfordern können). - In Fällen, in denen die Neuverpackung automatisiert erfolgt, müssen die Distributoren sorgfältig überlegen, wie mit Standard-Extras umzugehen ist, und dies kann eine nicht unerhebliche Menge an Arbeit und Diskussion bedeuten.
Es ist für einen PEP wie diesen unmöglich, jeden der verschiedenen Paketdistributionen erschöpfend zu berücksichtigen. Letztendlich sollten Standard-Extras jedoch als die Art und Weise verstanden werden, wie Paketautoren möchten, dass ihr Paket für die Mehrheit der Benutzer installiert wird, und dies sollte Entscheidungen darüber informieren, wie Standard-Extras behandelt werden sollen, sei es manuell oder automatisch.
Referenzimplementierung
Das folgende Repository enthält ein voll funktionsfähiges Demopaket, das Standard-Extras verwendet
https://github.com/wheel-next/pep_771
Dies nutzt modifizierte Zweige mehrerer Pakete, und die folgenden Links verweisen auf diese Zweige
Darüber hinaus enthält dieser Zweig eine modifizierte Version des Flit-Pakets.
Die oben genannten Implementierungen sind derzeit Proofs-of-Concept, und die bestehenden Änderungen wurden noch nicht von den zuständigen Betreuern überprüft. Nichtsdestotrotz sind sie funktionsfähig genug, um interessierten Betreuern zu ermöglichen, diese auszuprobieren.
Abgelehnte Ideen
Verwendung eines Meta-Pakets für empfohlene Installationen
Unter Verwendung bestehender Packaging-Tools und -Infrastruktur können Paketbetreuer, die für einige Benutzer eine minimale Installation und für normale Benutzer eine standardmäßige nicht-minimale Installation (z. B. mit empfohlenen Abhängigkeiten oder einem Standard-Backend) bereitstellen möchten, dies technisch bereits erreichen, wenn sie bereit sind, zwei Pakete anstelle von einem zu verteilen – z. B. package-core, das das Hauptpaket mit minimalen Abhängigkeiten wäre, und package, das ein Metapaket wäre, das von package-core mit optionalen Abhängigkeiten abhängen würde.
Nehmen wir noch einmal ein konkretes Beispiel aus dem Abschnitt Motivation. Das Paket astropy definiert ein recommended Extra, dessen Installation den Benutzern derzeit in den Standardinstallationsanweisungen empfohlen wird. Im Prinzip könnte man das bestehende Paket astropy in z. B. astropy-core umbenennen und dann ein neues Paket astropy erstellen, das ein Metapaket wäre, das den folgenden Abhängigkeitsabschnitt enthält:
dependencies = [
"astropy-core[recommended]"
]
Da Benutzer Versionen oder Versionsbeschränkungen für das astropy Meta-Paket festlegen möchten (z. B. astropy>5.0), müsste das Meta-Paket denselben Versionen wie das Kernpaket folgen und strenge Pinning im Abhängigkeitsabschnitt verwenden, z. B.:
version = "7.1.0"
dependencies = [
"astropy-core[recommended]==7.1.0"
]
Diese Idee mag ansprechend erscheinen, da sie technisch bereits machbar ist. In der Praxis haben sich jedoch viele Projekte aus einer Reihe von Gründen dagegen entschieden, die wir uns nun ansehen. Einige davon sind möglicherweise nicht auf zukünftige neue Projekte anwendbar, aber einige davon gelten für alle Projekte, alte und neue.
Diskrepanz zwischen Paket- und Modulnamen
In Bezug auf die Benennung gibt es zwei Hauptoptionen für ein Paket, das den Metapaket-Ansatz verwenden möchte
- Die erste Option ist, dass das bestehende Paket unverändert bleibt, was bedeutet, dass
packagedie minimale Installation bereitstellt und dann ein neues Metapaket mit einem anderen Namen erstellt, z. B.package-all. Dies leidet jedoch unter einem der Probleme, die diesen PEP ursprünglich motiviert haben – Benutzer sind sich oft nicht bewusst, dass sie z. B.package[recommended]tun können, und auf die gleiche Weise erkennen sie möglicherweise nicht, dasspackage-allexistiert. Dies legt wieder die Last auf den durchschnittlichen Benutzer, dies zu entdecken, anstatt einen Teil der Last auf fortgeschrittene Benutzer zu verlagern. - Die zweite Option ist, das bestehende Paket in z. B.
package-coreumzubenennen und das neue Meta-Paketpackagezu nennen. Dies ist eine bessere Option als die erste, aber nicht ideal, da sie dann einen unintuitiven Widerspruch zwischen dem Paketnamen und dem Modulnamen einführt, in dem Sinne, dasspackage-coredas Modulpackagebereitstellt undpackagekein Modul bereitstellt. Ein Beispiel dafür, warum dies zu Verwirrung führen würde, ist, dass ein durchschnittlicher Benutzer denken könnte, dass die Deinstallation des Modulspackagemit z. B.$ pip uninstall package
aber das wäre nicht der Fall (das Modul
packagewürde weiterhin funktionieren), und es ist diesem Benutzer möglicherweise nicht offensichtlich, dass das Paketpackage-coreüberhaupt existiert.
Mehrere Repositories oder Monorepos
Dieser Ansatz erfordert entweder die Wartung von zwei Repositories anstelle von einem oder den Wechsel zu einem Monorepo, das beide Pakete enthalten würde. Keine der Optionen ist ideal
- Die Aufteilung in zwei Repositories stellt eine langfristige zusätzliche Belastung für die Betreuer dar, die sicherstellen müssen, dass diese synchron bleiben (in Bezug auf die Version, aber auch andere Aspekte wie Extras, wie in Synchronizing metadata diskutiert wird). Darüber hinaus hat das in Mismatch between package and module name erwähnte Benennungsproblem zusätzliche Komplikationen – entweder stimmen die Namen der Repositories mit den Paketen überein, in diesem Fall muss jeder Benutzer, der einen Checkout des vorherigen
packageRepositories hat, seine Remote-URLs oder Git-Clone-URLs aktualisieren, um auf daspackage-coreRepository zu verweisen. Die Alternative ist, daspackageRepository beizubehalten, um das Paketpackage-corezu enthalten, und einen anderen Namen für das Metapaket zu haben, aber dies könnte zu Verwirrung führen. - Der Wechsel zu einem Monorepo kann für einige Projekte eine erhebliche Änderung sein, und es ist nicht ungewöhnlich, dass Tools standardmäßig davon ausgehen, dass ein einzelnes Repository einem einzelnen Paket entspricht – obwohl diese oft für die Arbeit mit einem Monorepo konfiguriert werden können, ist dies eine zusätzliche Belastung für die Betreuer. Darüber hinaus muss jeder Benutzer, der das Paket von der Repository-URL installiert (z. B. mit pip), wenn das Hauptpaket in ein Unterverzeichnis im Monorepo verschoben wird, dies anpassen, um aus einem Unterverzeichnis zu installieren (Hinzufügen von
subdirectory=zur Repo-URL), und alle bestehenden Workflows, die das Repository klonen und das vorherige Layout annehmen, würden unterbrochen.
Abhängigkeit vom Minimalpaket
Pakete, die von Paketversionen abhängen müssen, die älter sind als die erste Version, in der die Aufteilung erfolgte, können nicht einfach von dem Minimalpaket abhängen. Während bei dem Hauptvorschlag dieses PEP Downstream-Benutzer von z. B. package[]>version abhängen können, wobei version vor der Einführung von Standard-Extras liegt, wird es bei dem Aufteilungsansatz für Downstream-Benutzer nicht möglich sein, von z. B. package-core>version abzuhängen, da package-core zuvor nicht existierte.
Eine mögliche Lösung hierfür ist, dass Entwickler No-Op-Metadatenpakete für alle alten Versionen eines Pakets veröffentlichen, aber dies ist eine erhebliche zusätzliche Belastung für die Betreuer.
Deinstallation
Wie bei der Erwähnung von Namensproblemen in Mismatch between package and module name erwähnt, funktioniert die Deinstallation von Paketen nicht mehr so, wie Benutzer es erwarten. Ein Benutzer, der tut
$ pip uninstall package
wird immer noch mit package-core zurückgelassen, wird es aber vielleicht nicht bemerken. Dies ist nicht nur verwirrend, sondern ist de facto eine Breaking Change, die eine Reihe bestehender Workflows beeinträchtigen kann.
Paketverteilungen
Zwei Pakete anstelle von einem zu haben, würde die langfristigen Wartungskosten von Paketdistributionen erhöhen, allein schon durch die Tatsache, dass zwei Pakete anstelle von einem veröffentlicht werden müssten, und in einigen Fällen würde dies bei jeder Veröffentlichung zusätzliche manuelle Arbeit bedeuten.
Synchronisation von Metadaten
Die wichtigsten Metadaten, die zwischen dem Hauptpaket und dem Metapaket synchron gehalten werden müssten, ist die Version. Jedes Mal, wenn eine neue Version des Kernpakets veröffentlicht wird, muss die Version des Metapakets sowie das Version-Pinning für das Kernpaket in den Abhängigkeiten aktualisiert werden.
Darüber hinaus müssten alle im Kernpaket definierten Extras im Metapaket neu definiert und synchron gehalten werden. Wenn zum Beispiel package ein additional Extra definiert, sollten Benutzer immer noch package[additional] installieren können, aber Benutzer, die das Paket package-core installieren, sollten auch die Option haben, package-core[additional] zu tun.
Andere Metadaten, die synchron gehalten werden müssten, umfassen beispielsweise Autoreninformationen und Projekt-URLs.
Zusammenfassung
Insgesamt würde diese Lösung einen erheblich höheren Wartungsaufwand bedeuten, nicht nur in Bezug auf die anfängliche Einrichtung und Umstellung (die für große etablierte Projekte bereits prohibitiv sein könnte), sondern auch in Bezug auf die langfristige Wartung. Dies birgt auch das Potenzial, Benutzer-Workflows zu unterbrechen (insbesondere im Zusammenhang mit Repositories und z. B. der Deinstallation). Aus all diesen Gründen halten wir dies nicht für eine überzeugende Alternative zu diesem PEP.
Syntax zum Abwählen von Extras
Einer der Hauptkonkurrenzansätze war wie folgt: Anstatt Standard-Extras auszuwählen, wenn explizit Extras bereitgestellt wurden, müssten Standard-Extras explizit abgewählt werden.
In diesem Bild würde eine neue Syntax zum Abwählen von Extras als Erweiterung der in PEP 508 definierten Mini-Sprache eingeführt. Wenn ein Paket Standard-Extras definierte, könnten Benutzer diese Standardeinstellungen durch Verwenden eines Minuszeichens (-) vor dem Extra-Namen abwählen. Die vorgeschlagene Syntaxaktualisierung wäre wie folgt:
extras_list = (-)?identifier (wsp* ',' wsp* (-)?identifier)*
Gültige Beispiele für diese neue Syntax wären z. B.
package[-recommended]package[-backend1, backend2]package[pdf, -svg]
Es gibt jedoch zwei Hauptprobleme bei diesem Ansatz
- Man müsste eine Reihe von Regeln definieren, wie Eckfälle zu interpretieren sind, z. B. wenn ein Extra und seine negierte Version beide in derselben Abhängigkeitsspezifikation vorhanden sind (z. B.
package[pdf, -pdf]) oder wenn ein Abhängigkeitsbaum sowohlpackage[pdf]als auchpackage[-pdf]enthält, und die Regeln wären für Benutzer nicht intuitiv. - Noch kritischer ist, dass dies neue Syntax in die Abhängigkeitsspezifikation einführen würde, was bedeutet, dass, wenn ein Paket eine Abhängigkeit mit der neuen Syntax definierte, es und jedes andere Paket, das davon abhängt, nicht mehr von bestehenden Packaging-Tools installiert werden könnten, was einen erheblichen Rückwärtskompatibilitätsbruch darstellen würde.
Aus diesen Gründen wurde diese Alternative nicht in den endgültigen Vorschlag aufgenommen.
Hinzufügen eines speziellen Eintrags in extras_require
Eine mögliche Lösung, die als Alternative zur Einführung des neuen Metadatenfeldes Default-Extra untersucht wurde, wäre die Verwendung eines Extras mit einem „speziellen“ Namen.
Ein Beispiel wäre die Verwendung eines leeren Strings
Provides-Extra:
Requires-Dist: numpy ; extra == ''
Die Idee wäre, dass Abhängigkeiten, die als Teil der „leeren“ Extras installiert werden, nur dann installiert werden, wenn kein anderes Extra angegeben wurde. Eine Implementierung davon wurde in https://github.com/pypa/setuptools/pull/1503 vorgeschlagen, aber es wurde festgestellt, dass es keine Möglichkeit gäbe, dies zu erreichen, ohne die Kompatibilität mit bestehender Nutzung zu brechen. Zum Beispiel können Pakete, die Setuptools über eine setup.py-Datei verwenden, Folgendes tun:
setup(
...
extras_require={'': ['package_a']},
)
was gültig und äquivalent zu package_a ist, das in install_requires definiert ist, so dass die Änderung der Bedeutung des leeren Strings die Kompatibilität brechen würde.
Darüber hinaus kann kein anderer String (wie z. B. 'default') als spezieller String verwendet werden, da alle Strings, die ein rückwärtskompatibler gültiger Extras-Name wären, bereits in bestehenden Paketen verwendet werden können.
Es gab Vorschläge zur Verwendung der speziellen Python-Variable None, aber auch dies ist nicht möglich, da, obwohl man None in einer setup.py-Datei verwenden kann, dies in deklarativen Dateien wie setup.cfg oder pyproject.toml nicht möglich ist, und außerdem müssen Extra-Namen letztendlich in die Paketmetadaten in Strings umgewandelt werden. Das Haben
Provides-Extra: None
wäre nicht zu unterscheiden von dem String „None“, der möglicherweise bereits als Extra-Name in einem Python-Paket verwendet wird. Wenn wir die Kern-Metadatensyntax ändern würden, um nicht-String „spezielle“ Extra-Namen zuzulassen, dann wären wir wieder dabei, die Kern-Metadatenspezifikation zu ändern, was nicht besser ist als die Einführung von Default-Extra.
Verlassen auf Werkzeuge zum Abwählen von Standard-Extras
Eine weitere Option zur Abwahl von Extras wäre die Implementierung auf Ebene der Packaging-Tools. Zum Beispiel könnte pip eine Option wie
$ pip install package --no-default-extras
Diese Option könnte für alle oder bestimmte Pakete gelten, ähnlich der Option --no-binary, z. B.:
$ pip install package --no-default-extras :all:
Der Vorteil dieses Ansatzes ist, dass Tools, die Standard-Extras unterstützen, diese auch unterstützen könnten, sie abzuwählen. Dieser Ansatz wäre ähnlich der Option --no-install-recommends für das apt-Tool.
Dieser Lösungsansatz ist jedoch allein nicht ideal, da er es Paketen nicht ermöglichen würde, selbst anzugeben, dass sie einige der Standard-Extras einer Abhängigkeit nicht benötigen. Er wäre auch mit Risiken für Benutzer verbunden, die möglicherweise alle Standard-Extras in einem großen Abhängigkeitsbaum deaktivieren und dabei möglicherweise Pakete im Baum brechen, die sich an irgendeiner Stelle auf Standard-Extras verlassen.
Dennoch schließt dieser PEP diesen Ansatz nicht aus, und es liegt an den Betreuern der verschiedenen Packaging-Tools zu entscheiden, ob sie diese Art von Option unterstützen möchten. Es ist ein Flag, das zumindest für Paketbetreuer nützlich sein könnte, die Orte in Abhängigkeitsbäumen identifizieren möchten, an denen Standard-Extras genutzt werden. Wenn er jedoch unterstützt wird, sollte klargestellt werden, dass die Verwendung dieses Flags keine funktionierende Umgebung garantiert.
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-0771.rst
Zuletzt geändert: 2025-06-09 11:29:19 GMT