Ushka

API reference

ushka.guard.templates

Safe-by-construction HTML and SQL from t-strings (PEP 750, Python 3.14+).

A t-string keeps the literal parts written by the programmer apart from the interpolated values, so these helpers can escape (HTML) or bind (SQL) every value without any taint tracking at all:

html(t"<p>Hello {name}</p>")          # name is escaped
sql(t"SELECT * FROM users WHERE id = {user_id}")
# -> ("SELECT * FROM users WHERE id = :p0", {"p0": user_id})

On older Pythons TEMPLATES_SUPPORTED is False and is_template() always returns False; nothing here is imported eagerly by the rest of the package.

Limitation: html() escapes for text and quoted attribute contexts. It does not parse the markup, so it cannot stop href="{url}" from receiving a javascript: URL; validate URLs with safe.url().

def is_template(value: Any) -> bool

True if value is a PEP 750 Template (the result of a t-string).

def html(template: Any) -> str

Renders a t-string to HTML, escaping every interpolated value.

Values that already declare themselves as HTML (__html__, e.g. markupsafe.Markup or the result of a nested html() call) are kept. A nested t-string is rendered recursively.

def sql(template: Any, prefix: str = 'p') -> tuple[str, dict[str, Any]]

Turns a t-string into SQL with named bind parameters.

Every interpolation becomes :p0, :p1... and its value goes into the returned dict, ready for sqlalchemy.text(query).bindparams(**params) or any DB-API driver that accepts the named style. Values are never pasted into the SQL text, so they cannot change the query's structure.

Identifiers (table/column names) cannot be bound; choose them from a fixed list with safe.choice() and keep them in the literal part.