Programování

Balíčkování Python projektu: pyproject.toml krok za krokem

Jak zabalit Python knihovnu nebo CLI nástroj – src layout, pyproject.toml, build backend, závislosti, vstupní body, wheel a publikace na PyPI.

12. 10. 2026 · 4 min čtení

setup.py je minulost. Moderní Python projekt popisuje jediný soubor pyproject.toml podle standardů PEP 517, 518 a 621. Ukážeme si, jak z několika modulů udělat instalovatelný balíček, který jde nasadit přes pip install a případně publikovat na PyPI.

Struktura projektu

Doporučovaná je tzv. src layout:

prevodnik/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── prevodnik/
│       ├── __init__.py
│       ├── jednotky.py
│       └── cli.py
└── tests/
    └── test_jednotky.py

Proč src/? Testy pak nemohou omylem importovat kód z pracovní složky místo nainstalovaného balíčku. Chyby typu „u mě to funguje, po instalaci chybí soubor“ se objeví hned.

Minimální pyproject.toml

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

[project]
name = "prevodnik"
version = "0.1.0"
description = "Převody jednotek z příkazové řádky"
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
authors = [{ name = "Jan Novák", email = "[email protected]" }]
dependencies = [
    "typer>=0.12",
]

[project.optional-dependencies]
dev = ["pytest>=8", "mypy"]

[project.scripts]
prevodnik = "prevodnik.cli:app"

[project.urls]
Homepage = "https://github.com/jannovak/prevodnik"

Sekce po sekcích

  • [build-system] říká, čím se balíček sestaví. Hatchling je jednoduchý a rozumně výchozí. Alternativy: setuptools>=61 (backend setuptools.build_meta), flit_core, pdm-backend, uv_build. Hatchling i setuptools layout src/ najdou automaticky.
  • [project] jsou standardizovaná metadata (PEP 621) – stejná pro všechny backendy.
  • license = "MIT" jako SPDX výraz je novější forma (PEP 639). Starší verze backendů vyžadují tabulku license = { text = "MIT" } – pokud build hlásí chybu, aktualizujte backend nebo použijte starší zápis.
  • dependencies jsou závislosti potřebné za běhu.
  • optional-dependencies jsou volitelné skupiny, instalují se jako pip install "prevodnik[dev]".
  • [project.scripts] vytvoří po instalaci spustitelný příkaz prevodnik, který zavolá objekt app z modulu prevodnik.cli.

Jak psát závislosti

dependencies = [
    "httpx>=0.27,<1",
    "pydantic>=2.5",
    "tomli>=2; python_version < '3.11'",
]

U knihovny zadávejte co nejvolnější rozsahy – spodní hranici, kterou opravdu potřebujete. Přesné verze (==) by kolidovaly s jinými balíčky v prostředí uživatele.

U aplikace, kterou nasazujete, přesné verze patří do zamykacího souboru (uv.lock, requirements.txt z pip-compile), ne do pyproject.toml.

Poslední řádek ukazuje environment marker: tomli se nainstaluje jen na Pythonu starším než 3.11, kde chybí vestavěný tomllib.

Verze na jednom místě

Chcete číst verzi za běhu? Nepište ji do kódu podruhé:

# src/prevodnik/__init__.py
from importlib.metadata import version
__version__ = version("prevodnik")

Vývojová instalace

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

-e (editable) znamená, že změny v src/ se projeví hned bez reinstalace. Příkaz prevodnik už funguje v aktivním prostředí.

S uv:

uv sync --extra dev
uv run prevodnik --help

Konfigurace nástrojů

Do pyproject.toml patří i nastavení většiny vývojových nástrojů:

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

[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]

[tool.mypy]
strict = true

Sestavení

pip install build
python -m build

Ve složce dist/ vzniknou dva soubory:

  • prevodnik-0.1.0.tar.gz – zdrojová distribuce (sdist),
  • prevodnik-0.1.0-py3-none-any.whl – wheel, hotový k instalaci.

Před publikací ověřte, co ve wheelu skutečně je:

unzip -l dist/prevodnik-0.1.0-py3-none-any.whl
pip install dist/prevodnik-0.1.0-py3-none-any.whl   # do čistého venv

Datové soubory (šablony, JSON) uvnitř balíčku čtěte přes importlib.resources.files("prevodnik") / "data.json", ne přes relativní cesty k __file__.

Publikace na PyPI

  1. Vytvořte si účet na test.pypi.org a pypi.org, zapněte 2FA a vygenerujte API token.
  2. Ověřte, že jméno balíčku je volné.
  3. Nahrajte nejdřív na TestPyPI:
pip install twine
twine check dist/*
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ prevodnik
  1. Když je vše v pořádku, twine upload dist/*.

Pro automatické publikování z GitHub Actions doporučuje PyPI tzv. Trusted Publishing (OIDC) – v CI pak nemusíte ukládat žádný token.

Alternativně: uv build a uv publish dělají totéž bez dalších nástrojů.

Časté chyby

  • ModuleNotFoundError po instalaci – modul není uvnitř balíčku ve src/ nebo chybí __init__.py.
  • Nahrání stejné verze podruhé – PyPI to odmítne. Každá publikace potřebuje nové číslo verze a smazanou verzi nelze znovu použít.
  • Zapomenutý README v sdistu – readme v [project] musí ukazovat na existující soubor.

Pokud máte projekt na GitHubu a chcete ho převést ze setup.py na pyproject.toml, můžete ho naimportovat do Projektu v ColdHost AI, nechat úpravu navrhnout a výsledek poslat zpět jako pull request ke kontrole.