Wrappers Python ergonômicos sobre o Ministack (emulador local de AWS) para acelerar o desenvolvimento de aplicações de engenharia de dados — pipelines, ETLs, streaming, jobs Spark/Glue, orquestração — sem depender de uma conta AWS real durante o ciclo de desenvolvimento.
Cada classe *Wrapper esconde a complexidade do boto3 por trás de métodos de API enxuta: argumentos primitivos (str, int, bool, dict, list[tuple]) entram, dados úteis saem (ARN, ID, lista, dict). Sem precisar montar dicts aninhados do AWS na mão, sem ler StreamingBody, sem fazer json.dumps em política IAM.
- Python 3.13+
- uv para dependências
- boto3 para chamar o Ministack
- Ministack rodando em
http://localhost:4566via Docker Compose - pytest + pytest-mock para os testes unitários
- ruff para lint e formatação
- commitizen para Conventional Commits + versionamento semver automático
# 1. Subir o Ministack (e Redis, etc.) via docker compose
docker compose up -d ministack
# 2. Instalar dependências
uv sync
# 3. Instalar git hooks locais (commit-msg + post-commit)
./scripts/install_git_hooks.sh
# 4. Rodar a suíte de testes
uv run pytestCada wrapper se constrói sem argumentos — o Client aponta para o Ministack local por padrão (http://localhost:4566, região us-east-1, credenciais test/test):
from wrappers.s3 import S3Wrapper
from wrappers.kinesis import KinesisWrapper
from wrappers.glue import GlueWrapper
s3 = S3Wrapper()
s3.create_bucket("raw-events")
s3.put_object("raw-events", "events/2026/01/01.json", b'{"id": 1}')
print(s3.list_objects("raw-events"))
kinesis = KinesisWrapper()
kinesis.create_stream("clickstream", shard_count=2)
kinesis.put_record("clickstream", b'{"user_id": 123}', partition_key="123")
glue = GlueWrapper()
glue.create_database({"Name": "analytics"})
glue.create_table("analytics", {"Name": "events", "StorageDescriptor": {}})Cada operação loga em INFO uma linha de confirmação [ok] <ação>: <identificador> para facilitar o debug.
A lista abaixo reflete o estado atual — novos serviços serão adicionados conforme a necessidade de cada aplicação de dados que for sendo construída em cima dessa base.
| Serviço | Wrapper | Operações |
|---|---|---|
| CloudWatch | CloudWatchWrapper |
20 |
| EventBridge | EventBridgeWrapper |
57 |
| EMR | EMRWrapper |
22 |
| Firehose | FirehoseWrapper |
12 |
| Glue | GlueWrapper |
65 |
| IAM | IAMWrapper |
67 |
| Kinesis | KinesisWrapper |
26 |
| S3 | S3Wrapper |
68 |
| Secrets Manager | SecretsManagerWrapper |
20 |
| Step Functions | StepFunctionsWrapper |
33 |
Todos os métodos cobrem 1:1 as operações suportadas pelo Ministack (verificadas contra o código-fonte do projeto). Próximos serviços previstos conforme demanda: SQS, SNS, Lambda, DynamoDB, Athena, RDS, Redshift, entre outros.
src/
common/
utils.py # Client (dataclass com defaults pro Ministack)
wrappers/
cloudwatch.py
emr.py
eventbridge.py
firehose.py
glue.py
iam.py
kinesis.py
s3.py
secretsmanager.py
stepfunctions.py
tests/
conftest.py # fixture que mocka boto3 via Client.build_client
wrappers/
test_*.py # 1 teste de happy path por método (~393 testes)
docker-compose.yml # Ministack + Redis
pyproject.toml # deps + config do pytest
Cada método obedece o mesmo formato:
- Argumentos primitivos tipados — nada de dicts aninhados do boto3 vazando para o caller.
dictPython entra para policies/definitions/payloads — serialização para JSON é interna.- Retornos: ARN/ID quando faz sentido (criação),
list[dict]para listagens,bytesem downloads. - Uma operação boto3 por método — sem branches, sem helpers internos.
- Log
[ok] <ação>: <identificador>no fim de cada operação bem-sucedida.
uv run pytest # toda a suíte
uv run pytest tests/wrappers/test_s3.py # um wrapper específico
uv run pytest -k "create_bucket" # por nomeOs testes não tocam o Ministack — Client.build_client é substituído por um MagicMock na fixture boto3_client do conftest.py. Cada teste valida o caminho feliz: que o método chama a operação boto3 correta com os kwargs esperados e devolve o dado certo.
Configuração do ruff em pyproject.toml (line-length 100, target py313, regras E,W,F,I,B,UP,SIM,C4,N,RUF).
uv run ruff check . # lint
uv run ruff check . --fix # lint com auto-fix
uv run ruff format . # formatar
uv run ruff format . --check # checar formatação sem alterarO projeto adota Conventional Commits e versionamento semver gerenciado pelo commitizen. A versão fica em pyproject.toml (campo [project].version) e é atualizada automaticamente a partir do histórico de commits.
- commit-msg — valida que toda mensagem segue Conventional Commits. Commits fora do padrão são rejeitados.
- post-commit — apenas imprime um lembrete quando há commits acumulados desde a última tag, sugerindo rodar o bump. Não bumpa automaticamente para evitar loops e poluição do histórico.
<tipo>(<escopo opcional>): <descrição curta>
[corpo opcional]
[footer opcional, ex: BREAKING CHANGE: ...]
Tipos comuns: feat, fix, docs, style, refactor, perf, test, chore, build, ci.
Exemplos:
git commit -m "feat(s3): add presigned_url helper"
git commit -m "fix(kinesis): handle empty shard list"
git commit -m "chore: bump boto3"Para escrever o commit interativamente seguindo o padrão:
uv run cz commitscripts/bump_version.sh analisa os commits desde a última tag, decide se o bump é MAJOR / MINOR / PATCH (com base nos tipos feat, fix, BREAKING CHANGE), atualiza pyproject.toml, gera/atualiza CHANGELOG.md e cria a tag git vX.Y.Z.
./scripts/bump_version.sh # bump automático
./scripts/bump_version.sh --dry-run # mostra o que seria feito
./scripts/bump_version.sh --increment MINOR # forçar tipo de bump
# Publicar a tag e o commit de release
git push --follow-tagsComandos úteis do commitizen direto:
uv run cz version --project # versão atual
uv run cz changelog # gerar CHANGELOG sem bumpar
uv run cz check --rev-range HEAD~5..HEAD # validar últimos commits