[!NOTE] Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

pyproject.toml Leitfaden: Paketierung mit Python, Abhängigkeiten und Konfiguration von Tools

ältere Python-Projekte verteilen die Konfiguration oft über setup.py, setup.cfg, Anforderungsdateien, MANIFEST.insowie getrennte Dateien für die Entwicklungstools. pyproject.toml es bietet diesen Anliegen eine gemeinsame Plattform, ersetzt jedoch nicht jede Projektdatei und macht auch nicht jeden Abschnitt zu einem Teil eines einheitlichen Standards.

Dieser Leitfaden führt Sie durch die Datei – von der Build-Konfiguration über die Projektmetadaten bis hin zu den Tool-Einstellungen. Die einfachste Möglichkeit, ihn verständlich zu halten, besteht darin, vier Verantwortungsbereiche voneinander zu trennen: Was das Projekt selbst kompiliert, was das Projekt veröffentlicht, was die Mitwirkenden lokal benötigen, sowie welche einzelnen Tools konfiguriert werden müssen.

TL;DR. Verwenden Sie [build-system] für den Backend, der eine Distribution erstellt. [project] für veröffentlichte Metadaten sowie die Anforderungen an Runtime. [dependency-groups] für unveröffentlichte Entwicklungsumgebungen, und [tool.*] Nur dort, wo die Dokumentation des Tools dies vorschreibt. Eine Abhängigkeitsdeklaration ist kein Lockfile.

Ein Datei, vier Eigentümer

Die vier Eigentumsgrenzen in pyproject.toml

Drei Verpackungsstandards legten die grundlegende Struktur fest:

Toolautoren können ebenfalls einen Namespace darunter beanspruchen. [tool]. Dieser Bereich ist konventionell und nicht universell: Jedes Tool definiert seine eigenen Schlüssel sowie sein eigenes Verhalten.

Hier ist eine kleine, verpackte Bibliothek:

[build-system]
requires = ["hatchling>=1.26"]
build-backend = "hatchling.build"

[project]
name = "weather-client"
version = "0.1.0"
description = "A small weather API client"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
  "httpx>=0.27",
]

[project.optional-dependencies]
cli = ["rich>=13"]

[project.scripts]
weather = "weather_client.cli:main"

[dependency-groups]
test = ["pytest>=8", "pytest-cov>=5"]
lint = ["ruff>=0.12"]

[tool.pytest.ini_options]
testpaths = ["tests"]

[tool.ruff]
line-length = 100
target-version = "py311"

Jede Abhängigkeitsliste beantwortet eine andere Frage. Diese Unterscheidung bildet den Kern der Datei.

[build-system]: Wie aus einer Quelle eine Distribution entsteht

Ein Build Frontend wie zum Beispiel python -m buildpip oder uv rufen einen Build-Prozess Backend auf. Der Backend bestimmt, wie aus dem Quellbaum ein sdist oder eine Wheel-Datei erstellt wird sowie welche Dateien in diese Artefakte aufgenommen werden.

[build-system]
requires = ["hatchling>=1.26"]
build-backend = "hatchling.build"

requires Es enthält Abhängigkeiten, die zur Ausführung des Backend in seiner isolierten Build-Umgebung erforderlich sind. Es handelt sich dabei nicht um die Liste der Runtime-Abhängigkeiten für Ihr Paket.

Erklären Sie ein Build-System, wenn das Projekt eine Distribution erstellt oder seine eigenen Code-Bibliotheken als Paket installiert werden müssen. Ein nicht-paketbasiertes Projekt kann dennoch eines verwenden. pyproject.toml; PEP 735 erlaubt sogar eine Datei, die ausschließlich Abhängigkeitsgruppen enthält. Umgebungsmanager unterscheiden sich dabei in der Art und Weise, wie sie mit Projekten umgehen, die kein Build-System besitzen – daher sollte diese Entscheidung bewusst getroffen werden.

Wählen Sie den Backend aus den Build-Anforderungen aus:

Kopieren Sie die derzeit empfohlene Tabelle von Backend aus seiner Dokumentation. Fügen Sie nichts hinzu. wheel Um Anforderungen aus Gewohnheit zu formulieren, legt der Backend fest, was benötigt wird.

[project]: Von den Metadaten-Konsumenten empfangen

Der [project] Die Tabelle beschreibt die Verteilung: ihren Namen, die Version, die Kompatibilität mit Python, die Runtime-Abhängigkeiten, die Eingangspunkte sowie weitere Metadaten des Index.

