Python template engine for HTML, terminal, and streaming — with framework integration
Project description
)彡 Kida
A template engine that compiles to Python AST, renders to HTML/terminal/markdown, and scales across cores on free-threaded Python.
from kida import Environment
env = Environment()
template = env.from_string("Hello, {{ name }}!")
print(template.render(name="World"))
# Hello, World!
Why Kida
Most template engines generate Python source code as strings, then exec() it. Kida compiles directly to ast.Module — the same structured representation Python itself uses. This unlocks compile-time optimization, precise error mapping, and safe concurrent rendering that string-based engines can't achieve.
| Kida | Jinja2 | |
|---|---|---|
| Compilation target | Python AST (ast.Module) |
Python source strings |
| Free-threading (PEP 703) | Safe under PYTHON_GIL=0 |
Not tested/supported |
| Rendering modes | render(), render_stream(), render_block(), async variants |
render(), generate() |
| Compile-time optimization | Constant folding, dead branch elimination, filter eval, component inlining | None |
| Terminal rendering | Built-in (autoescape="terminal") with 30+ ANSI-aware filters |
None |
| Pattern matching | {% match %} / {% case %} |
None |
| Null safety | ??, ?., `? |
>, ? |
| Components | {% def %} + {% slot %} + named slots |
Macros (no slots) |
| Regions | {% region name(params) %} — parameterized blocks |
None |
| Block rendering | render_block(), render_with_blocks() |
None (third-party) |
| Fragment caching | {% cache "key" %} built-in |
Extension required |
Performance
| Template complexity | Kida | vs Jinja2 |
|---|---|---|
| Minimal (~4µs) | Baseline | ~1x |
| Small loop + filter (~7µs) | Baseline | ~1x |
| Medium ~100 vars (~0.2ms) | Baseline | ~1.3x faster |
| Large 1000-item loop (~1.6ms) | Baseline | 2.5x faster |
| Complex 3-level inheritance (~19µs) | Baseline | 1.5x faster |
With static_context (compile-time folding) |
1.5-2x additional speedup | N/A |
Concurrent (2-4 cores, PYTHON_GIL=0) |
Linear scaling | Not supported |
Installation
pip install kida-templates
Requires Python 3.14+. Zero runtime dependencies.
Render Anywhere
Kida renders to three surfaces from the same template syntax.
HTML (default)
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
template = env.get_template("page.html")
html = template.render(title="Hello")
Terminal
from kida.terminal import terminal_env
env = terminal_env()
template = env.from_string("""
{{ "Deploy Status" | bold | cyan }}
{{ hr(40) }}
{% for svc in services %}
{{ svc.name | pad(20) }}{{ svc.status | badge }}
{% end %}
""")
print(template.render(services=[
{"name": "api", "status": "pass"},
{"name": "worker", "status": "fail"},
]))
30+ ANSI-aware filters (bold, fg(), pad, badge, bar, kv, table, tree, diff), built-in panel/box components, and LiveRenderer for in-place re-rendering with spinners.
Markdown
from kida.markdown import markdown_env
env = markdown_env()
template = env.from_string("# {{ title }}\n\n{{ body }}")
md = template.render(title="Report", body="All tests passed.")
Key Features
Template Inheritance
base.html:
<!DOCTYPE html>
<html>
<body>
{% block content %}{% end %}
</body>
</html>
page.html:
{% extends "base.html" %}
{% block content %}
<h1>{{ title }}</h1>
{% end %}
Components & Named Slots
{% def card(title) %}
<article class="card">
<h2>{{ title }}</h2>
<div class="actions">{% slot header_actions %}</div>
<div class="body">{% slot %}</div>
</article>
{% end %}
{% call card("Settings") %}
{% slot header_actions %}<button>Save</button>{% end %}
<p>Body content.</p>
{% end %}
Pattern Matching & Null Safety
{% match status %}
{% case "active" %}
Active user
{% case "pending" %}
Pending verification
{% case _ %}
Unknown status
{% end %}
{# Null coalescing #}
{{ user.nickname ?? user.name ?? "Anonymous" }}
{# Optional chaining #}
{{ config?.database?.host }}
{# Safe pipeline — stops on None #}
{{ data ?|> parse ?|> validate ?|> render }}
Streaming & Block Rendering
# Stream chunks as they render (chunked HTTP, SSE)
for chunk in template.render_stream(items=large_list):
response.write(chunk)
# Render a single block (HTMX partials)
html = template.render_block("content", title="Hello")
# Compose layouts with pre-rendered blocks
html = layout.render_with_blocks({"content": inner_html}, title="Page")
Regions — Parameterized Blocks
{% region sidebar(current_path="/") %}
<nav>{{ current_path }}</nav>
{% end %}
{{ sidebar(current_path="/about") }}
Regions are blocks (for render_block()) and callables (for inline use). Ideal for HTMX OOB swaps and framework integration.
Compile-Time Optimization
# Pass static data at compile time — kida folds constants,
# eliminates dead branches, and evaluates pure filters
template = env.from_string(source, static_context={
"site": site_config,
"settings": app_settings,
})
# Only dynamic data needed at render time
html = template.render(page_title="Home", items=page_items)
67 pure filters evaluated at compile time. Dead {% if debug %} branches removed entirely. Component inlining for small defs with constant args.
Use kida render template.html --explain to see which optimizations are active.
Free-Threading
All public APIs are safe under PYTHON_GIL=0 (Python 3.14t, PEP 703):
- Templates compile to immutable AST — no shared mutable state
- Rendering uses thread-local StringBuilder — no contention
- Environment uses copy-on-write for configuration changes
LiveRenderer.update()is thread-safe with internal locking
Module declares GIL independence via _Py_mod_gil = 0. Rendering scales linearly with cores.
Framework Integration
Drop-in adapters for Flask, Starlette/FastAPI, and Django:
# Flask
from kida.contrib.flask import KidaFlask
kida = KidaFlask(app)
# Starlette / FastAPI
from kida.contrib.starlette import KidaStarlette
templates = KidaStarlette(directory="templates")
# Django
TEMPLATES = [{"BACKEND": "kida.contrib.django.KidaDjango", ...}]
GitHub Action — CI Reports
Turn pytest, coverage, ruff, and other tool output into formatted step summaries and PR comments.
- name: Run tests
run: pytest --junitxml=results.xml
- name: Post test report
uses: lbliii/kida@v0.3.3
with:
template: pytest
data: results.xml
data-format: junit-xml
Built-in templates
| Template | Data format | Tool |
|---|---|---|
pytest |
junit-xml | pytest --junitxml |
coverage |
json | coverage.py --json or lcov |
ruff |
json | ruff --output-format json |
ty |
junit-xml | ty --output-format junit |
jest |
json | jest --json |
gotest |
junit-xml | go-junit-report |
sarif |
sarif | CodeQL, Semgrep, Trivy, ESLint |
PR comments with deduplication
- name: Post coverage to PR
uses: lbliii/kida@v0.3.3
with:
template: coverage
data: coverage.json
post-to: step-summary,pr-comment
Custom templates
- uses: lbliii/kida@v0.3.3
with:
template: .github/kida-templates/my-report.md
data: output.json
Inputs
| Input | Default | Description |
|---|---|---|
template |
(required) | Built-in name or path to template file |
data |
(required) | Path to data file |
data-format |
json |
json, junit-xml, sarif, or lcov |
post-to |
step-summary |
step-summary, pr-comment, or both |
comment-header |
template name | Marker for PR comment deduplication |
token |
github.token |
GitHub token (needed for pr-comment) |
python-version |
3.14 |
Python version (skip to use existing) |
install |
true |
Whether to pip install kida-templates |
CLI
# Render a template
kida render template.txt --data context.json
kida render dashboard.txt --mode terminal --width 80 --color truecolor
# Show which compiler optimizations are active
kida render template.html --explain
# Check all templates for syntax errors
kida check templates/
# Strict mode: require explicit end tags ({% endif %} not {% end %})
kida check templates/ --strict
# Validate macro call sites against signatures
kida check templates/ --validate-calls
# Accessibility and type checking
kida check templates/ --a11y --typed
# Auto-format templates
kida fmt templates/
API Reference
| Function | Description |
|---|---|
Environment() |
Create a template environment |
env.from_string(src) |
Compile template from string |
env.get_template(name) |
Load template from filesystem |
template.render(**ctx) |
Full render (StringBuilder, fastest) |
template.render_block(name, **ctx) |
Single block (HTMX partials) |
template.render_stream(**ctx) |
Generator (chunked HTTP, SSE) |
template.render_async(**ctx) |
Async buffered output |
template.render_stream_async(**ctx) |
Async streaming |
template.render_with_blocks(overrides, **ctx) |
Compose layout with pre-rendered blocks |
template.list_blocks() |
Block names for validation |
template.template_metadata() |
Full analysis (blocks, regions, deps) |
Full documentation: lbliii.github.io/kida. See also: Kida vs Jinja2
| Section | |
|---|---|
| Get Started | Installation, quickstart, coming from Jinja2 |
| Syntax | Template language reference |
| Usage | Loading, rendering, escaping, terminal mode |
| Framework Integration | Flask, Starlette, Django adapters |
| Advanced | Compiler, profiling, coverage, security |
| Reference | Complete API docs |
Architecture
Template Source → Lexer → Parser → Kida AST → Compiler → Python AST → exec()
Kida generates ast.Module objects directly — no intermediate source strings. This enables compile-time optimization (constant folding, dead branch elimination, filter evaluation), precise error source mapping (exact line:column in template source), and safe concurrent execution (immutable AST, no shared mutable state).
Rendering uses two modes from a single compilation:
render()— StringBuilder pattern (_out.append(...)) for maximum throughputrender_stream()— Python generator (yield ...) for statement-level streaming
Development
git clone https://github.com/lbliii/kida.git
cd kida
uv sync --group dev --python 3.14t
PYTHON_GIL=0 uv run --python 3.14t pytest
The Bengal Ecosystem
Kida is part of a pure-Python stack built for 3.14t free-threading.
| ᓚᘏᗢ | Bengal | Static site generator | Docs |
| ∿∿ | Purr | Content runtime | — |
| ⌁⌁ | Chirp | Web framework | Docs |
| =^..^= | Pounce | ASGI server | Docs |
| )彡 | Kida | Template engine | Docs |
| ฅᨐฅ | Patitas | Markdown parser | Docs |
| ⌾⌾⌾ | Rosettes | Syntax highlighter | Docs |
| ᓃ‿ᓃ | Milo | Terminal UI framework | Docs |
License
MIT License — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kida_templates-0.3.3.tar.gz.
File metadata
- Download URL: kida_templates-0.3.3.tar.gz
- Upload date:
- Size: 451.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b00bb34e2c63dc3de89962e27a7fd6702fc5c7934c68bcf6640aafff37c3e8c
|
|
| MD5 |
6a1c397727d46699f149b426ab258d43
|
|
| BLAKE2b-256 |
197c8bab18a91a19de3a8e9c8ae2ced42ea2e051e129ee8997fc2fbd3111263c
|
Provenance
The following attestation bundles were made for kida_templates-0.3.3.tar.gz:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.3.3.tar.gz -
Subject digest:
3b00bb34e2c63dc3de89962e27a7fd6702fc5c7934c68bcf6640aafff37c3e8c - Sigstore transparency entry: 1248725265
- Sigstore integration time:
-
Permalink:
lbliii/kida@5e285f3fe79016140567f3023e21b45fa1cf55e6 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5e285f3fe79016140567f3023e21b45fa1cf55e6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file kida_templates-0.3.3-py3-none-any.whl.
File metadata
- Download URL: kida_templates-0.3.3-py3-none-any.whl
- Upload date:
- Size: 341.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd847de4303e49ee530a3e05c4484a976a5f21c437532552e6c42a157edee1f0
|
|
| MD5 |
ca2dbe98e7f4b6379c31580366b5afce
|
|
| BLAKE2b-256 |
f93a10b6c27b425638de8dc47a185edbcca758c7483c57f537c6995e05916bff
|
Provenance
The following attestation bundles were made for kida_templates-0.3.3-py3-none-any.whl:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.3.3-py3-none-any.whl -
Subject digest:
dd847de4303e49ee530a3e05c4484a976a5f21c437532552e6c42a157edee1f0 - Sigstore transparency entry: 1248725292
- Sigstore integration time:
-
Permalink:
lbliii/kida@5e285f3fe79016140567f3023e21b45fa1cf55e6 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5e285f3fe79016140567f3023e21b45fa1cf55e6 -
Trigger Event:
release
-
Statement type: