---
title: "Publicando Pacote no PyPI: Guia Completo"
url: "https://python.dev.br/guias/publicando-pacote-pypi/"
markdown_url: "https://python.dev.br/guias/publicando-pacote-pypi.MD"
description: "Aprenda a criar e publicar seu pacote Python no PyPI. Estrutura do projeto, setup, build, testes no TestPyPI e publicação final"
date: "2025-11-18"
author: ""
---

# 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


## 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](https://pypi.org/) e no [test.pypi.org](https://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:

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

## Escrevendo o código do pacote

Crie `src/calculabr/__init__.py`:

```python
from .formatador import formatar_real, formatar_cpf, formatar_cnpj

__version__ = "0.1.0"
```

Crie `src/calculabr/formatador.py`:

```python
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`:

```python
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:

```toml
[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:

```markdown
# 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:

```bash
pip install build twine
```

Gere os arquivos de distribuição:

```bash
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:

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

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

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

Teste a instalação:

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

Verifique se funciona:

```python
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:

```bash
python -m twine upload dist/*
```

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

```bash
pip install calculabr
```

## Configurando tokens de API

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

```ini
[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.

Cada linguagem tem seu ecossistema de pacotes. Veja como funciona a publicação de módulos em <a href="https://golang.com.br/blog/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', {source: 'python.dev.br', target: 'golang.com.br', content: 'publicando-pacote-pypi'})">Go com Go Modules</a> ou como publicar crates em <a href="https://rustlang.com.br/blog/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', {source: 'python.dev.br', target: 'rustlang.com.br', content: 'publicando-pacote-pypi'})">Rust no crates.io</a>.
