Modern template engine for Python 3.14t
from kida import Environment
env = Environment()
template = env.from_string("Hello, {{ name }}!")
print(template.render(name="World"))
# Output: Hello, World!Kida is a modern template engine for Python 3.14t. It works for static site generation (Bengal), dynamic web apps (Chirp), and anywhere you need templates — same syntax, same engine. It compiles templates to Python AST directly (no string generation), supports streaming and block rendering, and is built for free-threading.
What's good about it:
- AST-native — Compiles to Python AST directly. Structured code manipulation, compile-time optimization, precise error source mapping.
- Free-threading ready — Safe for Python 3.14t concurrent execution (PEP 703). All public APIs are thread-safe.
- Dual-mode rendering —
render()uses StringBuilder for maximum throughput.render_stream()yields chunks for streaming HTTP and SSE. - Modern syntax — Pattern matching, pipeline operator, unified
{% end %}, null coalescing, optional chaining. - Zero dependencies — Pure Python, includes native
Markupimplementation.
pip install kida-templatesRequires Python 3.14+
| 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 page (StringBuilder, fastest) |
template.render_block(name, **ctx) |
Single block (fragments, HTMX) |
template.render_stream(**ctx) |
Generator (chunked HTTP, SSE) |
template.render_async(**ctx) |
Async buffered output |
template.render_stream_async(**ctx) |
Async streaming (for {% async for %}) |
template.list_blocks() |
Block names for validation |
RenderedTemplate(template, ctx) |
Lazy iterable wrapper for streaming |
| Feature | Description | Docs |
|---|---|---|
| Template Syntax | Variables, filters, control flow, pattern matching | Syntax → |
| Inheritance | Template extends, blocks, includes | Inheritance → |
| Filters & Tests | 50+ built-in filters, custom filter registration | Filters → |
| Streaming | Statement-level generator rendering via render_stream() |
Streaming → |
| Async Support | Native async for, await in templates |
Async → |
| Caching | Fragment caching with TTL support | Caching → |
| Components & Slots | {% def %}, {% call %}, default + named {% slot %} |
Functions → |
| Block Rendering | render_block(), render_with_blocks() for fragments and layout composition |
Framework Integration → |
| Introspection | template_metadata(), block_metadata(), validate_context() for frameworks |
Analysis → |
| Partial Evaluation | Compile-time evaluation via static_context |
Advanced → |
| Block Recompilation | Recompile only changed blocks in live templates | Advanced → |
| Extensibility | Custom filters, tests, globals, loaders | Extending → |
| T-Strings (PEP 750) | k() auto-escaping, r() composable regex (Python 3.14+) |
T-Strings → |
| HTMX Helpers | hx_request(), hx_target(), csrf_token() for partials |
Custom Globals → |
| Worker Auto-Tuning | get_optimal_workers(), should_parallelize() for parallel render |
Workers → |
📚 Full documentation: lbliii.github.io/kida
File-based Templates — Load from filesystem
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
template = env.get_template("page.html")
print(template.render(title="Hello", content="World"))Template Inheritance — Extend base templates
base.html:
<!DOCTYPE html>
<html>
<body>
{% block content %}{% end %}
</body>
</html>
page.html:
{% extends "base.html" %}
{% block content %}
<h1>{{ title }}</h1>
<p>{{ content }}</p>
{% end %}
Control Flow — Conditionals, loops, pattern matching
{% if user.is_active %}
<p>Welcome, {{ user.name }}!</p>
{% end %}
{% for item in items %}
<li>{{ item.name }}</li>
{% end %}
{% match status %}
{% case "active" %}
Active user
{% case "pending" %}
Pending verification
{% case _ %}
Unknown status
{% end %}
Components & Named Slots — Reusable UI composition
{% 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 %}
{% slot %} is the default slot. Named slot blocks inside {% call %} map to
matching placeholders in {% def %}.
Filters & Pipelines — Transform values
{# Traditional syntax #}
{{ title | escape | capitalize | truncate(50) }}
{# Pipeline operator #}
{{ title |> escape |> capitalize |> truncate(50) }}
{# Custom filters #}
{{ items | sort(attribute="name") | first }}
Streaming Rendering — Yield chunks as they're ready
from kida import Environment
env = Environment()
template = env.from_string("""
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% end %}
</ul>
""")
# Generator: yields each statement as a string chunk
for chunk in template.render_stream(items=["a", "b", "c"]):
print(chunk, end="")
# RenderedTemplate: lazy iterable wrapper
from kida import RenderedTemplate
rendered = RenderedTemplate(template, {"items": ["a", "b", "c"]})
for chunk in rendered:
send_to_client(chunk)Works with inheritance ({% extends %}), includes, and all control flow. Blocks like {% capture %} and {% spaceless %} buffer internally and yield the processed result.
Async Templates — Await in templates
{% async for item in fetch_items() %}
{{ item }}
{% end %}
{{ await get_user() }}Fragment Caching — Cache expensive blocks
{% cache "navigation" %}
{% for item in nav_items %}
<a href="{{ item.url }}">{{ item.title }}</a>
{% end %}
{% end %}
Block Rendering — Fragments and layout composition
# Render a single block (HTMX partials, cached nav)
html = template.render_block("content", title="Hello")
# Compose layout with pre-rendered blocks
layout = env.get_template("_layout.html")
html = layout.render_with_blocks({"content": inner_html}, title="Page")| Use case | Key APIs | Example |
|---|---|---|
| Static sites | render(), fragment cache, bytecode cache |
Bengal |
| Dynamic web | render_block(), render_stream(), render_with_blocks() |
Chirp |
| Streaming / SSE | render_stream(), render_stream_async() |
Chunked HTTP, LLM streaming |
| Framework integration | template_metadata(), validate_block_exists(), get_structure() |
Build adapters, validate routes |
Compilation Pipeline — AST-native
Template Source → Lexer → Parser → Kida AST → Compiler → Python AST → exec()
Kida generates ast.Module objects directly. This enables:
- Structured code manipulation — Transform and optimize AST nodes
- Compile-time optimization — Dead code elimination, constant folding
- Precise error source mapping — Exact line/column in template source
Dual-Mode Rendering — StringBuilder + streaming generator
# render() — StringBuilder (fastest, default)
_out.append(...)
return "".join(_out)
# render_stream() — Generator (streaming, chunked HTTP)
yield ...The compiler generates both modes from a single template. render() uses StringBuilder for maximum throughput. render_stream() uses Python generators for statement-level streaming — ideal for chunked HTTP responses and Server-Sent Events.
Thread Safety — Free-threading ready
All public APIs are thread-safe by design:
- Template compilation — Idempotent (same input → same output)
- Rendering — Uses only local state (StringBuilder pattern)
- Environment — Copy-on-write for filters/tests/globals
- LRU caches — Atomic operations
Module declares itself GIL-independent via _Py_mod_gil = 0 (PEP 703).
- Simple render — ~0.12ms
- Complex template — ~2.1ms
- Concurrent (8 threads) — ~0.15ms avg under Python 3.14t free-threading
See benchmarks/README.md and benchmarks/RESULTS.md for full Kida vs Jinja2 comparison.
| Section | Description |
|---|---|
| Get Started | Installation and quickstart |
| Syntax | Template language reference |
| Usage | Loading, rendering, escaping |
| Framework Integration | Block rendering, introspection, adapters |
| Extending | Custom filters, tests, loaders |
| Reference | Complete API documentation |
| Tutorials | Jinja2 migration, Flask integration |
git clone https://github.com/lbliii/kida.git
cd kida
# Uses Python 3.14t by default (.python-version)
uv sync --group dev --python 3.14t
PYTHON_GIL=0 uv run --python 3.14t pytestA structured reactive stack — every layer written in pure Python for 3.14t free-threading.
| ᓚᘏᗢ | Bengal | Static site generator | Docs |
| ∿∿ | Purr | Content runtime | — |
| ⌁⌁ | Chirp | Web framework | Docs |
| =^..^= | Pounce | ASGI server | Docs |
| )彡 | Kida | Template engine ← You are here | Docs |
| ฅᨐฅ | Patitas | Markdown parser | Docs |
| ⌾⌾⌾ | Rosettes | Syntax highlighter | Docs |
Python-native. Free-threading ready. No npm required.
MIT License — see LICENSE for details.