Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sucuri_framework-0.0.4b0.tar.gz (16.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sucuri_framework-0.0.4b0-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

File details

Details for the file sucuri_framework-0.0.4b0.tar.gz.

File metadata

  • Download URL: sucuri_framework-0.0.4b0.tar.gz
  • Upload date:
  • Size: 16.7 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

Hashes for sucuri_framework-0.0.4b0.tar.gz
Algorithm Hash digest
SHA256 25d4b5bf7263699a2d2430670a60dd3bf7b1426c5be77ffb0b9ffff61f2e9ad1
MD5 ae8d06af0b42092b36fc0a377dfbecc8
BLAKE2b-256 7b2bcadf65544bc8a48aefd4f5adf30ab1ac5de0195906a11cdc18d194d110e1

See more details on using hashes here.

File details

Details for the file sucuri_framework-0.0.4b0-py3-none-any.whl.

File metadata

  • Download URL: sucuri_framework-0.0.4b0-py3-none-any.whl
  • Upload date:
  • Size: 24.6 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

Hashes for sucuri_framework-0.0.4b0-py3-none-any.whl
Algorithm Hash digest
SHA256 dd420be6c591d3abfe1703a6312185e5233d83eca51c68213973dcb8aea77734
MD5 cd923c7cdb94e19997a9e7758e8daceb
BLAKE2b-256 da5bba46337087b4a85ce3ce09c150189d41d14fa570160419e2213087302848

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page