Skip to content

v0.5.0 Release Notes¶

Release Date: October 2025

Cello v0.5.0 is a major feature release that adds dependency injection, role-based access control (RBAC) through composable guards, built-in Prometheus metrics, automatic OpenAPI schema generation, background task scheduling, and Jinja2 template rendering. These features bring Cello to parity with full-stack frameworks while maintaining Rust-level performance.


Highlights¶

  • Dependency Injection -- Constructor-based DI with singleton and transient lifetimes
  • Guards (RBAC) -- Composable access control with Role, Permission, and custom guards
  • Prometheus Metrics -- Built-in request counters, latency histograms, and gauge metrics
  • OpenAPI Generation -- Automatic /docs endpoint with Swagger UI
  • Background Tasks -- Fire-and-forget and scheduled tasks
  • Jinja2 Templates -- Server-side HTML rendering with template inheritance

New Features¶

Dependency Injection¶

Inject services into handlers using Depends, inspired by FastAPI but resolved at the Rust layer for lower overhead.

from cello import App, Depends

app = App()

class Database:
    def __init__(self):
        self.connection = connect_to_db()

    def get_user(self, user_id: str):
        return self.connection.query("SELECT * FROM users WHERE id = ?", user_id)

def get_db():
    return Database()

def get_current_user(request, db=Depends(get_db)):
    token = request.get_header("Authorization")
    return db.get_user_by_token(token)

@app.get("/profile")
def profile(request, user=Depends(get_current_user)):
    return {"name": user.name, "email": user.email}

Dependency features:

Feature Description
Depends(factory) Resolve a dependency per request
register_singleton(key, instance) Register a shared instance
Nested dependencies Dependencies can depend on other dependencies
Caching Each dependency is resolved once per request

Guards (RBAC)¶

Composable access control guards that run before the handler. Guards inspect the request context and either allow or deny access.

from cello import App
from cello.guards import RoleGuard, PermissionGuard, AuthenticatedGuard

app = App()

# Require authentication
@app.get("/dashboard", guards=[AuthenticatedGuard()])
def dashboard(request):
    return {"welcome": request.context.get("user_id")}

# Require admin role
@app.get("/admin", guards=[RoleGuard(["admin"])])
def admin_panel(request):
    return {"admin": True}

# Require specific permission
@app.delete("/users/{id}", guards=[PermissionGuard(["users:delete"])])
def delete_user(request):
    return {"deleted": request.params["id"]}

Composable logic with And, Or, Not:

from cello.guards import AndGuard, OrGuard, NotGuard

# Admin OR has special permission
flexible = OrGuard([
    RoleGuard(["admin"]),
    PermissionGuard(["elevated:access"]),
])

# Authenticated AND not banned
safe = AndGuard([
    AuthenticatedGuard(),
    NotGuard(RoleGuard(["banned"])),
])

@app.get("/resource", guards=[flexible])
def resource(request):
    return {"access": "granted"}

Custom guards:

from cello.guards import Guard

class IPWhitelistGuard(Guard):
    def __init__(self, allowed_ips):
        self.allowed_ips = allowed_ips

    def check(self, request, context) -> bool:
        return request.client_ip in self.allowed_ips

Prometheus Metrics¶

Built-in metrics middleware exposes a /metrics endpoint in Prometheus exposition format.

app.enable_prometheus(
    endpoint="/metrics",
    namespace="cello",
    subsystem="http",
)

Exposed metrics:

Metric Type Description
cello_http_requests_total Counter Total requests by method, status, path
cello_http_request_duration_seconds Histogram Request latency distribution
cello_http_requests_in_flight Gauge Currently active requests
cello_http_response_size_bytes Histogram Response body size distribution

The metrics are collected in Rust and serialized on demand, adding negligible overhead to request processing.

OpenAPI Generation¶

Cello generates an OpenAPI 3.0 specification from your route definitions and serves interactive documentation.

from cello import App

app = App()

app.enable_openapi(
    title="My API",
    version="1.0.1",
    description="A Cello-powered REST API",
    docs_url="/docs",
    redoc_url="/redoc",
    openapi_url="/openapi.json",
)

Routes are automatically documented based on:

  • HTTP method and path
  • Path parameters and their types
  • Response examples from handler return types
  • DTO validation schemas (when used with v0.6.0+)

Background Tasks¶

Run tasks after the response is sent, or on a schedule.

from cello import App

app = App()

# Fire-and-forget after response
@app.post("/orders")
def create_order(request):
    order = process(request.json())
    request.add_background_task(send_confirmation_email, order.email)
    return {"order_id": order.id}

async def send_confirmation_email(email):
    await email_service.send(email, "Order confirmed")

Background tasks run on the Tokio runtime and do not block the response. Failures are logged but do not affect the client.

Jinja2 Templates¶

Render HTML using Jinja2 templates with full inheritance support.

from cello import App, TemplateEngine

app = App()

app.enable_templates(TemplateEngine(
    directory="templates",
    auto_reload=True,  # Watch for changes in development
))

@app.get("/")
def home(request):
    return app.render_template("home.html", {
        "title": "Welcome",
        "items": ["Cello", "is", "fast"],
    })

Templates are loaded from the specified directory and cached in memory. In development mode, templates are reloaded automatically when files change.


Improvements¶

Performance¶

  • 12% faster middleware chain through pre-computed execution plans
  • Reduced PyO3 overhead for handler invocation
  • Smarter buffer reuse in response serialization

Developer Experience¶

  • Route listing via python app.py --routes to display all registered endpoints
  • Startup banner showing host, port, workers, and enabled features
  • Improved error tracebacks that include both Python and Rust stack frames

Breaking Changes¶

Middleware Priority¶

Middleware priority values have been renumbered. If you set custom priority() values on middleware, review the new defaults:

Middleware Old Priority New Priority
Security Headers 100 10
CORS 90 20
Rate Limiting 80 30
Authentication 70 40
Logging 50 50
Compression 30 90

Blueprint Registration¶

app.include_blueprint() has been renamed to app.register_blueprint() for consistency.

# Before (v0.4.x)
app.include_blueprint(users_bp)

# After (v0.5.0)
app.register_blueprint(users_bp)

Bug Fixes¶

  • Fixed JWT token refresh returning expired claims
  • Fixed rate limiter not resetting window after full expiry
  • Fixed WebSocket handler not receiving close events
  • Fixed static files returning 404 for URL-encoded filenames
  • Fixed multipart parser failing on boundaries with special characters
  • Fixed session cookie Path attribute not being set correctly

Dependencies Added¶

Dependency Version Purpose
prometheus 0.13 Metrics collection and exposition
minijinja 1.0 Template rendering engine (Rust-native Jinja2)

Migration from v0.4.0¶

  1. Update your dependency:

    pip install --upgrade cello-framework
    

  2. Rename blueprint registration (if used):

    # Change include_blueprint to register_blueprint
    app.register_blueprint(my_bp)
    

  3. Review middleware priority if you use custom values.

  4. New features are opt-in. Enable them as needed:

    app.enable_prometheus(...)
    app.enable_openapi(...)
    app.enable_templates(...)
    


Full Changelog¶

See the complete changelog for all changes in this release.