Module

environment.core

Core Environment class for Kida template system.

The Environment is the central hub for template configuration, compilation, and caching. It manages loaders, filters, tests, and global variables.

Thread-Safety:

  • Immutable configuration after construction
  • Copy-on-write for filters/tests/globals (no locking)
  • LRU caches use atomic pointer swaps
  • Safe for concurrentget_template()andrender()calls

Example:

>>> from kida import Environment, FileSystemLoader
>>> env = Environment(
...     loader=FileSystemLoader("templates/"),
...     autoescape=True,
... )
>>> env.get_template("page.html").render(page=page)

Classes

Environment 50
Central configuration and template management hub. The Environment holds all template engine setti…

Central configuration and template management hub.

The Environment holds all template engine settings and provides the primary API for loading and rendering templates. It manages three key concerns:

  1. Template Loading: Via configurable loaders (filesystem, dict, etc.)
  2. Compilation Settings: Autoescape, strict undefined handling
  3. Runtime Context: Filters, tests, and global variables

Strict Mode: Undefined variables raiseUndefinedErrorinstead of returning empty string. Catches typos and missing context variables at render time.

    >>> env = Environment()
    >>> env.from_string("{{ typo_var }}").render()
UndefinedError: Undefined variable 'typo_var' in <template>:1

    >>> env.from_string("{{ optional | default('N/A') }}").render()
    'N/A'

Caching:

Three cache layers for optimal performance:

  • Bytecode cache (disk): Persistent compiled bytecode via marshal.

    Auto-enabled for FileSystemLoader in `__pycache__/kida/`.
    
    Current cold-start gain is modest (~7-8% median in
    
    `benchmarks/benchmark_cold_start.py`); most startup time is import
    
    cost, so lazy imports or pre-compilation are required for larger
    
    improvements.
    
  • Template cache (memory): Compiled Template objects (keyed by name)

  • Fragment cache (memory):{% cache key %}block outputs

    >> env.cache_info()

{'template': {'size': 5, 'max_size': 400, 'hits': 100, 'misses': 5},
     'fragment': {'size': 12, 'max_size': 1000, 'hits': 50, 'misses': 12},
     'bytecode': {'file_count': 10, 'total_bytes': 45000}}

Attributes

Name Type Description
loader Loader | None

Template source provider (FileSystemLoader, DictLoader, etc.)

autoescape bool | Callable[[str | None], bool]

HTML auto-escaping. True, False, or callable(name) → bool

auto_reload bool

Check template modification times (default: True)

strict_none bool

Fail early on None comparisons during sorting (default: False)

preserve_ast bool
cache_size int

Maximum compiled templates to cache (default: 400)

fragment_cache_size int

Maximum{% cache %}fragment entries (default: 1000)

fragment_ttl float

Fragment cache TTL in seconds (default: 300.0)

bytecode_cache BytecodeCache | bool | None

Persistent bytecode cache configuration: - None (default): Auto-enabled for FileSystemLoader - False: Explicitly disabled - BytecodeCache instance: Custom cache directory

_bytecode_cache BytecodeCache | None
block_start str
block_end str
variable_start str
variable_end str
comment_start str
comment_end str
trim_blocks bool
lstrip_blocks bool
fstring_coalescing bool
pure_filters set[str]
globals dict[str, Any]

Variables available in all templates (includes Python builtins) Thread-Safety: All operations are safe for concurrent use: - Configuration is immutable after__post_init__-add_filter(),add_test(),add_global()use copy-on-write -get_template()uses lock-free LRU cache with atomic operations -render()uses only local state (StringBuilder pattern)

_filters dict[str, Callable[..., Any]]
_tests dict[str, Callable[..., Any]]
_cache LRUCache[str, Template]
_fragment_cache LRUCache[str, str]
_template_hashes dict[str, str]
_analysis_cache dict[str, Any]

Methods

filters 0 FilterRegistry
Get filters as dict-like registry.
property
def filters(self) -> FilterRegistry
Returns
FilterRegistry
tests 0 FilterRegistry
Get tests as dict-like registry.
property
def tests(self) -> FilterRegistry
Returns
FilterRegistry
add_filter 2
Add a filter (copy-on-write).
def add_filter(self, name: str, func: Callable[..., Any]) -> None
Parameters
Name Type Description
name

Filter name (used in templates as {{ x | name }})

func

Filter function

add_test 2
Add a test (copy-on-write).
def add_test(self, name: str, func: Callable[..., Any]) -> None
Parameters
Name Type Description
name

Test name (used in templates as {% if x is name %})

func

Test function returning bool

add_global 2
Add a global variable (copy-on-write).
def add_global(self, name: str, value: Any) -> None
Parameters
Name Type Description
name

Global name (used in templates as {{ name }})

value

Any value (variable, function, etc.)

update_filters 1
Add multiple filters at once (copy-on-write).
def update_filters(self, filters: dict[str, Callable[..., Any]]) -> None
Parameters
Name Type Description
filters

Dict mapping filter names to functions

