Kida 0.2.4

Composition API, inherited blocks, slot context inheritance, string concatenation

Status: Released March 6, 2026.

This release adds the composition API for framework integration, inherited block support inrender_block(), slot context inheritance for layout chains, and polymorphic string concatenation.

Highlights

  • Composition APIvalidate_block_exists(), get_structure(), and TemplateStructureManifestfor Chirp, Dori, and custom adapters.
  • Inherited blocksrender_block() and list_blocks() now include parent blocks; render sidebarfrom a child that extends a base.
  • Slot context inheritancecaller()in nested def/call/slot chains correctly resolves to the def's caller (Dori/chirpui layout support).
  • String concatenation — Polymorphic+: numeric add when both operands are numeric, else string concat.
  • Bytecode cache fix — Atomic write with unique temp files prevents concurrent writer races.

Added

Composition API

Newkida.compositionmodule for frameworks that compose templates via block rendering:

from kida import Environment, FileSystemLoader
from kida.composition import validate_block_exists, get_structure

env = Environment(loader=FileSystemLoader("templates/"))

# Validate before render_block
if validate_block_exists(env, "page.html", "content"):
    html = env.get_template("page.html").render_block("content", ...)

# Lightweight structure manifest for composition planning
struct = get_structure(env, "page.html")
if struct and "page_root" in struct.block_names:
    ...

Inherited Block Support

render_block() and list_blocks()now include blocks inherited from parent templates:

# Child extends base.html which defines {% block sidebar %}
template = env.get_template("skills/page.html")
html = template.render_block("sidebar", site=site)  # Works — sidebar from parent
blocks = template.list_blocks()  # Includes inherited blocks

Slot Context Inheritance

Nested{% def %} / {% call %} / {% slot %} chains now correctly scope caller(). When a slot body containscaller(), it resolves to the def's caller, not an inner wrapper. Enables layout chains like Dori and ChirpUI.

String Concatenation

The+operator is now polymorphic:

  • Both operands numeric → addition:{{ 1 + 2 }}3
  • Otherwise → string concatenation:{{ "Hello " + name }}"Hello World"

String Escape Decoding

String literals now decode Python-style escapes:\n, \t, \r, \\, \', \", \uXXXX, \UXXXXXXXX. Invalid escapes raise LexerErrorwith location.

Parser Improvements

  • Error codesParseError carries ErrorCode (e.g. UNEXPECTED_TOKEN, UNCLOSED_BLOCK) with docs URLs.
  • Token display namesTokenType.display_namefor human-readable error messages (e.g. "identifier" instead of "name").
  • Hyphen detection{% block foo-bar %} and {% fragment foo-bar %}are rejected with a suggestion to use underscores.

Framework Integration Docs

New Framework Integration guide covers block rendering, introspection, and composition for Chirp, Bengal, and Dori.

Changed

  • Async APITemplate.render_async() is explicitly a thread-pool wrapper for sync templates; async templates should use render_stream_async().
  • Cache TTL{% cache key, ttl=... %} enforces per-fragment TTL (numeric seconds or s/m/h/dsuffixes).

Fixed

  • Bytecode cache write raceBytecodeCache.set() uses unique temp files and os.replace()for atomic replacement, preventing concurrent writer collisions.

Upgrade Notes

  1. No breaking changes — Existing templates continue to work.
  2. Composition API — Optional; addvalidate_block_exists() or get_structure()where you compose templates programmatically.
  3. Block names with hyphens — Use underscores instead (foo_bar not foo-bar).