ushka.guard.rewrite
Compile-time rewriting that keeps the Untrusted mark where CPython drops it.
CPython builds f-strings, "...".format(), "..." % (a, b) and
sep.join(items) in C, without asking the operands, so a plain
str subclass cannot propagate through them. Since Ushka loads the files in
routes/ itself, it can compile them through this module, which rewrites
exactly those expressions into calls to small helpers that propagate the
mark. Code outside the rewritten modules (the stdlib, third-party packages)
is untouched, so it keeps its exact semantics.
Rewrites (only inside the compiled module):
============================== ===========================================
source becomes
============================== ===========================================
f"a{x!r:>10}b" G.fstr(("a", G.fmt(x, "r", ">10"), "b"))
obj.format(*a, **k) G.format(obj, *a, **k)
obj.join(items) G.join(obj, items)
left % right G.mod(left, right)
str(x) G.str_(str, x)
============================== ===========================================
Every helper falls back to the original operation when the receiver is not a
str (bytes.join, int % int, a shadowed str...), so behaviour
only changes in one way: results built from Untrusted values stay
Untrusted. t-strings (t"...") are left alone; they need no help.
def rewrite_tree(tree: ast.Module) -> ast.Module
Applies the taint-preserving rewrite to a parsed module.
def compile_source(source: str, filename: str) -> types.CodeType
Parses, rewrites and compiles module source code.
def exec_file(module: types.ModuleType, path: str | Path) -> None
Executes the file at path as module, with the rewrite applied.
def install(prefixes: Iterable[str]) -> None
Applies the rewrite to every module imported under prefixes from now on.
Call it before importing your own code, e.g. at the top of app.py:
from ushka.guard import rewrite
rewrite.install(["myapp"])
from myapp import views
Modules that are already imported are not affected. Only your own packages should be listed; the stdlib and dependencies keep running unmodified.
def uninstall() -> None
Removes the import hook installed by install().