[!NOTE] Traducción automática Este artículo se tradujo automáticamente a partir de la versión original en inglés.
pyproject.toml Guía: Empaquetado en Python, dependencias y configuración de herramientas
Los proyectos antiguos en Python suelen distribuir la configuración por todo el código. setup.py, setup.cfg, archivos de requisitos, MANIFEST.in, y archivos separados para las herramientas de desarrollo. pyproject.toml
Esta guía explica el archivo desde la configuración de compilación hasta los metadatos del proyecto y los parámetros de las herramientas. La forma más sencilla de mantenerla comprensible es asignar cuatro responsables distintos: quien compila el proyecto, lo que publica el proyecto, lo que necesitan los colaboradores a nivel local, y qué herramientas individuales realizan la configuración.
TL;DR. Utilice
[build-system]para el backend que construye una distribución,[project]para los metadatos publicados y los requisitos de runtime.[dependency-groups]para entornos de desarrollo no publicados, y[tool.*]Solo donde indique la documentación de esa herramienta. Una declaración de dependencia no es un archivo de bloqueo.
Un archivo, cuatro propietarios
Tres normas de empaquetado definen la estructura básica:
- PEP 518 Define los requisitos del sistema de compilación. PEP 621 Define los metadatos del proyecto. PEP 735 Define grupos de dependencias no publicados.
Los autores de herramientas también pueden reclamar un espacio de nombres en la sección inferior. [tool]. Esa zona es convencional y no universal: cada herramienta define sus propias claves y comportamientos.
Se trata de una pequeña biblioteca empaquetada:
[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"
Cada lista de dependencias responde a una pregunta distinta. Esa distinción constituye el eje central del archivo.
[build-system]: cómo una fuente se convierte en una distribución
Una compilación frontend como python -m buildpip, uv o pipx invocan un proceso de compilación backend. El backend determina cómo el árbol de fuentes se convierte en un sdist o wheel, así como qué archivos formarán parte de dichos artefactos.
[build-system]
requires = ["hatchling>=1.26"]
build-backend = "hatchling.build"
requires Contiene las dependencias necesarias para ejecutar el backend en su entorno de compilación aislado. No forma parte de la lista de dependencias runtime del paquete.
Declarar un sistema de compilación cuando el proyecto genera una distribución o necesita que su propio código se instale como paquete. Un proyecto que no sea un paquete aún puede utilizarlo. pyproject.toml; PEP 735 incluso permite que un archivo contenga únicamente grupos de dependencias. Los gestores de entorno difieren en la forma en que tratan un proyecto sin sistema de compilación, por lo que es necesario tomar esa decisión de manera deliberada.
Elige el backend de los requisitos de compilación:
- Requisitos de diseño basado en Python puro y de selección de archivos
- Extensiones compiladas o sistemas de construcción externos
- Necesidades relacionadas con versiones dinámicas o archivos generados
- Comportamiento de instalación editable
- Grado de madurez backend en la versión pipeline
Copia la tabla recomendada actual de backend de su documentación. No añadas nada. wheel Para definir los requisitos de forma habitual, el backend especifica qué es lo que se necesita.
[project]: los consumidores de metadatos reciben
El [project] La tabla describe la distribución: su nombre, versión, compatibilidad con Python, las dependencias runtime, los puntos de entrada y otros metadatos del índice.
[project]
name = "weather-client"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["httpx>=0.27"]
Los especificadores de dependencias son restricciones para un resolutor, y no una captura de un entorno concreto. Forman parte de los metadatos de wheel y sdist, de modo que los instaladores posteriores pueden combinarlos con los requisitos de otros paquetes.
Los extras constituyen una interfaz de instalación pública.
[project.optional-dependencies] Define los extras que pueden solicitar los consumidores:
[project.optional-dependencies]
cli = ["rich>=13"]
postgres = ["psycopg[binary]>=3.2"]
Un usuario final puede instalar weather-client[cli]. Dado que se publican nombres y requisitos adicionales, trátelos como capacidades del producto. No utilice un nombre adicional dev Únicamente con el fin de alojar todas las herramientas destinadas a los colaboradores.
Los puntos de entrada vinculan los comandos instalados con Python
[project.scripts]
weather = "weather_client.cli:main"
Una vez instalada la distribución, el entorno expone weather, que importa y llama a weather_client.cli:main. Pruebe el comando desde un paquete precompilado (wheel), y no solo desde la raíz del repositorio; es este wheel el que reciben los usuarios.
[dependency-groups]: entornos locales, no publicados
Los grupos de dependencias PEP 735 permiten describir entornos de desarrollo o que no son paquetes, sin necesidad de publicarlos como metadatos de paquete.
[dependency-groups]
test = ["pytest>=8", "pytest-cov>=5"]
lint = ["ruff>=0.12"]
dev = [
{ include-group = "test" },
{ include-group = "lint" },
]
Este es el límite adecuado para pruebas, herramientas de linting, generadores de documentación y otros instrumentos similares destinados a los colaboradores. También resulta útil para aplicaciones o cuadernos de notas que no generan una distribución.
Los grupos de dependencias son datos estandarizados, pero las interfaces de instalación siguen variando. Verifique cómo el gestor de entornos seleccionado los instala, bloquea y resuelve. PEP 735 no define una interfaz de línea de comandos universal.
[tool.*]: configuración propiedad de una herramienta específica
Las tablas de herramientas no comparten un esquema:
[tool.pytest.ini_options]
addopts = "-ra"
testpaths = ["tests"]
[tool.ruff]
line-length = 100
Utilizar un [tool.*] La tabla solo se incluye si la herramienta lo documenta. Algunas configuraciones siguen estando en archivos separados porque otro ecosistema las consume, porque el archivo requiere un formato distinto, o porque dicha configuración resulta más clara por sí sola. pyproject.toml Se trata de un punto de coordinación, y no de una orden para centralizar todo.
Las declaraciones, los bloqueos y los archivos de requisitos resuelven problemas distintos
La causa más frecuente de confusión es tratar cada artefacto de dependencia como una fuente de verdad competidora.
| Artefacto | Propósito principal | Contenidos típicos |
|---|---|---|
[project.dependencies] | Publicado el contrato runtime | Requisitos directos y rangos compatibles |
[project.optional-dependencies] | Funcionalidades publicadas con opción de activación | Extras dirigidos al usuario final |
[dependency-groups] | Entornos locales no publicados | Grupos de pruebas, análisis de código, documentación o aplicaciones |
| Archivo de bloqueo | Reproducir un entorno resuelto | Versiones exactas, fuentes y metadatos de resolución |
requirements.txt | Entrada de instalación compatible con pip | Requisitos, además de opciones específicas de pip, restricciones, URL o hashes |
Una biblioteca suele publicar restricciones y pruebas compatibles para un rango determinado. Una aplicación, por lo general, guarda el archivo de bloqueo del gestor de entornos. Los valores exactos de las versiones deben incluirse en el artefacto de despliegue finalizado, y no de forma arbitraria en los metadatos públicos de la biblioteca.
Conserva un archivo de requisitos cuando una integración requiera el formato o funcionalidades de pip. Si un proyecto gestionado con uv necesita uno, expórtalo desde el archivo lock en lugar de mantener dos conjuntos de dependencias independientes:
uv export --format requirements.txt --output-file requirements.txt
La exportación corresponde a un resultado de compatibilidad derivado. El bloqueo sigue siendo la versión original resuelta para dicho flujo de trabajo.
Una migración que mantiene el comportamiento existente
No comience eliminando. setup.py o requirements.txt. Primero, clasifica la función de cada archivo existente.
- Lógica de construcción del inventario, metadatos, requisitos de runtime, elementos adicionales, entornos de desarrollo, configuraciones de herramientas, datos de paquetes y puntos de entrada.
- Elegir un backend capaz de reproducir la inclusión actual de archivos, los artefactos compilados y las instalaciones editables.
- Mover el metadato publicado estático a
[project]; mantenga explícitos los campos verdaderamente dinámicos. - Mueva los requisitos exclusivos para colaboradores a
[dependency-groups], sin elementos adicionales públicos. - Mover la configuración de la herramienta únicamente allí donde esta admita semánticas equivalentes.
- Generar tanto un sdist como un wheel, inspeccionar su contenido e instalar el wheel en un entorno limpio.
- Ejecutar los puntos de entrada, las pruebas, las comprobaciones de importación y el proceso real de despliegue.
- Eliminar la configuración antigua únicamente después de que los artefactos y el comportamiento coincidan.
MANIFEST.in Todavía podría ser necesario en algunos diseños de setuptools, y una pequeña cantidad setup.py Puede seguir siendo válido para el comportamiento de compilación programática. La modernización consiste en un cambio de propietario, y no en una competencia por eliminar archivos.
Un flujo de trabajo actual para UV
uv distingue entre aplicaciones y bibliotecas al crear un proyecto:
# Non-library application template
uv init weather-app
# Packaged library with a src layout and build system
uv init --lib weather-client
uv init Crea los archivos del proyecto. La primera operación del proyecto, como por ejemplo uv run, uv sync, o bien uv lock crea el archivo de bloqueo y la persistencia .venv según sea necesario.
cd weather-client
uv add httpx
uv add --group test pytest pytest-cov
uv run pytest
uv build
UV actualmente agrupa los requisitos de desarrollo local en grupos de dependencias estandarizados. Su implementación nativa uv_build backend representa una opción para proyectos puramente en Python; las extensiones compiladas requieren una alternativa adecuada como maturin o scikit-build-core.
Conclusión
pyproject.toml Queda claro cuando cada tabla cuenta con un único público objetivo. La implementación de aislamiento corresponde a [build-system]. El comportamiento publicado corresponde a [project]. Los entornos de los colaboradores pertenecen a [dependency-groups]. Los conmutadores de herramienta pertenecen a la herramienta que los define.
Una vez que esas fronteras se estabilizan, el archivo resulta más fácil de revisar, y las migraciones dejan de confundir los metadatos del paquete con el entorno resuelto de una máquina concreta.