Padronização de pyproject.toml¶
Este documento define as convenções adotadas no pyproject.toml dos pacotes Quantilica. Cada pacote é um repositório git independente, publicado standalone no PyPI (ver Publicação e Release) — portanto o pyproject.toml de cada um é auto-contido.
1. Metadados de distribuição (pacotes publicáveis)¶
Todo pacote publicável declara:
[project]
license = "MIT" # PEP 639 (expressão SPDX)
license-files = ["LICENSE"]
classifiers = [
# ... sem "License :: OSI Approved :: ..." (redundante sob PEP 639)
"Typing :: Typed", # se o pacote envia py.typed (PEP 561)
]
[build-system]
requires = ["hatchling>=1.27"] # >=1.27 para suporte a PEP 639
build-backend = "hatchling.build"
- Licença — PEP 639: use a expressão SPDX (
license = "MIT") +license-files, não a forma antigalicense = { file = "LICENSE" }nem o classifierLicense :: OSI Approved :: MIT License. Requerhatchling>=1.27. - Tipagem — PEP 561: um pacote tipado envia um arquivo marcador
py.typed(vazio) emsrc/<pacote>/py.typede declara o classifierTyping :: Typed. Sem o marcador, consumidores com mypy/pyright não enxergam os tipos. Os dois andam juntos: ou tem ambos, ou nenhum.
2. Dependências de desenvolvimento — [dependency-groups]¶
Use sempre [dependency-groups] (PEP 735) para declarar dependências de desenvolvimento. Não use [project.optional-dependencies] para esse fim.
[dependency-groups]
dev = [
"pytest>=8.0",
"ruff>=0.9.0",
]
Por que não [project.optional-dependencies]?¶
[project.optional-dependencies] é projetado para extras de runtime instaláveis pelo usuário final (ex: pip install pacote[postgres]). Usar esse mecanismo para dependências de desenvolvimento é um abuso semântico — esses extras aparecem nos metadados publicados do pacote e não fazem sentido fora do contexto de desenvolvimento.
[dependency-groups] foi introduzido exatamente para separar esse caso: dependências que só existem durante o desenvolvimento e não integram os metadados de distribuição.
Instalando o grupo de desenvolvimento¶
# workspace (padrão — instala tudo incluindo dev)
uv sync --all-packages
# pacote isolado
uv sync --dev
Quando [project.optional-dependencies] é correto¶
Use [project.optional-dependencies] somente para extras que o usuário pode instalar opcionalmente em produção:
[project.optional-dependencies]
cli = ["typer>=0.12", "rich>=13"]
analysis = ["quantilica-analytics>=0.2.0"]
Exemplos corretos:
- quantilica-core expõe quantilica-core[cli] para habilitar helpers de Rich/Typer nos hosts que precisam deles.
- Fetchers usam a tag analysis para dependências analíticas pesadas (como quantilica-analytics, polars ou pandas), já que fetchers são focados primariamente em extração leve. Subcomandos analíticos (como convert ou pipeline) devem tratar a ausência dessas dependências graciosamente.
3. Configuração do ruff¶
Como cada pacote é um repositório independente publicado standalone, a configuração do ruff vive no próprio pyproject.toml — para que ruff check/ruff format --check (rodados pelo CI test.yml dentro do repo) apliquem as regras sem depender de um arquivo externo do workspace.
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]
# Overrides locais, quando necessário:
[tool.ruff.lint.per-file-ignores]
"src/meu_pacote/cli.py" = ["B008"] # typer.Option/Argument como default é idiomático
"tests/test_algo.py" = ["E402"] # imports fora do topo por necessidade de mock
line-length = 88,select = ["E", "F", "I", "UP", "B"]— valores canônicos do ecossistema.- O
ruff.tomlna raiz do workspace serve à conveniência de rodar o linter a partir da raiz durante o desenvolvimento; ele não substitui o[tool.ruff]de cada pacote, que é o que acompanha o pacote publicado.
4. Configuração do pytest¶
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = ["-p", "no:cacheprovider"]