Budowanie paczek Pythonowych
Do konfiguracji projektu i jego budowania używamy pliku pyproject.toml
, ponieważ staje się on de facto standardem dla projektów Pythonowych.
Jest o tym trochę w PEP-518, PEP-621, oraz PEP-660.
Sporo narzędzi preferuje konfigurację w pyproject.toml
(black
, isort
, ruff
, pytest
…).
Podstawowy plik pyproject.toml
Konfiguracje w tym formacie zawierają kilka istotnych sekcji:
- Sekcja
[build-system]
określa jaki backend zostanie użyty do budowania paczki jaksetuptools
czyhatch
. - Sekcja
[project]
zawiera podstawowe metadane o projekcie, nazwę, wersję, linki, zależności, autorów i opis. - Sekcja
[tool]
często występuje wielokrotnie z dodaną nazwą narzędzia, którego dotyczy. Jest to coś w rodzaju konfiguracji per biblioteka. Przykładem jest chociażby[tool.hatch]
,[tool.black]
czy[tool.mypy]
. Co w tych sekcjach umieszczać zależy od konkretnych narzędzi i powinno być opisane w ich dokumentacji 😉.
Poniżej przykładowa konfiguracja projektu oparta o setuptools
. Uwaga, wymagana wersja to 61.0
ze względu na wprowadzone wsparcie plików .toml
-> notka z setuptools.
Przykładowy plik pyproject.toml
dla fikcyjnego projektu snakes
.
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "snakes"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"requests",
"Django>=4.2",
"pydantic>=2.5",
]
dynamic = ["version"]
[tool.setuptools.dynamic]
version = {attr = "snakes.VERSION"}
W wyżej wylistowanym pliku widzimy kilka podstawowych sekcji, jak nazwa, wymagana wersja interpretera oraz zależności. Więcej informacji o dostepnych sekcjach na https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration.
Wpis dynamic
pozwala nam na pobieranie wersji projektu dynamicznie, to jest w czasie budowania paczki. Wymaga to podania w sekcji [tool.setuptools.dynamic]
skąd pobrac informację. W przykładowym wpisie "my_package.VERSION"
będzie oznaczało odczyt zmiennej w kontekście pakietu snakes
. Czyli jeśli do snakes/__init__.py
wrzucimy VERSION = "0.1.0"
, to setuptoolsy zaciągną tę zmienną i ustawią wersję naszej budowanej paczki na 0.1.0
. Jednocześnie w innych miejscach projektu możemy VERSION
zaimportować, żeby zachować spójność, np gdy wyświetlamy wersję w stopce strony czy panelu admina.
Zależności opcjonalne
Na podstawie dokumentacji zależności opcjonalnych można dopisać do naszego pliku następującą sekcję:
...
[project.optional-dependencies]
cli = [
"rich",
"click",
]
Dla takiej konfiguracji, gdy wykonamy pip install snakes[cli]
podane paczki rich
oraz click
zostaną doinstalowane jako zależności. Jest to przydatne gdy nie chcemy zawsze instalować pewnych elementów, jak na przykład biblioteki obsługi różnych baz danych lub jak elementy GUI/CLI.
Niestandardowa struktura projektu
Domyślnie setuptools
wykonują “automatyczne skanowanie” projektu. W praktyce wygląda to jednak tak, że o ile nie mamy jednego katalogu jak src
, to automat może nie zadziałać. W innym przypadku mamy katalogi ze skryptami, których nie chcemy “pakować” do paczki (w rozumieniu dystrybucji z pakietem np na PyPi).
Ze względu na powyższe przydatne może być podanie dokładnie co pakować, a czego nie. W niektórych przypadkach polecenie budowanie zakończy się błędem bez takiej konfiguracji.
...
[tool.setuptools.packages.find]
where = ["snakes"] # root katalog projektu, w którym są wszystkie exportowalne elementy
include = ["pkg*"] # lub: `exclude = ["additional*"]`
taki plik załączy snakes/pkg*
do dystrybuowanej paczki, czyli w poniższym przypadki snakes/pkg1
oraz snakes/pkg2
.
include
oraz exclude
używają stringów w formacie glob pattern
. Oznacza to, że przykładowy wpis util
zaimportuje paczkę o tej nazwie, ale już nie moduły w niej umieszczone. Jeśli chcemy wszystko z paczki o jakiejś nazwie, trzeba podać pełne ścieżki, bądź wildcard paczka*
.
├── pyproject.toml # AND/OR setup.cfg, setup.py
└── snakes
├── pkg1
│ └── __init__.py
├── pkg2
│ └── __init__.py
├── additional
│ └── __init__.py
└── tests
└── __init__.py
Budujemy 🔨
Zalecanym przez dokumentację setuptools
narzędziem jest build
, który pod spodem wykorzystuje setuptools
.
https://setuptools.pypa.io/en/latest/userguide/quickstart.html
pip install --upgrade build
Jeśli nasza konfiguracja jest poprawna, to polecenie
python -m build
powinno utworzyć paczki .tar.gz
oraz .wheel
w katalogu dist
. Te paczki można wgrać czy to na PyPi, cyz do naszego lokalnego repozytorium paczek Pythonowych 😄.
Źródła
- Słowniczek pojęć: https://packaging.python.org/en/latest/glossary/
- https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml
- https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
🐍