Framework Web API - Sucuri Project
Project description
🐍 Sucuri Framework
O Sucuri é um framework Web Python assíncrono e opinativo, criado para simplificar o desenvolvimento de APIs. Ele atua como uma fachada elegante sobre FastAPI, Tortoise ORM e Pydantic, abstraindo a complexidade dessas bibliotecas e focando em uma arquitetura limpa, rápida e direta, com "baterias inclusas".
Filosofia
O desenvolvedor final do Sucuri não lida diretamente com os pacotes ASGI originais. O isolamento garante uma experiência padronizada, onde você interage unicamente através dos pacotes sucuri.*. O framework dita como as pastas são estruturadas e como a injeção de dependências e roteamento são gerenciados.
Instalação
# Instale a partir do repositório/diretório local usando o uv ou pip
uv pip install -e .
🛠️ O Guia Prático (Como usar)
A única forma oficial de gerir seu projeto Sucuri é através da CLI sucuri.
1. Criar um Novo Projeto
O gerador oficial montará o esqueleto do projeto com as pastas necessárias (controllers, models, schemas).
sucuri new meu_projeto
cd meu_projeto
Isso criará uma pasta app/ contendo main.py e os submódulos, já pré-configurado com a base de dados.
2. Gerar Recursos
Para evitar código repetitivo, use o gerador de recursos (Resource). Ele criará o Model, o Schema, o Service, o Controller e o Módulo correspondente, amarrando tudo automaticamente.
sucuri generate resource Usuario
Você verá os seguintes arquivos gerados:
app/models/usuario.py(Modelo Active Record)app/schemas/usuario.py(Validação de Dados)app/services/usuario.py(Lógica de negócios via DI)app/controllers/usuario.py(Rotas RESTful)app/modules/usuario.py(Módulo autônomo aglomerando Controllers e Services)
✨ Magia do Framework: O arquivo app/app_module.py será lido via AST e modificado silenciosamente, injetando e conectando o novo UsuarioModule direto na árvore principal de dependências! Sem a necessidade de importações manuais de rotas.
Nota: Você também pode gerar componentes individuais com: sucuri generate module, sucuri generate controller, sucuri generate service, sucuri generate guard, sucuri generate filter, sucuri generate task, sucuri generate model ou sucuri generate schema.
3. Migrações de Banco de Dados
O Sucuri já vem pré-configurado para rastrear alterações no banco de dados.
Sempre que alterar um Model: Crie e aplique uma migração.
sucuri db migrate "Adicionado campo idade no Usuario"
sucuri db upgrade
4. Rodar o Servidor
O framework utiliza a CLI moderna do FastAPI por debaixo dos panos. Você tem duas opções:
Modo de Desenvolvimento (Com Hot Reloading):
sucuri dev
Modo de Produção (Otimizado, sem Hot Reloading):
sucuri run
Acesse http://127.0.0.1:8000/docs para ver a sua documentação automática interativa Swagger gerada pela nossa abstração de rotas!
5. Scripts Customizados (Taskipy)
Você pode rodar comandos curtos de automação com o executor integrado de scripts! Os scripts devem ser definidos na seção [tool.taskipy.tasks] do arquivo pyproject.toml gerado pelo framework.
# Executando um script customizado com o nome "format"
sucuri task format
🧩 Referência de Componentes
Aplicação (Core) e Tratamento de Erros
O app/main.py é minimalista. Ele inicializa a Fachada, varre a árvore de módulos e conecta o banco. Opcionalmente, permite interceptadores globais de erros.
from sucuri_framework.core import SucuriApp
from app.app_module import AppModule
from app.filters.database_filter import DatabaseNotFoundFilter
app = SucuriApp(title="Minha API", version="1.0.0")
# Registra um Exception Filter global para erros não tratados
app.use_global_filters(DatabaseNotFoundFilter())
# Inicializa os controllers e dependências varrendo a árvore de módulos
app.bootstrap(AppModule)
# Conecta o banco de dados magicamente (lê as configurações internas do projeto)
app.connect_database()
Organização em Módulos (@Module)
Projetos crescem. Para evitar bagunça global, agrupamos responsabilidades e importações em Módulos, criando uma hierarquia limpa a partir do AppModule.
from sucuri_framework.module import Module
from app.controllers.produtos import produtos_controller
produtos_module = Module("produtos")
produtos_module.include_controller(produtos_controller)
Modelagem (Active Record)
Utilizamos o Tortoise ORM nativamente. Operações como .create(), .filter(), e .get() são providas de forma assíncrona.
from sucuri_framework.models import Model, fields
class Produto(Model):
nome = fields.CharField(max_length=255)
class Meta:
table = "produtos"
Validação (Schemas)
O framework abstrai as rotas através de Controllers com uma interface fluida.
Os Schemas do Pydantic interagem perfeitamente com o ORM. Para retornar dados do Banco diretamente e esconder campos sensíveis, basta usar o response_model no Controller e garantir que o Schema tenha a flag from_attributes=True.
1. Criando os Schemas
# app/schemas/produtos.py
from sucuri_framework.schema import Schema
class ProdutoSchema(Schema):
nome: str
preco: float
class ProdutoPublicoSchema(Schema):
id: int
nome: str
preco: float
class Config:
from_attributes = True # Permite leitura direta de objetos Tortoise ORM
2. O Roteador e a Ocultação Mágica
# app/controllers/produtos.py
from sucuri_framework.routing import Controller
from app.models.produtos import Produto
from app.schemas.produtos import ProdutoPublicoSchema
produtos_controller = Controller(prefix="/api/produtos")
@produtos_controller.get("/", response_model=list[ProdutoPublicoSchema])
async def listar():
# O Pydantic oculta automaticamente qualquer campo extra do banco de dados
# (ex: log_auditoria, senha, flags internas) que não estiver no Schema!
return await Produto.all()
Injeção de Dependências Explícita (DI)
Regras de negócios devem residir em classes separadas. Seguindo a premissa de que "explícito é melhor que implícito", o Sucuri utiliza um contêiner nativo super-rápido para injetar dependências nas suas rotas.
Basta marcar o seu Service com @Injectable(). Por padrão, eles são tratados como Singletons. O contêiner do Sucuri suporta Injeção em Cascata (serviços injetando outros serviços no construtor) e previne falhas com Detecção de Dependência Circular.
# app/services/email.py
from sucuri_framework.di import Injectable
@Injectable()
class EmailService:
async def enviar(self): pass
# app/services/produtos.py
from sucuri_framework.di import Injectable, Inject
from app.services.email import EmailService
@Injectable()
class ProdutoService:
def __init__(self, email: EmailService = Inject()):
self.email = email
async def validar_estoque(self, id: int):
await self.email.enviar()
3. Controller Dinâmico
# app/controllers/produtos.py
from sucuri_framework.routing import Controller
from sucuri_framework.di import Inject
from app.services.produtos import ProdutoService
produtos_controller = Controller(prefix="/api/produtos")
@produtos_controller.post("/")
async def criar(payload: dict, service: ProdutoService = Inject()):
# O framework usa a dica de tipo (ProdutoService) e injeta a
# instância Singleton correspondente dinamicamente pelo FastAPI.
await service.validar_estoque(payload["id"])
return {"status": "sucesso"}
Autorização e Proteção de Rotas (Guards)
Guards verificam se a requisição pode acessar a rota antes que o Controller seja executado.
from sucuri_framework.core import Request
from sucuri_framework.guards import BaseGuard, UseGuards
from sucuri_framework.exceptions import HTTPException
class AuthGuard(BaseGuard):
async def can_activate(self, request: Request) -> bool:
if not request.headers.get("Authorization"):
raise HTTPException(status_code=401, detail="Token ausente")
return True
@produtos_controller.get("/vip")
@UseGuards(AuthGuard)
async def rota_protegida():
return {"mensagem": "Protegido"}
Tarefas em Segundo Plano (Background Worker)
O framework injeta gerenciadores de tarefas para você realizar operações lentas sem bloquear o retorno HTTP.
from sucuri_framework.tasks import BackgroundWorker
async def enviar_email(email: str):
... # lógica demorada
@produtos_controller.post("/notificar")
async def notificar(email: str, worker: BackgroundWorker):
worker.add_task(enviar_email, email)
return {"status": "enfileirado"}
Filtros de Exceção (Exception Filters)
Traduza exceções difíceis ou de terceiros em payloads HTTP controlados.
from sucuri_framework.exceptions import ExceptionFilter, Catch
from tortoise.exceptions import DoesNotExist
from fastapi.responses import JSONResponse
@Catch(DoesNotExist)
class DatabaseNotFoundFilter(ExceptionFilter):
async def catch(self, exception: Exception, request):
return JSONResponse(status_code=404, content={"error": "Não encontrado"})
Utilitários de Teste E2E (Test Client)
O framework oferece o SucuriTestClient para que você não precise gerenciar o Event Loop ou as conexões do banco de dados na mão. Ele faz o setup do Tortoise (em memória) e já lida com chamadas HTTP 100% assíncronas internamente!
import pytest
from sucuri_framework.testing import SucuriTestClient
from app.main import app
from app.services.produtos import ProdutoService
@pytest.mark.asyncio
async def test_envio(monkeypatch):
# Sobrescreve o método da classe nativamente (Mock)
async def mock_validar(*args):
return True
monkeypatch.setattr(ProdutoService, "validar_estoque", mock_validar)
# SucuriTestClient inicia o BD e serve a aplicação de forma assíncrona
async with SucuriTestClient(app, db_url="sqlite://:memory:") as client:
response = await client.post("/api/produtos/", json={"id": 10})
assert response.status_code == 200
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sucuri_framework-0.0.1.tar.gz.
File metadata
- Download URL: sucuri_framework-0.0.1.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f666dcde9fda57cb3b2e8ccf962f59c8c6f066a2b011a66774637290fb929a70
|
|
| MD5 |
7f84c3a6c56e117fdb58984e32e914ce
|
|
| BLAKE2b-256 |
8ec35895b0a3d48b21f3a64c72420908b293c2320076935b6479abc5fd824aa9
|
File details
Details for the file sucuri_framework-0.0.1-py3-none-any.whl.
File metadata
- Download URL: sucuri_framework-0.0.1-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e428ac71b839fe5e7b551c94663d406966ff2ddeffe8f0ddaca314c96559819
|
|
| MD5 |
e2d50ee1f7dec436b001de24802df2ac
|
|
| BLAKE2b-256 |
1ac5cdb107b0f6120c903708041d1dcf74794bc0e77e3ff26accc70d1b298f14
|