update_tests 1
Add multiple tests at once (copy-on-write).
def update_tests(self, tests: dict[str, Callable[..., Any]]) -> None
Parameters
Name Type Description
tests

Dict mapping test names to functions

get_template 1 Template
Load and cache a template by name.
def get_template(self, name: str) -> Template
Parameters
Name Type Description
name

Template identifier (e.g., "index.html")

Returns
Template Compiled Template object
from_string 2 Template
Compile a template from a string.
def from_string(self, source: str, name: str | None = None) -> Template
Parameters
Name Type Description
source

Template source code

name

Optional template name for error messages

Default:None
Returns
Template Compiled Template object
clear_template_cache 1
Clear template cache (optional, for external invalidation). Useful when an ext…
def clear_template_cache(self, names: list[str] | None = None) -> None

Clear template cache (optional, for external invalidation).

Useful when an external system (e.g., Bengal) detects template changes and wants to force cache invalidation without waiting for hash check.

Parameters
Name Type Description
names

Specific template names to clear, or None to clear all

Default:None
render 1 str
Render a template by name with context. Convenience method combining get_templ…
def render(self, template_name: str, *args: Any, **kwargs: Any) -> str

Render a template by name with context.

Convenience method combining get_template() and render().

Parameters
Name Type Description
template_name

Template identifier (e.g., "index.html") *args: Single dict of context variables (optional) **kwargs: Context variables as keyword arguments

Returns
str Rendered template as string
render_string 1 str
Compile and render a template string. Convenience method combining from_string…
def render_string(self, source: str, *args: Any, **kwargs: Any) -> str

Compile and render a template string.

Convenience method combining from_string() and render().

Parameters
Name Type Description
source

Template source code *args: Single dict of context variables (optional) **kwargs: Context variables as keyword arguments

Returns
str Rendered template as string
filter 1 Callable[[Callable[..., …
Decorator to register a filter function.
def filter(self, name: str | None = None) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Parameters
Name Type Description
name

Filter name (defaults to function name)

Default:None
Returns
Callable[[Callable[..., Any]], Callable[..., Any]] Decorator function
test 1 Callable[[Callable[..., …
Decorator to register a test function.
def test(self, name: str | None = None) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Parameters
Name Type Description
name

Test name (defaults to function name)

Default:None
Returns
Callable[[Callable[..., Any]], Callable[..., Any]] Decorator function
select_autoescape 1 bool
Determine if autoescape should be enabled for a template.
def select_autoescape(self, name: str | None) -> bool
Parameters
Name Type Description
name

Template name (may be None for string templates)

Returns
bool True if autoescape should be enabled
clear_cache 1
Clear all cached templates and fragments. Call this to release memory when tem…
def clear_cache(self, include_bytecode: bool = False) -> None

Clear all cached templates and fragments.

Call this to release memory when templates are no longer needed, or when template files have been modified and need reloading.

Parameters
Name Type Description
include_bytecode

Also clear persistent bytecode cache (default: False)

Default:False
clear_fragment_cache 0
Clear only the fragment cache (keep template cache).
def clear_fragment_cache(self) -> None
clear_bytecode_cache 0 int
Clear persistent bytecode cache.
def clear_bytecode_cache(self) -> int
Returns
int Number of cache files removed.
cache_info 0 dict[str, Any]
Return cache statistics. Returns cache statistics for template and fragment ca…
def cache_info(self) -> dict[str, Any]

Return cache statistics.

Returns cache statistics for template and fragment caches.

Returns
dict[str, Any] Dict with cache statistics including hit/miss rates.
Internal Methods 4
__post_init__ 0
Initialize derived configuration.
def __post_init__(self) -> None
_resolve_bytecode_cache 0 BytecodeCache | None
Resolve bytecode cache from configuration. Auto-detection logic: - If byte…
def _resolve_bytecode_cache(self) -> BytecodeCache | None

Resolve bytecode cache from configuration.

Auto-detection logic:

  • If bytecode_cache is False: disabled
  • If bytecode_cache is BytecodeCache: use it
  • If bytecode_cache is None and loader is FileSystemLoader:
    auto-create cache in first search path's __pycache__/kida/
    
Returns
BytecodeCache | None Resolved BytecodeCache or None if disabled/unavailable.
_compile 3 Template
Compile template source to Template object. Uses bytecode cache when configure…
def _compile(self, source: str, name: str | None, filename: str | None) -> Template

Compile template source to Template object.

Uses bytecode cache when configured for fast cold-start. Preserves AST for introspection when self.preserve_ast=True (default).

Parameters
Name Type Description
source
name
filename
Returns
Template
_is_template_stale 1 bool
Check if a cached template is stale (source changed). Compares current source …
def _is_template_stale(self, name: str) -> bool

Check if a cached template is stale (source changed).

Compares current source hash with cached hash. Returns True if:

  • No cached hash exists (first load)
  • Current source hash differs from cached hash
Parameters
Name Type Description
name

Template identifier

Returns
bool True if template source changed, False if unchanged