Ushka

Features

Ushka Guard

Everything that comes from outside your program arrives as Untrusted: query, form, JSON, headers, cookies, path parameters, and model output marked with guard.llm(). Raw HTML responses, |safe, redirects, file paths and raw SQL refuse it with an error that names the risk and the fix. Explicit sanitisers (safe.url, safe.path, safe.integer, safe.choice, safe.html) are the way out. See Ushka Guard.

t-strings (Python 3.14+)

return t"<p>{comment}</p>"                                  # escaped HTML
rows = await db.execute(t"SELECT * FROM posts WHERE id = {post_id}")  # bound

Filesystem and decorator routing

  • routes/index.py → /
  • routes/users/[user_id].py → /users/<user_id>
  • @app.get("/health") for anything else

Handlers return a str, dict, list, None (204), a (body, status) tuple, a t-string or a Response. HEAD and 405 Method Not Allowed (with Allow) are handled for you. See Routing.

Async Active Record ORM

from ushka.orm import db

class User(db.Model):
    name = db.String(length=100)

user = await User.create(name="Alice")

Built on SQLAlchemy, with migrations through ushka db. Install it with pip install "ushka[orm]". See ORM.

Templates, sessions and flash messages

Jinja2 with autoescaping, project templates that override the built-in ones, signed cookie sessions and flash messages. See Templates and Flashing.

Configuration without extra packages

Defaults, then ushka.toml, then .env, then USHKA_<SECTION>_<KEY> environment variables. DATABASE_URL from your host works as is. Ushka never rewrites your files. See Configuration.

CLI

ushka new scaffolds a project that is secure from the first commit. ushka dev reloads the app on change. ushka check --production fails your CI if debug is on or the secret key is missing. routes and db cover the rest. See CLI.

Lifespan hooks

@app.on_startup
async def connect():
    ...

Edit this page