Guia

Publicando Pacote no PyPI: Guia Completo

Aprenda a criar e publicar seu pacote Python no PyPI. Estrutura do projeto, setup, build, testes no TestPyPI e publicação final

4 min de leitura

Introdução

O PyPI (Python Package Index) é o repositório oficial de pacotes Python. Quando você executa pip install nome-do-pacote, o pip baixa o pacote diretamente do PyPI. Publicar seu próprio pacote permite que outros desenvolvedores reutilizem seu código facilmente.

Neste guia, vamos criar um pacote Python completo, testá-lo no TestPyPI e publicá-lo no PyPI oficial.

Pré-requisitos

  • Python 3.10 ou superior
  • Uma conta no pypi.org e no test.pypi.org
  • Conhecimento básico de estrutura de projetos Python

Estrutura do projeto

Vamos criar um pacote simples chamado calculabr que formata números no padrão brasileiro:

calculabr/
    src/
        calculabr/
            __init__.py
            formatador.py
    tests/
        test_formatador.py
    pyproject.toml
    README.md
    LICENSE

Crie as pastas:

mkdir -p calculabr/src/calculabr calculabr/tests
cd calculabr

Escrevendo o código do pacote

Crie src/calculabr/__init__.py:

from .formatador import formatar_real, formatar_cpf, formatar_cnpj

__version__ = "0.1.0"

Crie src/calculabr/formatador.py:

def formatar_real(valor: float) -> str:
    """Formata um valor numerico para o padrão monetário brasileiro.

    Args:
        valor: Valor numérico a ser formatado.

    Returns:
        String formatada no padrão R$ 1.234,56.
    """
    if valor < 0:
        return f"-R$ {abs(valor):,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
    return f"R$ {valor:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")


def formatar_cpf(cpf: str) -> str:
    """Formata uma string de 11 digitos como CPF.

    Args:
        cpf: String contendo apenas os 11 digitos do CPF.

    Returns:
        CPF formatado no padrão 123.456.789-00.
    """
    cpf = cpf.replace(".", "").replace("-", "")
    if len(cpf) != 11:
        raise ValueError("CPF deve conter 11 digitos")
    return f"{cpf[:3]}.{cpf[3:6]}.{cpf[6:9]}-{cpf[9:]}"


def formatar_cnpj(cnpj: str) -> str:
    """Formata uma string de 14 digitos como CNPJ.

    Args:
        cnpj: String contendo apenas os 14 digitos do CNPJ.

    Returns:
        CNPJ formatado no padrão 12.345.678/0001-90.
    """
    cnpj = cnpj.replace(".", "").replace("/", "").replace("-", "")
    if len(cnpj) != 14:
        raise ValueError("CNPJ deve conter 14 digitos")
    return f"{cnpj[:2]}.{cnpj[2:5]}.{cnpj[5:8]}/{cnpj[8:12]}-{cnpj[12:]}"

Escrevendo testes

Crie tests/test_formatador.py:

from calculabr import formatar_real, formatar_cpf, formatar_cnpj
import pytest


def test_formatar_real_positivo():
    assert formatar_real(1234.56) == "R$ 1.234,56"


def test_formatar_real_negativo():
    assert formatar_real(-99.90) == "-R$ 99,90"


def test_formatar_cpf_valido():
    assert formatar_cpf("12345678900") == "123.456.789-00"


def test_formatar_cpf_invalido():
    with pytest.raises(ValueError):
        formatar_cpf("123")


def test_formatar_cnpj_valido():
    assert formatar_cnpj("12345678000190") == "12.345.678/0001-90"

Configurando o pyproject.toml

O pyproject.toml é o arquivo de configuração moderno para pacotes Python. Crie-o na raiz do projeto:

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

[project]
name = "calculabr"
version = "0.1.0"
authors = [
    { name = "Seu Nome", email = "seu@email.com" },
]
description = "Formatador de valores no padrao brasileiro"
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Natural Language :: Portuguese (Brazilian)",
]

[project.urls]
Homepage = "https://github.com/seu-usuario/calculabr"
Issues = "https://github.com/seu-usuario/calculabr/issues"

Os campos mais importantes são name (que deve ser único no PyPI), version e requires-python.

Criando o README.md

O README aparece na página do pacote no PyPI:

# calculabr

Formatador de valores no padrao brasileiro para Python.

## Instalacao

pip install calculabr

## Uso

from calculabr import formatar_real, formatar_cpf

print(formatar_real(1234.56))   # R$ 1.234,56
print(formatar_cpf("12345678900"))  # 123.456.789-00

Criando a licença

Para projetos de código aberto, a licença MIT é a mais comum. Crie o arquivo LICENSE com o texto padrão da MIT License, substituindo ano e nome.

Gerando o pacote

Instale as ferramentas de build:

pip install build twine

Gere os arquivos de distribuição:

python -m build

Isso cria dois arquivos na pasta dist/:

  • calculabr-0.1.0.tar.gz (source distribution)
  • calculabr-0.1.0-py3-none-any.whl (wheel / distribuição binária)

Testando no TestPyPI

Antes de publicar no PyPI oficial, teste no TestPyPI:

python -m twine upload --repository testpypi dist/*

O twine vai pedir suas credenciais do TestPyPI. Para usar tokens de API (recomendado):

python -m twine upload --repository testpypi dist/* -u __token__ -p pypi-SeuTokenAqui

Teste a instalação:

pip install --index-url https://test.pypi.org/simple/ calculabr

Verifique se funciona:

from calculabr import formatar_real
print(formatar_real(9999.99))  # R$ 9.999,99

Publicando no PyPI oficial

Se tudo funcionou no TestPyPI, publique no PyPI real:

python -m twine upload dist/*

Pronto! Seu pacote estará disponível em https://pypi.org/project/calculabr/ e qualquer pessoa poderá instalá-lo com:

pip install calculabr

Configurando tokens de API

Para não precisar digitar credenciais toda vez, crie o arquivo ~/.pypirc:

[pypi]
username = __token__
password = pypi-SeuTokenPyPI

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-SeuTokenTestPyPI

Gere tokens em Account Settings > API Tokens no site do PyPI.

Atualizando o pacote

Para publicar uma nova versão:

  1. Atualize a versão no pyproject.toml e __init__.py
  2. Remova a pasta dist/ antiga: rm -rf dist/
  3. Gere novamente: python -m build
  4. Publique: python -m twine upload dist/*

Siga o versionamento semântico: MAJOR.MINOR.PATCH (ex: 1.0.0 para a primeira versão estável).

Boas práticas

  • Escolha nomes descritivos e únicos para seus pacotes
  • Sempre inclua testes automatizados
  • Documente funções com docstrings
  • Use type hints para melhorar a experiência do usuário
  • Teste no TestPyPI antes de publicar oficialmente
  • Configure CI/CD para automatizar publicação com GitHub Actions

Conclusão

Publicar um pacote no PyPI é mais simples do que parece. Com o pyproject.toml, ferramentas de build modernas e o TestPyPI para testes, o processo é seguro e direto. Compartilhar código como pacote é uma excelente forma de contribuir com a comunidade Python e facilitar a reutilização de soluções em diferentes projetos.