[project]
name = "weather-client"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["httpx>=0.27"]

Die Abhängigkeitsspezifikatoren stellen Einschränkungen für einen Resolver dar und sind keine Beschränkung einer bestimmten Snapshot-Umgebung. Sie werden Teil der Metadaten von Wheel- und sdist-Dateien, sodass nachfolgende Installer sie mit den Anforderungen anderer Pakete kombinieren können.

Zusatzfunktionen stellen eine öffentliche Installations-Schnittstelle dar.

[project.optional-dependencies] definiert zusätzliche Funktionen, die von den Nutzern angefordert werden können:

[project.optional-dependencies]
cli = ["rich>=13"]
postgres = ["psycopg[binary]>=3.2"]

Ein Endnutzer kann einrichten weather-client[cli]. Da zusätzliche Namen und Anforderungen veröffentlicht werden, sollten diese als Produktfunktionen betrachtet werden. Verwenden Sie keine zusätzlich benannten dev ausschließlich zur Bereitstellung von Tools für alle Mitwirkenden.

Eingangspunkte verbinden installierte Befehle mit Python

[project.scripts]
weather = "weather_client.cli:main"

Nach der Installation der Distribution stellt die Umgebung die notwendigen Ressourcen bereit. weather, welches importiert und aufruft weather_client.cli:main. Testen Sie den Befehl aus einer bereitgestellten Wheel-Datei, und zwar nicht nur vom Root-Verzeichnis des Repositoriums – schließlich ist es die Wheel-Datei, die die Benutzer erhalten.

[dependency-groups]: lokale, unveröffentlichte Umgebungen

Die in PEP 735 definierten Abhängigkeitsgruppen beschreiben Entwicklungs- oder Nicht-Paket-Umgebungen, ohne diese als Metadaten eines Pakets zu veröffentlichen.

[dependency-groups]
test = ["pytest>=8", "pytest-cov>=5"]
lint = ["ruff>=0.12"]
dev = [
  { include-group = "test" },
  { include-group = "lint" },
]

Dies ist die richtige Grenze für Tests, Linter, Dokumentationsgeneratoren sowie ähnliche Tools für Mitwirkende. Sie ist außerdem nützlich für Anwendungen oder Notebooks, die keine Distribution erstellen.

Abhängigkeitsgruppen sind standardisierte Daten, doch die Installierungsinterfaces unterscheiden sich weiterhin. Prüfen Sie, wie der ausgewählte Umgebungsmanager sie installiert, absichert und auflöst. PEP 735 definiert keine universelle Kommandozeilen-Schnittstelle.

[tool.*]: Konfiguration, die einem Tool gehört

Tool-Tabellen teilen kein Schema miteinander:

[tool.pytest.ini_options]
addopts = "-ra"
testpaths = ["tests"]

[tool.ruff]
line-length = 100

Verwenden Sie einen [tool.*] Die Tabelle wird nur dann bereitgestellt, wenn das Tool dies dokumentiert. Einige Einstellungen befinden sich weiterhin in eigenen Dateien, weil ein anderes Ökosystem sie benötigt, die Datei ein anderes Format erfordert oder die Konfiguration für sich genommen klarer ist. pyproject.toml Es handelt sich dabei um einen Koordinierungspunkt und nicht um eine Vorgabe, alles zentral zu verwalten.

Konfigurationsverarbeiter im Umfeld von pyproject.toml

Deklarationen, Sperren und Anforderungsdateien lösen unterschiedliche Probleme

Die häufigste Quelle für Verwirrung besteht darin, jedes Abhängigkeitsartefakt als konkurrierende Quelle der Wahrheit zu betrachten.

ArtefaktHauptzweckTypische Inhalte
[project.dependencies]Veröffentlichter Runtime-VertragDirekte Anforderungen und kompatible Wertebereiche
[project.optional-dependencies]Veröffentlichte, auf Anfrage verfügbare FunktionenKundenorientierte Zusatzfunktionen
[dependency-groups]Unveröffentlichte lokale UmgebungenTest-, Lint-, Dokumentations- oder Anwendungsgruppen
SchlüsseldateiEin bereits bereinigtes Umfeld reproduzierenGenaue Versionen, Quellen sowie Metadaten zur Auflösung
requirements.txtEingabe für eine pip-kompatible InstallationAnforderungen sowie pip-spezifische Optionen, Einschränkungen, URLs oder Hash-Werte

