Estrutura de um projeto Python profissional
1. Organização de diretórios e módulos
Um projeto Python profissional começa com uma estrutura de diretórios clara e previsível. A organização padrão adotada pela comunidade separa o código-fonte do código de configuração, facilitando a navegação e manutenção.
meu_projeto/
├── src/
│ └── meu_pacote/
│ ├── __init__.py
│ ├── modulo_principal.py
│ ├── utils/
│ │ ├── __init__.py
│ │ └── helpers.py
│ └── models/
│ ├── __init__.py
│ └── entidades.py
├── tests/
│ ├── __init__.py
│ ├── unitarios/
│ ├── integracao/
│ └── conftest.py
├── docs/
├── scripts/
├── pyproject.toml
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
└── .gitignore
A pasta src/ isola o código de produção, evitando importações acidentais de diretórios de configuração. A nomenclatura de pacotes segue o padrão snake_case, e módulos dentro do pacote devem ser nomeados de forma descritiva e curta.
# src/meu_pacote/__init__.py
from .modulo_principal import funcao_principal
__version__ = "0.1.0"
__all__ = ["funcao_principal"]
2. Gerenciamento de dependências e ambientes
O arquivo pyproject.toml tornou-se o padrão para configuração de projetos Python modernos, substituindo setup.py e setup.cfg. Ele centraliza metadados, dependências e configurações de ferramentas.
[build-system]
requires = ["setuptools>=64.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "meu-pacote"
version = "0.1.0"
description = "Um pacote profissional"
requires-python = ">=3.10"
dependencies = [
"requests>=2.28",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"ruff>=0.1.0",
"mypy>=1.0",
"pre-commit>=3.0",
]
test = [
"pytest>=7.0",
"pytest-cov>=4.0",
]
Para ambientes de desenvolvimento, ferramentas como poetry ou pipenv gerenciam automaticamente o bloqueio de versões. Se optar por pip, utilize requirements.txt e requirements-dev.txt:
# requirements.txt
requests==2.31.0
pydantic==2.5.0
# requirements-dev.txt
-r requirements.txt
pytest==7.4.3
black==23.11.0
3. Configuração de ferramentas de qualidade de código
A qualidade do código é mantida através de ferramentas automatizadas. O ruff é recomendado por sua velocidade e por unificar linting e formatação, substituindo flake8 e black em projetos modernos.
[tool.ruff]
target-version = "py310"
line-length = 88
select = ["E", "F", "I", "N", "W"]
[tool.ruff.format]
quote-style = "double"
[tool.black]
line-length = 88
target-version = ["py310"]
[tool.mypy]
python_version = "3.10"
strict = true
ignore_missing_imports = true
O pre-commit automatiza a execução dessas ferramentas antes de cada commit:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
hooks:
- id: mypy
4. Estrutura de testes automatizados
Testes são organizados em camadas: unitários (testam funções isoladas), de integração (testam interações entre módulos) e de sistema (testam fluxos completos). O pytest é a ferramenta padrão.
# tests/conftest.py
import pytest
from meu_pacote.models.entidades import Usuario
@pytest.fixture
def usuario_padrao():
return Usuario(nome="Maria", email="maria@exemplo.com")
@pytest.fixture
def dados_api():
return {"status": "ok", "dados": []}
# tests/unitarios/test_modulo_principal.py
import pytest
from meu_pacote.modulo_principal import processar_dados
class TestProcessarDados:
def test_dados_validos(self, dados_api):
resultado = processar_dados(dados_api)
assert resultado["status"] == "processado"
@pytest.mark.integracao
def test_com_api_externa(self):
# Teste que requer integração
pass
@pytest.mark.slow
def test_grande_volume(self):
# Teste demorado
pass
Para cobertura de código, configure o pytest-cov no pyproject.toml:
[tool.coverage.run]
source = ["src"]
omit = ["*/tests/*", "*/__init__.py"]
[tool.coverage.report]
fail_under = 80
show_missing = true
Execute os testes com: pytest --cov=src --cov-report=html tests/
5. Documentação e versionamento
Documentação inline segue a PEP 257 com docstrings no estilo Google ou NumPy. O Sphinx gera documentação HTML a partir dessas docstrings.
def calcular_media(valores: list[float]) -> float:
"""Calcula a média aritmética de uma lista de números.
Args:
valores: Lista de números float para cálculo.
Returns:
A média aritmética dos valores.
Raises:
ValueError: Se a lista estiver vazia.
Examples:
>>> calcular_media([1.0, 2.0, 3.0])
2.0
"""
if not valores:
raise ValueError("Lista vazia não permite cálculo de média")
return sum(valores) / len(valores)
O repositório deve conter:
- README.md: Visão geral, instalação e exemplos rápidos
- CHANGELOG.md: Registro de versões seguindo Keep a Changelog
- CONTRIBUTING.md: Guia para contribuidores com padrões de código e processo de PR
O versionamento semântico (SemVer) segue o formato MAJOR.MINOR.PATCH:
- MAJOR: mudanças incompatíveis na API
- MINOR: funcionalidades novas compatíveis
- PATCH: correções de bugs compatíveis
6. Automação de tarefas e CI/CD
Um Makefile automatiza tarefas comuns de desenvolvimento:
.PHONY: install test lint format clean
install:
pip install -e ".[dev,test]"
test:
pytest --cov=src tests/
lint:
ruff check src/ tests/
format:
ruff format src/ tests/
typecheck:
mypy src/
clean:
rm -rf build/ dist/ *.egg-info .coverage htmlcov/
all: lint format typecheck test
Para CI/CD com GitHub Actions, configure um pipeline completo:
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -e ".[dev,test]"
- name: Lint
run: ruff check src/ tests/
- name: Type check
run: mypy src/
- name: Test
run: pytest --cov=src --cov-report=xml tests/
- name: Upload coverage
uses: codecov/codecov-action@v3
publish:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build
run: |
pip install build
python -m build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
A publicação automatizada no PyPI dispara quando uma tag com prefixo v é criada, garantindo que apenas versões testadas sejam disponibilizadas.
Uma estrutura profissional não é burocrática — é um investimento que reduz custos de manutenção, acelera a integração de novos membros e aumenta a confiabilidade do software. Comece com o básico (estrutura de pastas, pyproject.toml, pytest) e adicione ferramentas conforme seu projeto cresce.
Referências
- PEP 621 – Storing project metadata in pyproject.toml — Especificação oficial do formato pyproject.toml para metadados de projeto
- Python Packaging User Guide — Guia oficial de empacotamento Python, incluindo estrutura de diretórios e distribuição
- Ruff Documentation — Documentação completa do ruff, ferramenta de linting e formatação
- pytest: Full pytest documentation — Guia oficial do pytest com fixtures, marcadores e plugins
- pre-commit: Quick Start — Tutorial oficial de configuração de hooks pre-commit para automação de qualidade
- Semantic Versioning 2.0.0 — Especificação oficial do versionamento semântico
- GitHub Actions: Building and testing Python — Guia oficial para configurar CI/CD de projetos Python no GitHub Actions