Nesta aula, você vai aprender a empacotar e distribuir seus projetos Python, transformando seu código em um pacote instalável que pode ser compartilhado com outras pessoas ou publicado no PyPI (Python Package Index). Esse processo é essencial para distribuir bibliotecas, ferramentas de linha de comando ou aplicações que precisam ser instaladas em outros ambientes.

Vamos explorar o arquivo pyproject.toml, que se tornou o padrão para configuração de pacotes, o processo de build com ferramentas como build e setuptools, uma visão geral da publicação no PyPI e as boas práticas de versionamento semântico. Ao final, você terá uma base sólida para publicar seus próprios pacotes.

pyproject.toml

O pyproject.toml é o arquivo de configuração central para empacotamento em Python moderno. Ele foi introduzido pela PEP 518 (2016) e se tornou obrigatório a partir da PEP 621 (2020) para definir metadados de pacotes. Ele substitui o antigo setup.py e setup.cfg em muitos aspectos, permitindo uma configuração declarativa e mais limpa.

Um pyproject.toml típico contém seções como [build-system], que especifica a ferramenta de build, e [project], que define os metadados do pacote (nome, versão, descrição, dependências, etc.). Veja um exemplo básico:

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "meu-pacote"
version = "0.1.0"
description = "Um pacote de exemplo"
readme = "README.md"
authors = [
    {name = "Seu Nome", email = "seu@email.com"}
]
license = {text = "MIT"}
requires-python = ">=3.8"
dependencies = [
    "requests>=2.25"
]

[tool.setuptools.packages.find]
include = ["meupacote*"]

A seção [build-system] indica que usaremos o setuptools como backend. O campo requires é usado pelo pip para preparar o ambiente de build. A seção [project] contém os metadados: nome, versão, descrição, autores, licença, versão mínima do Python e dependências. Para incluir pacotes, usamos [tool.setuptools.packages.find] para localizar automaticamente os pacotes no diretório. Outras ferramentas, como Poetry e Flit, também usam pyproject.toml, mas com formatos diferentes.

build

O processo de build transforma seu código-fonte em artefatos distribuíveis, como wheels (pacotes .whl) e source distributions (sdist, .tar.gz). O wheel é um formato binário que acelera a instalação, enquanto o sdist contém o código-fonte e pode ser usado para builds em outras plataformas.

Para construir seu pacote, você pode usar a ferramenta build, que é a recomendação oficial. Primeiro, instale-a com pip install build. Em seguida, execute python -m build no diretório raiz do projeto. Isso criará uma pasta dist/ com os artefatos. Alternativamente, você pode usar pip wheel . -w dist para gerar apenas o wheel, ou python setup.py sdist se estiver usando setuptools, mas o build é mais simples e moderno.

$ pip install build
$ python -m build

O comando acima gera os arquivos em dist/. Você pode inspecionar o conteúdo do wheel com unzip -l dist/meu_pacote-0.1.0-py3-none-any.whl. O wheel é um zip com uma estrutura específica. Para testar o pacote localmente, você pode instalá-lo com pip install dist/meu_pacote-0.1.0-py3-none-any.whl.

Publicando no PyPI (visão geral)

PyPI (Python Package Index) é o repositório oficial de pacotes Python. Publicar seu pacote lá permite que qualquer pessoa instale com pip install seu-pacote. O processo envolve criar uma conta no PyPI, gerar um token de API e usar uma ferramenta como twine para enviar os artefatos.

Para publicar, primeiro crie uma conta em pypi.org e ative a autenticação de dois fatores. Depois, crie um token de API em pypi.org/manage/account/token/. Em seguida, instale o twine: pip install twine. Antes de publicar, é recomendável testar no TestPyPI, um ambiente de testes. O comando para enviar é:

$ twine upload dist/*

Você será solicitado a fornecer o token como nome de usuário e __token__ como senha (ou use a variável de ambiente TWINE_PASSWORD). Para o TestPyPI, use --repository testpypi. Após o upload, seu pacote estará disponível publicamente. Lembre-se de que a versão deve ser única; você não pode republicar a mesma versão.

Versionamento

O versionamento é crucial para comunicar mudanças e garantir compatibilidade. O esquema mais comum é o Versionamento Semântico (SemVer), que usa o formato MAJOR.MINOR.PATCH. Incremente MAJOR para mudanças que quebram compatibilidade, MINOR para novas funcionalidades retrocompatíveis e PATCH para correções de bugs.

Além disso, é comum usar sufixos como alpha, beta, rc para pré-lançamentos (ex.: 1.0.0rc1). No pyproject.toml, a versão é definida no campo version. Você também pode usar ferramentas como setuptools-scm para gerar automaticamente a versão a partir de tags do git, evitando duplicação. Exemplo de configuração:

[project]
name = "meu-pacote"
dynamic = ["version"]

[tool.setuptools_scm]
version_scheme = "guess-next-dev"

Com essa configuração, a versão é derivada do git. Sempre que fizer uma alteração, use git tag v0.1.0 e o build usará essa versão. Isso reduz erros manuais de versionamento.

Boas práticas e observações finais

  • Use pyproject.toml para todos os projetos novos, mesmo que simples.
  • Inclua um README.md com instruções de uso e instalação.
  • Defina uma licença clara (ex.: MIT, Apache 2.0).
  • Teste seu pacote em um ambiente limpo (ex.: via pip install . em um virtualenv).
  • Publique no TestPyPI antes do PyPI oficial para validar o processo.
  • Mantenha o versionamento semântico e documente as mudanças em um changelog.

Referências

Exercícios

  1. Crie um arquivo pyproject.toml para um pacote chamado calculadora com versão 1.0.0, descrição curta, autor, licença MIT e dependência de numpy.
  2. ✓ Resposta:
    [build-system]
    requires = ["setuptools>=61.0"]
    build-backend = "setuptools.build_meta"
    
    [project]
    name = "calculadora"
    version = "1.0.0"
    description = "Uma calculadora simples"
    authors = [{name = "Seu Nome", email = "seu@email.com"}]
    license = {text = "MIT"}
    requires-python = ">=3.8"
    dependencies = ["numpy"]
    
  3. Instale a ferramenta build e gere os artefatos de distribuição (sdist e wheel) para o pacote do exercício anterior. Liste os arquivos gerados.
  4. ✓ Resposta: Execute pip install build e depois python -m build. Isso criará a pasta dist/ com os arquivos calculadora-1.0.0-py3-none-any.whl e calculadora-1.0.0.tar.gz.
  5. Explique a diferença entre sdist e wheel e quando usar cada um.
  6. ✓ Resposta: O sdist (source distribution) contém o código-fonte e é necessário para compilar extensões ou para plataformas sem wheel. O wheel é um formato binário pré-compilado que acelera a instalação e é preferível para a maioria dos usuários. Use sdist para garantir compatibilidade e wheel para instalação rápida.
  7. Qual é o comando para enviar seu pacote para o PyPI usando twine? Escreva o comando completo, incluindo a autenticação via variável de ambiente para o token.
  8. ✓ Resposta:
    export TWINE_USERNAME=__token__
    export TWINE_PASSWORD=seu_token_aqui
    twine upload dist/*
    
  9. Usando versionamento semântico, qual seria a próxima versão para cada cenário: (a) correção de bug, (b) nova funcionalidade retrocompatível, (c) mudança que quebra a API?
  10. ✓ Resposta: (a) Incrementar PATCH (ex.: de 1.0.0 para 1.0.1). (b) Incrementar MINOR (ex.: de 1.0.0 para 1.1.0). (c) Incrementar MAJOR (ex.: de 1.0.0 para 2.0.0).