Eine Bibliothek gibt in der Regel kompatible Einschränkungen sowie Tests für einen bestimmten Bereich bereit. Eine Anwendung speichert üblicherweise das Lockfile des Umgebungsmanagers. Genaue Versionsangaben sollten im finalen Deployment-Artefakt enthalten sein und nicht einfach blind in den öffentlichen Metadaten der Bibliothek.

Erstellen Sie eine Anforderungsdatei, wenn eine Integration das Format oder bestimmte Funktionen von pip erfordert. Falls ein von uv verwaltetes Projekt eine solche Datei benötigt, exportieren Sie sie aus dem lock-Datei statt zwei unabhängige Abhängigkeitsmengen zu pflegen:

uv export --format requirements.txt --output-file requirements.txt

Die Exportdatei stellt ein kompatibilitätsorientiertes Ausgabeformat dar. Der ursprüngliche Quellcode bleibt weiterhin die Grundlage für Workflow.

Eine Migration, die das Verhalten beibehält

Ein Migrationsprozess, bei dem zunächst die Konformität überprüft wird, auf pyproject.toml.

Beginnen Sie nicht damit, zu löschen. setup.py oder requirements.txt. Klassifizieren Sie zunächst, welche Aufgabe jede der vorhandenen Dateien erfüllt.

  1. Logik zur Erstellung des Inventars, Metadaten, Anforderungen an Runtime, Zusatzkomponenten, Entwicklungsumgebungen, Tool-Einstellungen, Paketdaten sowie Eingangspunkte.
  2. Auswahl eines Backend, der die aktuelle Dateiinklusion, kompilierte Artefakte sowie editierbare Installationen wiedergeben kann.
  3. Verschiebung der statischen, veröffentlichten Metadaten in [project]; Stellen Sie wirklich dynamische Felder ausdrücklich dar.
  4. Verschieben Sie Anforderungen, die ausschließlich für Mitwirkende gelten, in [dependency-groups], keine öffentlichen Zusatzdateien.
  5. Verschieben Sie die Werkzeugeinstellungen nur dorthin, wo das Werkzeug eine äquivalente Semantik unterstützt.
  6. Erstellen Sie sowohl einen sdist als auch ein Wheel, überprüfen Sie deren Inhalt und installieren Sie das Wheel in einer sauberen Umgebung.
  7. Führen Sie die Eingangspunkte, Tests, Import-Prüfungen sowie den eigentlichen Deployment-Pfad aus.
  8. Löschen Sie alte Konfigurationen erst, nachdem Artefakte und Verhalten übereinstimmen.

MANIFEST.in kann bei einigen setuptools-Layouts weiterhin erforderlich sein, sowie ein kleiner setup.py Er kann weiterhin für das programmgesteuerte Build-Verhalten gültig bleiben. Modernisierung bedeutet lediglich einen Wechsel des Eigentümers, nicht einen Wettbewerb um die Löschung von Dateien.

Ein aktueller uv Workflow

uv unterscheidet bei der Erstellung eines Projekts zwischen Anwendungen und Bibliotheken:

# Non-library application template
uv init weather-app

# Packaged library with a src layout and build system
uv init --lib weather-client

uv init erstellt Projektdateien. Die erste Projektoperation wie zum Beispiel uv run, uv syncoder uv lock erstellt die Sperrdatei sowie den persistenten Speicher. .venv je nach Bedarf.

cd weather-client
uv add httpx
uv add --group test pytest pytest-cov
uv run pytest
uv build

UV ordnet derzeit die Anforderungen an die lokale Entwicklung in standardisierte Abhängigkeitsgruppen ein. Seine eingebettete uv_build Backend ist eine Option für rein-Python-Projekte; kompilierte Erweiterungen erfordern hingegen eine geeignete Alternative wie maturin oder scikit-build-core.

Fazit

pyproject.toml Es ist klar, wenn jede Tabelle ein bestimmtes Publikum hat. Die Implementierung von Isolation gehört dazu. [build-system]. Das veröffentlichte Verhalten gehört zu [project]. Die Entwicklerumgebungen gehören zu [dependency-groups]. Werkzeugwechsler gehören zum Werkzeug, das sie definiert.

Sobald diese Grenzen stabil sind, lässt sich die Datei leichter überprüfen – und Migrationsvorgänge verwechseln nicht mehr die Paketmetadaten mit der auf einer bestimmten Maschine bereitgestellten, finalen Umgebungskonfiguration.

Referenzen