[!NOTE] Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.
pyproject.toml Guide : Emballage Python, dépendances et configuration des outils
Les projets Python plus anciens ont souvent tendance à disperser les configurations un peu partout. setup.py, setup.cfg, fichiers de spécifications, MANIFEST.in, ainsi que des fichiers distincts pour les outils de développement. pyproject.toml Il offre un cadre commun pour aborder ces problématiques, sans pour autant remplacer chaque fichier de projet ni intégrer chaque section au sein d’un standard unique.
Ce guide décrit le fichier en détaillant son utilisation, allant de la configuration de compilation jusqu’aux métadonnées du projet et aux paramètres des outils. La manière la plus simple de le rendre compréhensible consiste à assigner quatre responsables distincts : celui qui compile le projet, celui qui gère ce que le projet publie, celui dont les contributeurs ont besoin localement, et celui qui configure les différents outils.
En résumé. Utilisez
[build-system]pour le backend qui construit une distribution,[project]pour les métadonnées publiées ainsi que les exigences relatives à runtime.[dependency-groups]pour les environnements de développement non publiés, et[tool.*]Uniquement là où la documentation de cet outil l’indique. Une déclaration de dépendance n’est pas un fichier de verrouillage.
Un fichier, quatre propriétaires
Trois normes d’emballage ont défini la structure de base :
- PEP 518 Définit les exigences du système de construction. PEP 621 définit les métadonnées du projet. PEP 735 définit des groupes de dépendances non publiés.
Les auteurs d’outils peuvent également réclamer un espace de noms en dessous. [tool]. Cette zone est conventionnelle et non universelle : chaque outil définit ses propres clés ainsi que son propre comportement.
Voici une petite bibliothèque emballée :
[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"
Chaque liste de dépendances répond à une question distincte. Cette distinction constitue l’essence même du fichier.
[build-system]: comment une source devient une distribution
Une compilation frontend telle que python -m buildpip, uv ou encore pipenv lancent une étape de compilation backend. Le backend définit la manière dont l’arbre de sources est transformé en un fichier sdist ou wheel, ainsi quels fichiers sont inclus dans ces artefacts.
[build-system]
requires = ["hatchling>=1.26"]
build-backend = "hatchling.build"
requires Il contient les dépendances nécessaires pour exécuter le backend dans son environnement de construction isolé. Il ne fait pas partie de la liste de dépendances runtime de votre paquet.
Déclarez un système de construction lorsque le projet génère une distribution ou nécessite que son propre code soit installé en tant que paquet. Un projet qui n’est pas un paquet peut néanmoins en utiliser un. pyproject.toml; Le PEP 735 autorise même l’existence d’un fichier ne contenant que des groupes de dépendances. Les gestionnaires d’environnement diffèrent quant à la manière dont ils traitent un projet ne disposant pas de système de construction ; il convient donc de faire ce choix en toute conscience.
Sélectionnez le backend parmi les exigences de construction :
- Besoins en matière de structure basée sur Python pur et de sélection de fichiers
- Extensions compilées ou systèmes de construction externes
- Exigences liées à des versions dynamiques ou à des fichiers générés Comportement de l’installation éditable
- Maturité de backend dans la version pipeline
Copiez le tableau recommandé actuel de backend dans sa documentation. Ne rien ajouter wheel Élaborer les exigences de manière systématique ; le backend définit ce dont il a besoin.
[project]: ce que reçoivent les consommateurs de métadonnées
Le [project] Le tableau décrit la distribution : son nom, sa version, sa compatibilité avec Python, les dépendances runtime, les points d’entrée ainsi que d’autres métadonnées d’index.
[project]
name = "weather-client"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["httpx>=0.27"]
Les spécificateurs de dépendances constituent des contraintes destinées à un résolveur, et non une capture d’un environnement donné. Ils font partie des métadonnées générées par les fichiers wheel et sdist, ce qui permet aux outils d’installation ultérieurs de les combiner avec les exigences provenant d’autres paquets.
Les extras constituent une interface d’installation publique
[project.optional-dependencies] définit les extras que les consommateurs peuvent demander :
[project.optional-dependencies]
cli = ["rich>=13"]
postgres = ["psycopg[binary]>=3.2"]
Un utilisateur final peut installer weather-client[cli]. Étant donné que des noms et des exigences supplémentaires sont publiés, traitez-les comme des fonctionnalités du produit. Ne pas utiliser de nom supplémentaire dev uniquement afin de stocker les outils destinés à chaque contributeur.
Les points d’entrée relient les commandes installées au Python
[project.scripts]
weather = "weather_client.cli:main"
Une fois la distribution installée, l’environnement expose weather, qui importe et appelle weather_client.cli:main. Testez la commande à partir d’un paquet compilé, et non seulement depuis la racine du répertoire de dépôt ; c’est bien ce paquet qui est distribué aux utilisateurs.
[dependency-groups]: environnements locaux non publiés
Les groupes de dépendances PEP 735 permettent de décrire des environnements de développement ou des environnements hors paquet sans avoir à les publier en tant que métadonnées de paquet.
[dependency-groups]
test = ["pytest>=8", "pytest-cov>=5"]
lint = ["ruff>=0.12"]
dev = [
{ include-group = "test" },
{ include-group = "lint" },
]
Il s’agit de la limite adéquate pour les tests, les analyseurs de syntaxe, les générateurs de documentation ainsi que d’autres outils destinés aux contributeurs. Elle est également utile pour les applications ou les notebooks qui ne génèrent pas de distribution.
Les groupes de dépendances constituent des données normalisées, mais les interfaces d’installation restent variables. Vérifiez comment le gestionnaire d’environnement sélectionné les installe, les verrouille et les résout. La norme PEP 735 ne définit pas d’interface en ligne de commande universelle.
[tool.*]: configuration gérée par un seul outil
Les tables d’outils ne partagent pas de schéma :
[tool.pytest.ini_options]
addopts = "-ra"
testpaths = ["tests"]
[tool.ruff]
line-length = 100
Utiliser un [tool.*] La mise en forme en tableau n’est applicable que si la documentation de l’outil le prévoit. Certaines configurations restent dans des fichiers distincts, soit parce qu’un autre écosystème les utilise, soit parce que le fichier requiert un format différent, soit encore parce que la description de ces paramètres est plus claire lorsqu’elles se trouvent séparément. pyproject.toml Il s’agit d’un point de coordination, et non d’une obligation de centraliser tout le système.
Les déclarations, les verrous et les fichiers de spécifications résolvent des problèmes distincts
La source de confusion la plus fréquente est de considérer chaque artefact de dépendance comme une source de vérité concurrente.
| Artefact | But principal | Contenus typiques |
|---|---|---|
[project.dependencies] | Contrat runtime publié | Exigences directes et plages compatibles |
[project.optional-dependencies] | Fonctionnalités publiées avec consentement explicite | Fonctionnalités supplémentaires destinées aux utilisateurs finaux |
[dependency-groups] | Environnements locaux non publiés | Tests, vérifications de conformité, documentation ou groupes d’applications |
| Fichier de verrouillage | Reproduire un environnement résolu | Les versions exactes, les sources ainsi que les métadonnées de résolution |
requirements.txt | Entrée d’installation compatible avec pip | Les exigences ainsi que les options spécifiques à pip, les contraintes, les URL ou les hachages |
Une bibliothèque publie généralement des contraintes et des tests compatibles pour une plage donnée. Une application, quant à elle, enregistre habituellement le fichier de verrouillage du gestionnaire d’environnement. Les valeurs précises de pin doivent figurer dans l’artefact de déploiement final, et non de manière arbitraire dans les métadonnées publiques de la bibliothèque.
Conservez un fichier de spécifications lorsque l’intégration exige le format ou certaines fonctionnalités de pip. Si un projet géré par uv en a besoin, exportez-le depuis le fichier lock plutôt que de maintenir deux ensembles d’dependencies indépendants :
uv export --format requirements.txt --output-file requirements.txt
L’export correspond à une sortie de compatibilité dérivée. Le verrou reste la version source résolue pour ce flux de travail.
Une migration qui préserve le comportement
Ne commencez pas par supprimer. setup.py ou requirements.txt. Tout d’abord, classer la fonction de chaque fichier existant.
- L’inventaire comprend la logique de construction, les métadonnées, les exigences runtime, les éléments supplémentaires, les environnements de développement, les paramètres des outils, les données des paquets ainsi que les points d’entrée.
- Choisir un backend capable de reproduire l’inclusion actuelle des fichiers, les artefacts compilés et les installations modifiables.
- Déplacer les métadonnées statiques publiées vers
[project]; veiller à ce que les champs véritablement dynamiques soient explicitement définis. - Déplacer les exigences réservées aux contributeurs dans
[dependency-groups], pas d’éléments supplémentaires non publics. - Déplacer les paramètres de l’outil uniquement là où celui-ci prend en charge une sémantique équivalente.
- Créer à la fois un sdist et un wheel, examiner leur contenu, puis installer le wheel dans un environnement propre.
- Exécuter les points d’entrée, les tests, les vérifications d’importation ainsi que le processus de déploiement réel.
- Supprimer l’ancienne configuration uniquement après que les artefacts et le comportement correspondent.
MANIFEST.in il peut encore être nécessaire avec certaines configurations de setuptools, ainsi qu’un petit setup.py Il peut rester valide pour le comportement de compilation programmatisée. La modernisation correspond à un changement de propriétaire, et non à une compétition visant à supprimer des fichiers.
Un flux de travail UV actuel
uv permet de distinguer les applications des bibliothèques lors de la création d’un projet :
# Non-library application template
uv init weather-app
# Packaged library with a src layout and build system
uv init --lib weather-client
uv init crée les fichiers de projet. La première opération sur le projet, telle que uv run, uv sync, ou uv lock crée le fichier de verrouillage et la persistance .venv selon les besoins.
cd weather-client
uv add httpx
uv add --group test pytest pytest-cov
uv run pytest
uv build
uv place actuellement les exigences de développement local dans des groupes de dépendances normalisés. Son interface native uv_build backend constitue une option pour les projets purement écrits en Python ; les extensions compilées exigent une alternative adaptée telle que maturin ou scikit-build-core.
Conclusion
pyproject.toml Cela devient clair lorsque chaque tableau cible un seul public. Il convient alors d’implémenter l’isolation propre à ce cas. [build-system]. Le comportement publié appartient à [project]. Les environnements des contributeurs appartiennent à [dependency-groups]. Les commutateurs d’outil appartiennent à l’outil qui les définit.
Une fois ces frontières stabilisées, le fichier devient plus facile à examiner, et les migrations cessent de confondre les métadonnées des paquets avec l’environnement résolu d’une machine donnée.