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

Python Enhancement Proposals

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

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.

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:

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.

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 Extras
  • package für die Installation empfohlener Abhängigkeiten (in einem recommended-Extra)
  • package[alternative], um keine Standard-Extras zu installieren, sondern einen alternativen Satz optionaler Abhängigkeiten (in einem alternative-Extra) zu installieren
  • package[additional], um sowohl empfohlene als auch zusätzliche Abhängigkeiten zu installieren (in einem additional-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.

Paketersteller

Obwohl der Mechanismus zur Definition von Extras und die damit verbundene Regel, wann er verwendet werden soll, klar sind, müssen Paketautoren mehrere Punkte sorgfältig prüfen, bevor sie diese Funktionalität in ihren Paketen übernehmen, um unbeabsichtigte Rückwärtskompatibilitätsbrüche zu vermeiden.

Unterstützung älterer Versionen von Paketinstallern

Paketinstallierer wie pip oder uv werden die Unterstützung für Standard-Extras nicht unbedingt gleichzeitig implementieren, und sobald sie dies tun, werden Paketautoren wahrscheinlich Benutzer unterstützen wollen, die nicht die neueste Version eines Paketinstallers haben. In diesem Fall gelten die folgenden Empfehlungen:

  • Die Verschiebung eines Pakets von einer erforderlichen Abhängigkeit zu einem Standard-Extra wäre eine Breaking Change, da ältere Versionen von Paketinstallern das Konzept von Standard-Extras nicht erkennen würden und das Paket dann mit weniger Abhängigkeiten installieren würden, was Benutzer beeinträchtigen könnte, die sich auf diese verlassen haben. Daher sollte die Änderung von Abhängigkeiten von erforderlich zu einem Standard-Extra in einem etablierten Paket nur in Zukunft erfolgen, wenn die Entwickler nur noch Benutzer mit Installern unterstützen wollen, die dieses PEP implementieren.
  • Ein bestehendes Extra zu einem Standard zu machen, sollte sicherer sein, z.B. recommended in astropy zu einem Standard-Extra zu machen. Um jedoch Benutzer mit älteren Versionen von Paketinstallern zu unterstützen, sollte die Dokumentation das Extra weiterhin explizit erwähnen, solange es klar ist, dass die meisten/alle Benutzer Paketinstallierer verwenden, die dieses PEP implementieren. Es gibt keinen Nachteil, das Extra explizit zu erwähnen, aber dies stellt sicher, dass Benutzer mit moderneren Werkzeugen, die die Dokumentation nicht lesen (was einen nicht unerheblichen Teil der Benutzergemeinschaft ausmachen kann), standardmäßig die empfohlenen Abhängigkeiten erhalten.
  • Da vor diesem PEP package[] äquivalent zu package war, werden Autoren package[] als abwärtskompatible universelle Methode für eine minimale Installation dokumentieren können. Für Pakete, die Standard-Extras definieren, liefert die Installation von package[] auch mit älteren Versionen von Packaging-Tools wie pip immer eine minimale Installation, und Releases dieses Pakets, die vor der Einführung von Standard-Extras für ein bestimmtes Paket liegen, werden ebenfalls mit package[] installierbar sein (obwohl dies in diesen Fällen äquivalent zu package ist). Für Pakete, die keine Standard-Extras definieren, wird package[] weiterhin äquivalent zu package sein.

Vermeidung der Hinzufügung vieler Standardabhängigkeiten

Eine Verlockung für Autoren könnte sein, standardmäßig viele Abhängigkeiten einzuschließen, da sie eine Möglichkeit bieten können, sich davon abzumelden. Wir empfehlen jedoch, dass Autoren sorgfältig abwägen, was standardmäßig enthalten ist, um unnötige Installationen zu vermeiden und Abhängigkeitsbäume zu verkomplizieren. Die Verwendung von Standard-Extras bedeutet nicht, dass alle Extras standardmäßig sein müssen, und es gibt immer noch Spielraum für Benutzer, sich explizit für Nicht-Standard-Extras anzumelden.

Standard-Extras sollten im Allgemeinen mit dem gleichen „Gewicht“ wie erforderliche Abhängigkeiten behandelt werden. Wenn ein Paket weit verbreitet ist, führt die Einführung eines Standard-Extras dazu, dass die Abhängigkeiten dieses Extras transitiv einbezogen werden – es sei denn, alle nachgelagerten Pakete werden aktualisiert, um sich explizit mithilfe von Minimalinstallationsspezifikationen abzumelden.

Als Beispiel hat das Paket pytest derzeit fast 1.500 Plugins, die davon abhängen. Wenn pytest ein Standard-Extra hinzufügt und diese Plugins nicht entsprechend aktualisiert werden, würde die Installation eines Plugins die Abhängigkeiten der Standard-Extras einschließen. Dies schließt die Verwendung von Standard-Extras nicht aus, aber die Hinzufügung von Standard-Extras erfordert eine sorgfältige Bewertung seiner nachgelagerten Auswirkungen.

Erben von Standard-Extras

Wenn Paketautoren wählen, ein Extra standardmäßig installieren zu lassen, ist es wichtig, dass sie sich bewusst sind, dass das Standard-Extra möglicherweise nicht installiert wird, wenn Benutzer explizit ein anderes Extra angeben, es sei denn, sie verwenden den in Supporting extras that should not remove default extras beschriebenen Ansatz.

Es gibt Fälle, wie z. B. austauschbare Backends, in denen das Ignorieren des Standard-Extras, wenn ein Extra explizit angegeben wird, das Richtige ist. Für andere Fälle, wie z. B. die Verwendung von Standard-Extras zur Einbeziehung empfohlener Abhängigkeiten und gleichzeitig die Bereitstellung einer Möglichkeit für minimale Installationen, kann es sein, dass viele der anderen Extras explizit die Standard-Extra(s) „erben“ sollten, so dass Paketautoren sorgfältig überlegen sollten, in welchen Fällen sie möchten, dass die Standard-Extras installiert werden.

Inkompatible Extras

In einigen Fällen kann es vorkommen, dass Pakete Extras haben, die sich gegenseitig ausschließen. In diesem Fall empfehlen wir, die Funktion für Standard-Extras nicht für ein Extra zu verwenden, das eine Abhängigkeit enthält, die mit einer anderen inkompatibel sein könnte.

Betrachten Sie ein Paket, das die Extras package[A] und package[B] hat. Benutzer könnten bereits versuchen, package[A] und package[B] oder package[A,B] zu installieren, was zu einer fehlerhaften Installation führen würde, aber es wäre zumindest explizit, dass beide Extras installiert wurden. Wenn man jedoch A zu einem Standard-Extra macht, könnte dies zu unintuitiven Problemen führen. Ein Benutzer könnte tun

$ pip install package  # this installs package[A]
$ pip install package[B]

und mit einer fehlerhaften Installation enden, auch wenn A und B niemals explizit beide installiert wurden. Aus diesem Grund empfehlen wir, Standard-Extras nicht für Abhängigkeiten zu verwenden, bei denen dies wahrscheinlich ein Problem darstellt.

Zirkuläre Abhängigkeiten

Autoren müssen bei zirkulären Abhängigkeiten besondere Vorsicht walten lassen. Betrachten Sie zum Beispiel den folgenden Abhängigkeitsbaum

package1
└── package2
    └── package1

Wenn package1 ein Standard-Extra namens recommended hat, dann

$ pip install package1[]

Dies könnte durch eine Änderung des Abhängigkeitsbaums gelöst werden:

package1
└── package2
    └── package1[]

vorausgesetzt, dass package2 tatsächlich nicht von den Extra-Abhängigkeiten von package1 abhängt. Autoren müssen daher sorgfältig einen Migrationsplan erwägen und sich mit den Autoren von package2 abstimmen.

Dokumentation von Paketen mit Standard-Extras

Unabhängig davon, wie Standard-Extras verwendet werden, sollten Paketautoren darauf abzielen, sicherzustellen, dass ihre Dokumentation klarstellt, wie Extras zu verwenden sind. Die Dokumentation zu „Best Practices“ sollte erwähnen

  • dass die Installation von package äquivalent zu package[<default extras>] sein wird
  • dass die Installation von package[] nur minimale/erforderliche Abhängigkeiten einschließt, aber dass dies keine Garantie dafür ist, dass optionale Abhängigkeiten nicht installiert werden, wenn package irgendwo anders im Abhängigkeitsbaum vorkommt
  • welche anderen optionalen Extras verfügbar sind und ob sie die Standard-Extras deaktivieren (da dies wie in Supporting extras that should not remove default extras beschrieben gesteuert werden kann)
  • Anweisungen, die spezifisch für Pakete sind, die z. B. Standard-Backends und -Frontends haben (wie in Packages with multiple kinds of defaults beschrieben)

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 recommended Abhä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

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 sowohl package[pdf] als auch package[-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.


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

Zuletzt geändert: 2025-06-09 11:29:19 GMT