v0.3.0 Release Notes¶
Release Date: June 2025
This release adds real-time communication primitives and a modular routing system to Cello, enabling WebSocket connections, Server-Sent Events, multipart file uploads, and Flask-style Blueprints for organizing large applications.
Highlights¶
- WebSocket Support -- Full-duplex communication over a single TCP connection
- Server-Sent Events (SSE) -- Push updates from server to client over HTTP
- Multipart Form Handling -- File uploads and mixed form data
- Blueprints -- Modular route grouping with prefix and middleware isolation
New Features¶
WebSocket Support¶
Cello now supports WebSocket connections through tokio-tungstenite, with a clean Python decorator API.
from cello import App
app = App()
@app.websocket("/ws/chat")
async def chat(ws):
while True:
message = await ws.receive()
await ws.send(f"Echo: {message}")
Capabilities:
- Text and binary message frames
- Ping/pong heartbeat (automatic)
- Graceful connection close with status codes
- Per-connection state via the
wsobject - Concurrent connections managed by Tokio
Server-Sent Events (SSE)¶
Push real-time updates to clients over a standard HTTP connection.
from cello import App, SseStream
import asyncio
app = App()
@app.get("/events")
async def events(request):
async def stream():
for i in range(100):
yield {"event": "update", "data": f"tick {i}"}
await asyncio.sleep(1)
return SseStream(stream())
SSE responses automatically set Content-Type: text/event-stream and keep the connection open. The Rust layer handles buffering and flush semantics.
Multipart Form Handling¶
Upload files and process mixed form data using the multer crate under the hood.
@app.post("/upload")
async def upload(request):
files = await request.files()
form = await request.form()
for f in files:
content = await f.read()
save_file(f.filename, content)
return {"uploaded": len(files), "name": form.get("name")}
Features:
| Feature | Description |
|---|---|
| Multiple files | Upload several files in one request |
| Size limits | Configurable per-file and total limits |
| Streaming | Large files are streamed, not buffered entirely in memory |
| MIME detection | Automatic content-type detection |
Blueprints¶
Organize routes into reusable modules with shared prefixes, middleware, and guards.
from cello import App, Blueprint
users_bp = Blueprint("/users")
@users_bp.get("/")
def list_users(request):
return {"users": []}
@users_bp.get("/{id}")
def get_user(request):
return {"id": request.params["id"]}
app = App()
app.register_blueprint(users_bp)
# Routes: GET /users/, GET /users/{id}
Blueprint features:
- URL prefix applied to all routes in the group
- Middleware scoped to the blueprint
- Nested blueprints for hierarchical organization
- Independent error handlers per blueprint
# Nested blueprints
api = Blueprint("/api")
v1 = Blueprint("/v1")
v1.register_blueprint(users_bp)
api.register_blueprint(v1)
app.register_blueprint(api)
# Routes: GET /api/v1/users/, GET /api/v1/users/{id}
Improvements¶
Performance¶
- Optimized routing -- Radix tree now pre-compiles route patterns at registration time
- Zero-copy WebSocket frames -- Messages pass through Rust without Python copies where possible
- Faster multipart parsing -- Streaming parser avoids buffering entire request bodies
Developer Experience¶
- Better error messages for malformed routes and missing handlers
- Debug logging for WebSocket lifecycle events (connect, message, disconnect)
- Improved
--debugmode with request/response body inspection
Bug Fixes¶
- Fixed route conflicts when registering overlapping path parameters
- Fixed
Content-Lengthheader not being set for empty responses - Fixed incorrect HTTP status code for
HEADrequests on missing routes - Fixed memory growth when many short-lived connections were opened rapidly
Dependencies Added¶
| Dependency | Version | Purpose |
|---|---|---|
tokio-tungstenite | 0.21 | WebSocket protocol support |
multer | 3.0 | Multipart form parsing |
Migration from v0.2.0¶
No breaking changes. All v0.2.0 applications work without modification.
New optional features¶
- WebSocket routes -- Use
@app.websocket("/path")to add WebSocket endpoints. - SSE responses -- Return
SseStream(generator)from any GET handler. - File uploads -- Call
await request.files()in handlers that accept multipart data. - Blueprints -- Refactor existing routes into
Blueprintobjects for better organization.
Full Changelog¶
See the complete changelog for all changes in this release.