Environment
Central configuration and template management hub.
from kida import Environment, FileSystemLoader
env = Environment(
loader=FileSystemLoader("templates/"),
autoescape=True,
)
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
loader |
Loader |
None |
Template source provider |
autoescape |
bool | Callable |
True |
HTML auto-escaping |
auto_reload |
bool |
True |
Check for source changes |
cache_size |
int |
400 |
Max cached templates |
fragment_cache_size |
int |
1000 |
Max cached fragments |
fragment_ttl |
float |
300.0 |
Fragment TTL (seconds) |
Methods
get_template(name)
Load and cache a template by name.
template = env.get_template("page.html")
Raises: TemplateNotFoundError, TemplateSyntaxError
from_string(source, name=None)
Compile a template from string (not cached in the template cache).
template = env.from_string("Hello, {{ name }}!")
Bytecode caching: If you have a
bytecode_cacheconfigured, passname=to enable it. Without a name, there's no stable cache key, so the bytecode cache is bypassed. AUserWarningis emitted if you callfrom_string()withoutname=when a bytecode cache is active.
render(template_name, **context)
Load and render in one step.
html = env.render("page.html", title="Hello", items=items)
render_string(source, **context)
Compile and render string in one step.
html = env.render_string("{{ x * 2 }}", x=21)
add_filter(name, func)
Register a custom filter.
env.add_filter("double", lambda x: x * 2)
add_test(name, func)
Register a custom test.
env.add_test("even", lambda x: x % 2 == 0)
add_global(name, value)
Add a global variable.
env.add_global("site_name", "My Site")
filter() (decorator)
Decorator to register a filter.
@env.filter()
def double(value):
return value * 2
test() (decorator)
Decorator to register a test.
@env.test()
def is_even(value):
return value % 2 == 0
cache_info()
Get cache statistics.
info = env.cache_info()
# {'template': {...}, 'fragment': {...}}
clear_cache(include_bytecode=False)
Clear all caches.
env.clear_cache()
Template
Compiled template with render interface.
Methods
render(**context)
Render template with context.
html = template.render(name="World", items=[1, 2, 3])
render_async(**context)
Render template asynchronously.
html = await template.render_async(items=async_generator())
render_stream_async(**context)
Render template as an async stream. Supports native{% async for %} and {{ await }}constructs. Also works on sync templates (wraps the sync stream).
async for chunk in template.render_stream_async(items=async_iterable):
send_to_client(chunk)
Raises: RuntimeErrorif no render function is available.
render_block_stream_async(block_name, **context)
Render a single block as an async stream. Falls back to wrapping the sync block stream if no async variant exists.
async for chunk in template.render_block_stream_async("content", items=data):
send_to_client(chunk)
Raises: KeyErrorif the block does not exist.
Properties
| Property | Type | Description |
|---|---|---|
name |
str | None |
Template name |
filename |
str | None |
Source filename |
is_async |
bool |
True if template uses {% async for %} or {{ await }} |
Note: Calling
render()orrender_stream()on a template whereis_asyncisTrueraisesTemplateRuntimeError. Userender_stream_async()instead.
Loaders
FileSystemLoader
Load templates from filesystem directories.
from kida import FileSystemLoader
# Single directory
loader = FileSystemLoader("templates/")
# Multiple directories (searched in order)
loader = FileSystemLoader(["templates/", "shared/"])
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
paths |
str | Path | list |
Required | Search paths |
encoding |
str |
"utf-8" |
File encoding |
Methods
get_source(name)→tuple[str, str]list_templates()→list[str]
DictLoader
Load templates from a dictionary.
from kida import DictLoader
loader = DictLoader({
"base.html": "<html>{% block content %}{% end %}</html>",
"page.html": "{% extends 'base.html' %}...",
})
ChoiceLoader
Try multiple loaders in order, returning the first match.
from kida import ChoiceLoader, FileSystemLoader
loader = ChoiceLoader([
FileSystemLoader("themes/custom/"),
FileSystemLoader("themes/default/"),
])
Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
loaders |
list[Loader] |
Loaders to try in order |
Methods
get_source(name)→tuple[str, str | None]— Returns first successful matchlist_templates()→list[str]— Merged, deduplicated, sorted list from all loaders
PrefixLoader
Namespace templates by prefix, delegating to per-prefix loaders.
from kida import PrefixLoader, FileSystemLoader
loader = PrefixLoader({
"app": FileSystemLoader("templates/app/"),
"admin": FileSystemLoader("templates/admin/"),
})
# env.get_template("app/index.html") → templates/app/index.html
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mapping |
dict[str, Loader] |
Required | Prefix → loader mapping |
delimiter |
str |
"/" |
Prefix delimiter |
Methods
get_source(name)→tuple[str, str | None]— Splits on delimiter, delegates to prefix loaderlist_templates()→list[str]— All templates with prefix prepended
PackageLoader
Load templates from an installed Python package viaimportlib.resources.
from kida import PackageLoader
loader = PackageLoader("my_app", "templates")
# env.get_template("pages/index.html") → my_app/templates/pages/index.html
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
package_name |
str |
Required | Dotted Python package name |
package_path |
str |
"templates" |
Subdirectory within the package |
encoding |
str |
"utf-8" |
File encoding |
Methods
get_source(name)→tuple[str, str | None]— Loads from package resourceslist_templates()→list[str]— All templates in the package directory (recursive)
FunctionLoader
Wrap a callable as a loader.
from kida import FunctionLoader
loader = FunctionLoader(lambda name: templates.get(name))
Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
load_func |
Callable[[str], str | tuple[str, str | None] | None] |
Returns source,(source, filename), or None |
Methods
get_source(name)→tuple[str, str | None]— Callsload_funcand normalizes resultlist_templates()→list[str]— Always returns[](cannot enumerate)
Exceptions
TemplateError
Base class for all template errors.
TemplateSyntaxError
Invalid template syntax.
from kida import TemplateSyntaxError
try:
env.from_string("{% if x %}") # Missing end
except TemplateSyntaxError as e:
print(e)
TemplateNotFoundError
Template file not found.
from kida import TemplateNotFoundError
try:
env.get_template("nonexistent.html")
except TemplateNotFoundError as e:
print(e)
UndefinedError
Accessing undefined variable.
from kida import UndefinedError
try:
env.from_string("{{ missing }}").render()
except UndefinedError as e:
print(e)
Markup
HTML-safe string wrapper.
from kida import Markup
# Create safe HTML
safe = Markup("<b>Bold</b>")
# Escape unsafe content
escaped = Markup.escape("<script>")
# <script>
# Format with escaping
result = Markup("<p>{}</p>").format(user_input)
Class Methods
| Method | Description |
|---|---|
escape(s) |
Escape string and return Markup |
Operations
| Operation | Behavior |
|---|---|
Markup + str |
str is escaped |
Markup + Markup |
Concatenated as-is |
Markup.format(...) |
Arguments are escaped |
LoopContext
Available asloop variable inside {% for %}loops.
| Property | Type | Description |
|---|---|---|
index |
int |
1-based index |
index0 |
int |
0-based index |
first |
bool |
True on first iteration |
last |
bool |
True on last iteration |
length |
int |
Total items |
revindex |
int |
Reverse 1-based index |
revindex0 |
int |
Reverse 0-based index |
{% for item in items %}
{{ loop.index }}/{{ loop.length }}
{% end %}
AsyncLoopContext
Available asloop variable inside {% async for %} loops. Provides index-forward properties only — properties that require knowing the total size raise TemplateRuntimeErrorsince async iterables have no known length.
| Property | Type | Description |
|---|---|---|
index |
int |
1-based index |
index0 |
int |
0-based index |
first |
bool |
True on first iteration |
previtem |
Any | None |
Previous item (Noneon first) |
cycle(*values) |
method | Cycle through values |
last |
— | RaisesTemplateRuntimeError |
length |
— | RaisesTemplateRuntimeError |
revindex |
— | RaisesTemplateRuntimeError |
revindex0 |
— | RaisesTemplateRuntimeError |
nextitem |
— | RaisesTemplateRuntimeError |
{% async for user in fetch_users() %}
{{ loop.index }}: {{ user.name }}
{% if loop.first %}(first!){% end %}
{% end %}
RenderContext
Per-render state management via ContextVar.
from kida.render_context import (
RenderContext,
render_context,
get_render_context,
)
RenderContext Dataclass
| Attribute | Type | Description |
|---|---|---|
template_name |
str | None |
Current template name |
filename |
str | None |
Source file path |
line |
int |
Current line (for errors) |
include_depth |
int |
Include nesting depth |
max_include_depth |
int |
Max depth (default: 50) |
cached_blocks |
dict[str, str] |
Site-scoped block cache |
Methods
| Method | Description |
|---|---|
check_include_depth(name) |
Raise if depth exceeded |
child_context(name) |
Create child with incremented depth |
Functions
| Function | Description |
|---|---|
get_render_context() |
Get current context (None if not rendering) |
get_render_context_required() |
Get context or raise RuntimeError |
render_context(...) |
Context manager for render scope |
RenderAccumulator
Opt-in profiling for template rendering. When enabled viaprofiled_render(), the compiler-emitted instrumentation automatically tracks:
- Blocks — render timing (milliseconds) and call counts
- Filters — call counts per filter name
- Macros — call counts per
{% def %}name - Includes — counts per included template
Zero overhead when profiling is disabled — the instrumentation gates on a falsy check.
from kida.render_accumulator import (
RenderAccumulator,
profiled_render,
get_accumulator,
)
Usage
with profiled_render() as metrics:
html = template.render(page=page)
summary = metrics.summary()
# {
# "total_ms": 12.5,
# "blocks": {"content": {"ms": 8.2, "calls": 1}, "nav": {"ms": 1.1, "calls": 1}},
# "filters": {"upper": 3, "truncate": 2},
# "macros": {"card": 5},
# "includes": {"header.html": 1},
# }
RenderAccumulator Properties
| Property | Type | Description |
|---|---|---|
block_timings |
dict[str, BlockTiming] |
Block render times |
macro_calls |
dict[str, int] |
Macro call counts |
include_counts |
dict[str, int] |
Include counts |
filter_calls |
dict[str, int] |
Filter usage counts |
total_duration_ms |
float |
Total render time |
Methods
| Method | Description |
|---|---|
record_block(name, ms) |
Record block timing |
record_macro(name) |
Record macro call |
record_include(name) |
Record include |
record_filter(name) |
Record filter usage |
summary() |
Get metrics dict |
See Also
- Filters Reference — All built-in filters
- Tests Reference — All built-in tests
- Configuration — All options