[!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

Los cuatro límites de propiedad en pyproject.toml

Tres normas de empaquetado definen la estructura básica:

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:

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.

Consumidores de configuración en torno a pyproject.toml

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.

ArtefactoPropósito principalContenidos típicos
[project.dependencies]Publicado el contrato runtimeRequisitos directos y rangos compatibles
[project.optional-dependencies]Funcionalidades publicadas con opción de activaciónExtras dirigidos al usuario final
[dependency-groups]Entornos locales no publicadosGrupos de pruebas, análisis de código, documentación o aplicaciones
Archivo de bloqueoReproducir un entorno resueltoVersiones exactas, fuentes y metadatos de resolución
requirements.txtEntrada de instalación compatible con pipRequisitos, 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

Una migración orientada a la verificación previa hacia pyproject.toml

No comience eliminando. setup.py o requirements.txt. Primero, clasifica la función de cada archivo existente.

  1. 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.
  2. Elegir un backend capaz de reproducir la inclusión actual de archivos, los artefactos compilados y las instalaciones editables.
  3. Mover el metadato publicado estático a [project]; mantenga explícitos los campos verdaderamente dinámicos.
  4. Mueva los requisitos exclusivos para colaboradores a [dependency-groups], sin elementos adicionales públicos.
  5. Mover la configuración de la herramienta únicamente allí donde esta admita semánticas equivalentes.
  6. Generar tanto un sdist como un wheel, inspeccionar su contenido e instalar el wheel en un entorno limpio.
  7. Ejecutar los puntos de entrada, las pruebas, las comprobaciones de importación y el proceso real de despliegue.
  8. 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.

Referencias