This skill should be used when the user is configuring pyproject.toml, choosing a build backend, declaring dependencies, setting up dependency groups, configuring tool settings, defining entry points, or managing package versioning. Covers PEP 621 metadata, hatchling vs setuptools vs flit-core vs maturin, PEP 735 dependency groups, PEP 639 SPDX licenses, dynamic versioning with hatch-vcs, dependency version constraints, console scripts, and tool configuration consolidation.
Instalación
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
This skill should be used when the user is configuring pyproject.toml, choosing a build backend, declaring dependencies, setting up dependency groups, configuring tool settings, defining entry points, or managing package versioning. Covers PEP 621 metadata, hatchling vs setuptools vs flit-core vs maturin, PEP 735 dependency groups, PEP 639 SPDX licenses, dynamic versioning with hatch-vcs, dependency version constraints, console scripts, and tool configuration consolidation.
version
1.0.0
Configure Everything in pyproject.toml
pyproject.toml is the single source of truth for a Python package: build system, metadata, dependencies, and tool configuration. As of pip 24.0, setup.py install is no longer supported. Every major package -- Pydantic, httpx, Flask, pytest, attrs, Ruff -- has migrated. There is no reason to use setup.py, setup.cfg, or requirements.txt as primary configuration for any new package.
Anatomy: The Four Sections
Every pyproject.toml contains four logical sections. Keep them in this order:
[build-system] -- declares the build backend (PEP 517/518)
Use hatchling for new pure-Python packages. It is PyPA-maintained, fast, extensible, and the default for hatch new and uv init --lib.
Your Package
Use This Backend
Declaration
Pure Python (new)
hatchling
requires = ["hatchling"]
Pure Python (very simple)
flit-core
requires = ["flit_core>=3.10,<4"]
Pure Python (legacy)
setuptools
requires = ["setuptools>=75.0"]
Rust extensions (PyO3)
maturin
requires = ["maturin>=1.7,<2.0"]
C/C++ with CMake
scikit-build-core
requires = ["scikit-build-core>=0.10"]
What top packages use:
Package
Backend
Reason
Pydantic, httpx, Black, attrs
hatchling
Modern, clean, PyPA-aligned
Flask, Jinja2, Werkzeug
flit-core
Simple, minimal needs
pytest
setuptools
Historical, complex build
Polars, Ruff, pydantic-core
maturin
Rust (PyO3) bindings
PEP 621: Project Metadata
Only name and version are strictly required (unless version is listed in dynamic). For any public package, always set these recommended fields:
Field
Purpose
Example
name
PyPI distribution name (kebab-case)
"my-library"
version
PEP 440 version (or list in dynamic)
"1.0.0"
description
Single-line summary
"A fast HTTP client"
readme
PyPI long description
"README.md"
license
PEP 639 SPDX expression
"MIT"
requires-python
Minimum supported Python
">=3.10"
authors
Author list
[{ name = "Jane Doe" }]
classifiers
Trove classifiers (include Typing :: Typed)
See Reference Configuration
dependencies
Runtime dependencies (lower bounds only)
["httpx>=0.27"]
Set [project.urls] with at minimum Repository and Changelog -- PyPI renders recognized keys (Documentation, Issues, Funding) with icons. See the Reference Configuration for a complete example.
PEP 639: SPDX License Expressions
Use a plain SPDX string, not the legacy table form. All modern build backends and PyPI support this since 2024.
Do
Do Not
license = "MIT"
license = { text = "MIT License" }
license = "Apache-2.0"
license = { file = "LICENSE" }
license = "MIT OR Apache-2.0"
license = "BSD" (ambiguous -- which BSD?)
PEP 735: Dependency Groups
Use [dependency-groups] for dev-only dependencies. These are not published to PyPI and cannot be installed by end users.
[project.optional-dependencies]# Published to PyPI -- user-facing feature variantspostgres = ["asyncpg>=0.29"]
[dependency-groups]# NOT published -- developer-onlytest = ["pytest>=8.0", "pytest-cov>=6.0", "coverage[toml]>=7.6"]
lint = ["ruff>=0.9", "mypy>=1.14", "pyright>=1.1"]
dev = [{ include-group = "test" }, { include-group = "lint" }, "pre-commit>=4.0"]
User-facing feature variants (postgres, redis, HTTP/2) go in [project.optional-dependencies]. Dev-only tools (test, lint, docs) go in [dependency-groups]. Groups support { include-group = "name" } for composition. Install with uv sync --group dev or pip install --dependency-group test.
Dynamic Versioning
Derive the version from git tags to maintain a single source of truth. No version strings to update manually.
Consolidate all tool settings into [tool.*] sections of pyproject.toml where the tool supports it. Do not create .flake8, mypy.ini, pytest.ini, or .coveragerc -- Ruff, mypy, pytest, and coverage all read from pyproject.toml natively. Ruff replaces flake8, isort, Black, and pyupgrade as a single tool.
Exceptions: Tools that do not support pyproject.toml configuration keep their own files. The most common exception is .pre-commit-config.yaml -- pre-commit is a polyglot tool managing hooks across languages and requires its own YAML format.
The Reference Configuration below shows all recommended [tool.*] sections.
Reference Configuration
A complete, production-ready pyproject.toml for a new pure-Python package: