Module

compiler.core

Kida Compiler Core — main Compiler class.

The Compiler transforms Kida AST into Python AST, then compiles to executable code objects. Uses a mixin-based design for maintainability.

Design Principles:

  1. AST-to-AST: Generateast.Module, not source strings
  2. StringBuilder: Output viabuf.append(), join at end
  3. Local caching: Cache_escape, _str, buf.appendas locals
  4. O(1) dispatch: Dict-based node type → handler lookup

Performance Optimizations:

  • LOAD_FAST for cached functions (vs LOAD_GLOBAL+ hash)
  • Method lookup cached once:_append = buf.append
  • Single''.join(buf)at return (vs repeated concatenation)
  • Line markers only for error-prone nodes (Output, For, If, etc.)

Block Inheritance:

Templates with{% extends %}generate:

  1. Block functions:_block_header(ctx, _blocks)

  2. Render function: Registers blocks, delegates to parent

        def _block_header(ctx, _blocks):
            buf = []
            # Block body...
            return ''.join(buf)
    
        def render(ctx, _blocks=None):
            if _blocks is None: _blocks = {}
            _blocks.setdefault('header', _block_header)
            return _extends('base.html', ctx, _blocks)
    

Classes

Compiler 25
Compile Kida AST to Python code objects. The Compiler transforms a Kida Template AST into an `ast.…

Compile Kida AST to Python code objects.

The Compiler transforms a Kida Template AST into anast.Module, then compiles it to a code object ready forexec(). The generated code defines arender(ctx, _blocks=None)function.

Node Dispatch: Uses O(1) dict lookup for node type → handler: python dispatch = { "Data": self._compile_data, "Output": self._compile_output, "If": self._compile_if, ... } handler = dispatch[type(node).__name__]

Line Tracking: For nodes that can cause runtime errors (Output, For, If, etc.), generates_get_render_ctx().line = Nbefore the node's code. This updates the ContextVar-stored RenderContext instead of polluting the user's ctx dict, enabling rich error messages with source line numbers while keeping user context clean.

Attributes

Name Type Description
_env

Parent Environment (for filter/test access)

_name str | None

Template name for error messages

_filename str | None

Source file path for compile()

_locals set[str]

Set of local variable names (loop vars, macro args)

_blocks dict[str, Block]

Dict of block_name → Block node (for inheritance)

_block_counter int

Counter for unique variable names

Methods

compile 3 types.CodeType
Compile template AST to code object.
def compile(self, node: TemplateNode, name: str | None = None, filename: str | None = None) -> types.CodeType
Parameters
Name Type Description
node

Root Template node

name

Template name for error messages

Default:None
filename

Source filename for error messages

Default:None
Returns
types.CodeType Compiled code object ready for exec()
Internal Methods 18
__init__ 1
def __init__(self, env: Environment)
Parameters
Name Type Description
env
_collect_blocks 1
Recursively collect all Block nodes from the AST. This ensures nested blocks (…
def _collect_blocks(self, nodes: Sequence[Node]) -> None

Recursively collect all Block nodes from the AST.

This ensures nested blocks (blocks inside blocks, blocks inside conditionals, etc.) are all registered for compilation.

Parameters
Name Type Description
nodes
_emit_output 1 ast.stmt
Generate output statement: yield (streaming) or _append (StringBuilder). All o…
def _emit_output(self, value_expr: ast.expr) -> ast.stmt

Generate output statement: yield (streaming) or _append (StringBuilder).

All output generation in compiled templates flows through this method, allowing the compiler to switch between StringBuilder and generator modes.

Parameters
Name Type Description
value_expr
Returns
ast.stmt
_compile_template 1 ast.Module
Generate Python module from template. Produces both StringBuilder functions (r…
def _compile_template(self, node: TemplateNode) -> ast.Module

Generate Python module from template.

Produces both StringBuilder functions (render, block) and generator functions (render_stream, block_stream) in a single module. The StringBuilder path is used by Template.render() and the generator path by Template.render_stream().

When async constructs are detected (AsyncFor, Await), also generates async generator functions (render_stream_async, block*_stream_async).

Parameters
Name Type Description
node
Returns
ast.Module
_make_globals_setup 1 ast.FunctionDef | None
Generate _globals_setup(ctx) from {% globals %}, {% imports %}, and top-level i…
def _make_globals_setup(self, node: TemplateNode) -> ast.FunctionDef | None

Generate _globals_setup(ctx) from {% globals %}, {% imports %}, and top-level imports.

Scans the template body for:

  1. Top-level FromImport and Import nodes (so render_block has macros in scope)
  2. Globals and Imports nodes (macros/variables for block context)

This function is called by render_block() to inject macros and variables into the block's context. Returns None if neither globals nor top-level imports exist.

Parameters
Name Type Description
node
Returns
ast.FunctionDef | None
_make_block_preamble 1 list[ast.stmt]
Common setup stmts for block functions. Non-streaming adds buf and _append for…
def _make_block_preamble(self, streaming: bool) -> list[ast.stmt]

Common setup stmts for block functions.

Non-streaming adds buf and _append for StringBuilder.

Parameters
Name Type Description
streaming
Returns
list[ast.stmt]
_make_block_function 2 ast.FunctionDef
Generate a block function: _block_name(ctx, _blocks) -> str.
def _make_block_function(self, name: str, block_node: Block) -> ast.FunctionDef
Parameters
Name Type Description
name
block_node
Returns
ast.FunctionDef
_make_render_preamble 0 list[ast.stmt]
Shared init block for render functions: if _blocks, _scope_stack, _acc.
def _make_render_preamble(self) -> list[ast.stmt]
Returns
list[ast.stmt]
_make_render_extends_body 5 list[ast.stmt]
Top-level statements, block registration, and extends return/yield.
def _make_render_extends_body(self, node: TemplateNode, extends_node: Extends, block_names: dict[str, Block], block_suffix: str, extends_helper: str) -> list[ast.stmt]
Parameters
Name Type Description
node
extends_node
block_names
block_suffix
extends_helper
Returns
list[ast.stmt]
_make_render_direct_body 2 list[ast.stmt]
No-extends path: buf setup (if not streaming), body compile, return vs yield.
def _make_render_direct_body(self, node: TemplateNode, streaming: bool) -> list[ast.stmt]
Parameters
Name Type Description
node
streaming
Returns
list[ast.stmt]
_make_render_function 1 ast.FunctionDef
Generate the render(ctx, _blocks=None) function. Optimization: Cache global fu…
def _make_render_function(self, node: TemplateNode) -> ast.FunctionDef

Generate the render(ctx, _blocks=None) function.

Optimization: Cache global function references as locals for O(1) LOAD_FAST instead of O(1) LOAD_GLOBAL + hash lookup.

For templates with extends:

def render(ctx, _blocks=None):
    if _blocks is None: _blocks = {}
    # Register child blocks
    _blocks.setdefault('name', _block_name)
    # Render parent with blocks
    return _extends('parent.html', ctx, _blocks)
Parameters
Name Type Description
node
Returns
ast.FunctionDef
_make_block_function_stream 2 ast.FunctionDef
Generate a streaming block: _block_name_stream(ctx, _blocks) -> Generator[str].
def _make_block_function_stream(self, name: str, block_node: Block) -> ast.FunctionDef

Generate a streaming block: _block_name_stream(ctx, _blocks) -> Generator[str].

Parameters
Name Type Description
name
block_node
Returns
ast.FunctionDef
_make_render_function_stream 2 ast.FunctionDef
Generate render_stream(ctx, _blocks=None) generator function. For templates wi…
def _make_render_function_stream(self, node: TemplateNode, blocks: dict[str, Block]) -> ast.FunctionDef

Generate render_stream(ctx, _blocks=None) generator function.

For templates with extends:

yield from _extends_stream('parent.html', ctx, _blocks)

For templates without extends:

yield chunks directly
Parameters
Name Type Description
node
blocks
Returns
ast.FunctionDef
_make_block_function_stream_async 2 ast.AsyncFunctionDef
Generate async streaming block: _block_name_stream_async(ctx, _blocks). Mirror…
def _make_block_function_stream_async(self, name: str, block_node: Block) -> ast.AsyncFunctionDef

Generate async streaming block: _block_name_stream_async(ctx, _blocks).

Mirrors _make_block_function_stream() but produces an async generator function (async def + yield). Used when the template contains async constructs (AsyncFor, Await).

Part of RFC: rfc-async-rendering.

Parameters
Name Type Description
name
block_node
Returns
ast.AsyncFunctionDef
_make_render_function_stream_async 2 ast.AsyncFunctionDef
Generate async render_stream_async(ctx, _blocks=None) function. Mirrors _make_…
def _make_render_function_stream_async(self, node: TemplateNode, blocks: dict[str, Block]) -> ast.AsyncFunctionDef

Generate async render_stream_async(ctx, _blocks=None) function.

Mirrors _make_render_function_stream() but produces an async generator for native async iteration over AsyncFor loops and Await expressions.

For templates with extends:

async for chunk in _extends_stream_async(parent, ctx, _blocks):
    yield chunk

For templates without extends:

yield chunks directly via async generator

Part of RFC: rfc-async-rendering.

Parameters
Name Type Description
node
blocks
Returns
ast.AsyncFunctionDef
_make_line_marker 1 ast.stmt
Generate RenderContext line update for error tracking. Generates: _get_render_…
def _make_line_marker(self, lineno: int) -> ast.stmt

Generate RenderContext line update for error tracking.

Generates: _get_render_ctx().line = lineno

This updates the ContextVar-stored RenderContext instead of polluting the user's ctx dict.

RFC: kida-contextvar-patterns

Parameters
Name Type Description
lineno
Returns
ast.stmt
_compile_node 1 list[ast.stmt]
Compile a single AST node to Python statements. Complexity: O(1) type dispatch…
def _compile_node(self, node: Node) -> list[ast.stmt]

Compile a single AST node to Python statements.

Complexity: O(1) type dispatch using class name lookup.

For nodes that can cause runtime errors, injects a line marker statement (ctx['_line'] = N) before the node's code. This enables rich error messages with source line numbers.

Parameters
Name Type Description
node
Returns
list[ast.stmt]
_get_node_dispatch 0 dict[str, Callable]
Get node type dispatch table (cached on first call).
def _get_node_dispatch(self) -> dict[str, Callable]
Returns
dict[str, Callable]