Criando e publicando um pacote no PyPI
1. Preparação do ambiente e estrutura do projeto
1.1. Ferramentas essenciais
Antes de começar, certifique-se de ter Python 3.8+ instalado. As ferramentas fundamentais são:
python -m pip install --upgrade pip
pip install build twine virtualenv
Crie um ambiente virtual para isolar seu projeto:
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
1.2. Estrutura de diretórios recomendada
Adotaremos o src layout (recomendado pela comunidade):
meu-pacote/
├── src/
│ └── meupacote/
│ ├── __init__.py
│ ├── modulo1.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ └── test_modulo1.py
├── pyproject.toml
├── README.md
├── CHANGELOG.md
├── LICENSE
└── .gitignore
1.3. Versionamento com Git
git init
git add .
git commit -m "Initial commit: estrutura básica do pacote"
Adicione ao .gitignore:
venv/
__pycache__/
*.pyc
dist/
*.egg-info/
*.whl
2. Configuração do pyproject.toml (PEP 621)
O pyproject.toml é o coração da configuração moderna. Exemplo completo:
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "meupacote"
version = "0.1.0"
description = "Um pacote Python incrível para demonstração"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "Seu Nome", email = "seu@email.com"}
]
dependencies = [
"requests>=2.25.0",
"click>=8.0.0"
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black",
"flake8"
]
[project.urls]
Homepage = "https://github.com/seuusuario/meu-pacote"
Repository = "https://github.com/seuusuario/meu-pacote.git"
Documentation = "https://meupacote.readthedocs.io"
[project.scripts]
meu-cli = "meupacote.modulo1:main"
[tool.setuptools.packages.find]
where = ["src"]
2.1. Classificadores (Trove Classifiers)
Adicione classificadores relevantes:
[project.classifiers]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Software Development :: Libraries :: Python Modules",
]
3. Escrita do código do pacote
3.1. Organização de módulos
Crie src/meupacote/__init__.py:
"""Pacote de demonstração para PyPI."""
from .modulo1 import saudacao
from .utils import processar_dados
__version__ = "0.1.0"
__author__ = "Seu Nome"
src/meupacote/modulo1.py:
"""Módulo principal com funcionalidades do pacote."""
def saudacao(nome: str) -> str:
"""Retorna uma saudação personalizada."""
return f"Olá, {nome}! Bem-vindo ao meupacote."
def main():
"""Entry point para CLI."""
import sys
nome = sys.argv[1] if len(sys.argv) > 1 else "Mundo"
print(saudacao(nome))
src/meupacote/utils.py:
"""Utilitários para processamento de dados."""
def processar_dados(numeros: list) -> dict:
"""Processa uma lista de números e retorna estatísticas."""
return {
"soma": sum(numeros),
"media": sum(numeros) / len(numeros),
"maximo": max(numeros),
"minimo": min(numeros)
}
3.2. Importação relativa vs absoluta
Use importação absoluta dentro do pacote:
# Correto (absoluto)
from meupacote.utils import processar_dados
# Evite (relativo)
from .utils import processar_dados # Funciona, mas menos explícito
3.3. Inclusão de dados estáticos
Crie MANIFEST.in para incluir arquivos adicionais:
include README.md
include CHANGELOG.md
include LICENSE
include pyproject.toml
recursive-include src/meupacote/data *
4. Geração de builds e testes locais
4.1. Construção do pacote
python -m build
Isso gera:
- dist/meupacote-0.1.0-py3-none-any.whl (wheel)
- dist/meupacote-0.1.0.tar.gz (sdist)
4.2. Instalação local para desenvolvimento
pip install -e .
Agora você pode importar e testar:
from meupacote import saudacao, processar_dados
print(saudacao("Python")) # Olá, Python! Bem-vindo ao meupacote.
print(processar_dados([1, 2, 3, 4, 5]))
# {'soma': 15, 'media': 3.0, 'maximo': 5, 'minimo': 1}
4.3. Verificação com twine
twine check dist/*
Deve retornar "Passed" sem erros.
5. Versionamento semântico e changelog
5.1. Semantic Versioning (SemVer)
Siga o formato MAJOR.MINOR.PATCH:
- MAJOR: mudanças incompatíveis na API
- MINOR: novas funcionalidades compatíveis
- PATCH: correções de bugs
5.2. Ferramentas de versionamento
Com bumpversion:
pip install bumpversion
bumpversion patch # 0.1.0 → 0.1.1
bumpversion minor # 0.1.1 → 0.2.0
bumpversion major # 0.2.0 → 1.0.0
Com setuptools-scm (automático via git):
No pyproject.toml:
[project]
dynamic = ["version"]
[tool.setuptools_scm]
write_to = "src/meupacote/_version.py"
pip install setuptools-scm
5.3. Manutenção de CHANGELOG.md
# Changelog
## [0.1.1] - 2025-01-15
### Fixed
- Correção na função `processar_dados` para listas vazias
## [0.1.0] - 2025-01-10
### Added
- Função `saudacao` para saudações personalizadas
- Função `processar_dados` para estatísticas
- CLI básica com entry point
6. Publicação no PyPI
6.1. Criação de contas
- TestPyPI — para testes
- PyPI oficial — para publicação real
6.2. Autenticação com tokens de API
Gere tokens em: Account Settings → API tokens
Configure localmente:
# Para TestPyPI
cat > ~/.pypirc << EOF
[testpypi]
username = __token__
password = pypi-xxxxxxxxxxxxxxxxxxxx
EOF
# Para PyPI oficial
[distutils]
index-servers =
pypi
testpypi
[pypi]
username = __token__
password = pypi-yyyyyyyyyyyyyyyyyyyy
6.3. Upload com twine
Primeiro para TestPyPI:
twine upload --repository testpypi dist/*
Teste a instalação:
pip install --index-url https://test.pypi.org/simple/ meupacote
Depois para PyPI oficial:
twine upload dist/*
7. Automação e CI/CD
7.1. Workflow GitHub Actions
Crie .github/workflows/publish.yml:
name: Publicar no PyPI
on:
release:
types: [published]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Instalar dependências
run: |
python -m pip install --upgrade pip
pip install pytest
pip install -e .
- name: Executar testes
run: pytest
publish:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Instalar dependências
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build
run: python -m build
- name: Publicar no PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
7.2. Configuração de segredos
No GitHub: Settings → Secrets and variables → Actions → New repository secret
Adicione PYPI_API_TOKEN com seu token do PyPI.
7.3. Publicação automática
Ao criar uma release no GitHub, o workflow:
1. Executa testes
2. Constrói o pacote
3. Publica automaticamente no PyPI
8. Manutenção e boas práticas pós-publicação
8.1. Atualização de versões
# Para correções rápidas
bumpversion patch
git push --tags
# Crie uma nova release no GitHub
8.2. Remoção ou deprecação (yanking)
Se uma versão tiver problemas críticos:
pip install yank
yank meupacote==0.1.0
Ou manualmente no site do PyPI: "Yank this release".
8.3. Monitoramento
Acompanhe métricas no PyPI Stats:
pip install pypistats
pypistats recent meupacote
Referências
- Documentação oficial do Python Packaging — Guia completo da Python Packaging Authority sobre criação e publicação de pacotes
- PEP 621 – Storing project metadata in pyproject.toml — Especificação oficial para metadados de projeto no formato moderno
- Tutorial de empacotamento do Real Python — Guia prático e detalhado para publicar pacotes no PyPI
- Documentação do setuptools — Referência oficial sobre configuração e build com setuptools
- GitHub Action para PyPI — Ação oficial para automação de publicação no PyPI via GitHub Actions
- Semantic Versioning 2.0.0 — Especificação completa do versionamento semântico
- TestPyPI — Ambiente de testes para publicar pacotes antes do PyPI oficial