API Reference

Core classes and methods

14 min read 2894 words

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)
static_context dict | None None Values for compile-time partial evaluation
strict_undefined bool False Raise on missing attribute access
jinja2_compat_warnings bool False Warn on{% set %}scoping differences
validate_calls bool False Validate{% def %}call sites at compile time

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 abytecode_cache configured, pass name= to enable it. Without a name, there's no stable cache key, so the bytecode cache is bypassed. A UserWarning is emitted if you call from_string() without name=when a bytecode cache is active.

Partial evaluation: Passstatic_context={...} to evaluate expressions at compile time. Overrides Environment's static_contextfor this call.

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.

Rendering (all users)

render(**context)

Render template with context.

html = template.render(name="World", items=[1, 2, 3])

render_async(**context)

Render template asynchronously (thread-pool wrapper for sync templates).

html = await template.render_async(items=async_generator())

render_stream(**context)

Render template as a sync generator of string chunks. Yields at statement boundaries for chunked HTTP and streaming.

for chunk in template.render_stream(items=data):
    send_to_client(chunk)

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.

Block rendering (fragments, frameworks)

render_block(block_name, **context)

Render a single block from the template. Supports inherited blocks: when the template extends a parent, you can render parent-only blocks by name (e.g.render_block("content") on a child that extends a base defining content). Child overrides still win; super()is not supported.

html = template.render_block("content", title="Hello")

Raises: KeyErrorif the block does not exist in the template or any parent.

render_with_blocks(block_overrides, **context)

Render template with pre-rendered HTML injected into blocks. Enables programmatic layout composition without{% extends %}in the template source.

layout = env.get_template("_layout.html")
html = layout.render_with_blocks({"content": inner_html}, title="Page")

Each key in block_overrides names a block; the value is a pre-rendered HTML string. Unknown block names now raise TemplateRuntimeErrorwith did-you-mean suggestions.

render_block_stream_async(block_name, **context)

Render a single block as an async stream. Supports inherited blocks likerender_block(). 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.

list_blocks()

List all blocks available forrender_block(), including inherited blocks.

blocks = template.list_blocks()
# ['title', 'nav', 'content', 'footer']

Component Introspection

list_defs()

List all{% def %}component names in the template.

names = template.list_defs()
# ['card', 'nav_link', 'badge']

def_metadata()

Return metadata for all{% def %} components in the template. Returns a dict[str, DefMetadata].

meta = template.def_metadata()
card = meta["card"]
print(card.name)              # "card"
print(card.template_name)     # "components/card.html"
print(card.lineno)            # 3
print(card.params)            # (DefParamInfo(name='title', annotation='str', ...), ...)
print(card.slots)             # ('actions', 'footer')
print(card.has_default_slot)  # True

DefMetadata fields:

Field Type Description
name str Component name
template_name str | None Source template
lineno int Line number of{% def %}
params tuple[DefParamInfo, ...] Parameter metadata
slots tuple[str, ...] Named slot names
has_default_slot bool Whether{% slot %}(unnamed) exists

DefParamInfo fields:

Field Type Description
name str Parameter name
annotation str | None Type annotation (e.g."str", "int")
has_default bool Whether a default value is defined
is_required bool Trueif no default value

warnings

Compile-time warnings collected during template compilation.

for w in template.warnings:
    print(w.code, w.message, w.lineno)

Template Introspection (frameworks, build systems)

template_metadata()

Return full template analysis (blocks, extends, dependencies). ReturnsNoneif AST was not preserved.

meta = template.template_metadata()
if meta:
    print(meta.extends, meta.blocks.keys())
    regions = meta.regions()  # Only {% region %} blocks (for OOB discovery)

block_metadata()

Return per-block analysis (purity, cache scope, inferred role).

blocks = template.block_metadata()
nav = blocks.get("nav")
if nav and nav.cache_scope == "site":
    ...

depends_on()

Return all context paths this template may access.

deps = template.depends_on()
# frozenset({'page.title', 'site.pages'})

required_context()

Return top-level variable names the template needs.

names = template.required_context()
# frozenset({'page', 'site'})

validate_context(context)

Check a context dict for missing variables. Returns list of missing names.

missing = template.validate_context(user_context)
if missing:
    raise ValueError(f"Missing: {missing}")

is_cacheable(block_name=None)

Check if a block (or all blocks) can be safely cached.

template.is_cacheable("nav")   # True if nav is cacheable
template.is_cacheable()       # True only if all blocks cacheable

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 }}
warnings list[TemplateWarning] Compile-time warnings (precedence, coercion, migration)

Note: Callingrender() or render_stream() on a template where is_async is True raises TemplateRuntimeError. Use render_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 match
  • list_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 loader
  • list_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 resources
  • list_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] — Calls load_funcand normalizes result
  • list_templates()list[str] — Always returns [](cannot enumerate)

Composition Helpers

Validation and structure helpers for frameworks. See Framework Integration for full usage.

from kida.composition import (
    validate_block_exists,
    validate_template_block,
    get_structure,
    block_role_for_framework,
)

validate_block_exists(env, template_name, block_name) → bool

Check if a block exists in a template (including inherited blocks). ReturnsFalseif template not found or block missing.

if validate_block_exists(env, "skills/page.html", "page_content"):
    html = env.get_template("skills/page.html").render_block("page_content", ...)

validate_template_block(template, block_name) → bool

Check if a block exists in a loaded Template instance.

get_structure(env, template_name) → TemplateStructureManifest | None

Get lightweight structure manifest (block names, extends, dependencies). Cached by Environment.

struct = get_structure(env, "page.html")
if struct and "page_root" in struct.block_names:
    ...

TemplateStructureManifest fields:

Field Type Description
name str | None Template name
extends str | None Parent template from{% extends %}
block_names tuple[str, ...] Ordered block names
block_hashes dict[str, str] Per-block structural hashes
dependencies frozenset[str] Context paths accessed

block_role_for_framework(block_metadata, ...) → str | None

Classify block metadata into framework roles:"fragment", "page_root", or None.


Exceptions

TemplateError

Base class for all template errors. All Kida exceptions carry anErrorCode accessible via exc.code.

TemplateSyntaxError

Invalid template syntax.

from kida import TemplateSyntaxError

try:
    env.from_string("{% if x %}")  # Missing end
except TemplateSyntaxError as e:
    print(e)

TemplateRuntimeError

Error during template rendering. Includescomponent_stack for errors inside {% def %}components:

from kida import TemplateRuntimeError

try:
    template.render(items=None)
except TemplateRuntimeError as e:
    print(e.code)             # ErrorCode enum value
    print(e.component_stack)  # [(def_name, lineno, template_name), ...]
    print(e.format_compact()) # Formatted error with source snippet

TemplateNotFoundError

Template file not found. Includes caller context (requesting template and line number).

from kida import TemplateNotFoundError

try:
    env.get_template("nonexistent.html")
except TemplateNotFoundError as e:
    print(e)

UndefinedError

Accessing undefined variable or attribute. Thekindfield distinguishes between variable, attribute, and key lookups.

from kida import UndefinedError

try:
    env.from_string("{{ missing }}").render()
except UndefinedError as e:
    print(e.kind)  # "variable", "attribute", or "key"
    print(e)       # "Undefined variable 'missing' in <string>:1"

With strict_undefined=True, attribute access errors also include component context:

env = Environment(strict_undefined=True)
# "Undefined attribute 'typo' on User object in page.html:5"

SecurityError

Raised bySandboxedEnvironmentwhen a template violates the security policy. Carries error codes K-SEC-001 through K-SEC-005.

Warning Classes

Compile-time warnings emitted during template compilation:

Class Code Description
PrecedenceWarning K-WARN-001 | binds tighter than ??
CoercionWarning Silent type coercion in filters (e.g."abc" | float0.0)
MigrationWarning K-WARN-002 {% set %}scoping differs from Jinja2

These are standard Python warnings and can be filtered withwarnings.filterwarnings.


Markup

HTML-safe string wrapper.

from kida import Markup

# Create safe HTML
safe = Markup("<b>Bold</b>")

# Escape unsafe content
escaped = Markup.escape("<script>")
# &lt;script&gt;

# 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,
    async_render_context,
    get_render_context,
    get_render_context_required,
)

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(template_name=None, *, source=None) Create child for include/embed with incremented depth
child_context_for_extends(parent_name, *, source=None) Create child for extends with incremented extends_depth
get_meta(key, default=None) Get framework metadata (HTMX, CSRF, etc.)
set_meta(key, value) Set framework metadata before rendering

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
async_render_context(...) Async context manager for render scope

Low-Level APIs

For cases where the context manager isn't suitable (e.g. nested include/embed that need manual restore):

Function Description
set_render_context(ctx) Set a RenderContext, returns reset token
reset_render_context(token) Restore previous context using token fromset_render_context()

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