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 RLock; _template_hashes, _analysis_cache, _structure_manifest_cache are protected by _cache_lock for concurrent get_template/get_template_structure
  • Safe for concurrent get_template(), render(), and get_template_structure() calls

Example:

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

Classes

Environment 86 ▼
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 | str | Callable[[str | None], bool]

HTML auto-escaping. True, False, callable(name) → bool, or a string: "terminal" (enable, ANSI-aware mode), "true" (enable), or "false" (disable). Any other string raises ValueError.

auto_reload bool

Check template modification times (default: True)

strict_none bool

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

strict_undefined bool —
jinja2_compat_warnings bool —
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

template_aliases dict[str, str] | None —
static_context dict[str, Any] | None —
_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 —
max_extends_depth int —
max_include_depth int —
validate_calls bool —
enable_profiling bool —
enable_capture bool —
fstring_coalescing bool —
pure_filters set[str] —
inline_components bool —
optimize_translations bool —
enable_htmx_helpers bool —
extensions list[type] —
terminal_color str | None —
terminal_width int | None —
terminal_unicode bool | None —
ambiguous_width int | None —
globals dict[str, Any]

Variables available in all templates (includes Python builtins) Thread-Safety: Read and render operations are safe for concurrent use after startup: - Treat public configuration attributes as startup-only state -add_filter(), add_test(), add_global() publish copy-on-write snapshots for readers; applications must serialize competing writers - get_template() and related cache state use internal RLocks - render()uses per-call local and ContextVar state

_filters dict[str, Callable[..., Any]] —
_tests dict[str, Callable[..., bool]] —
_cache LRUCache[str, Template] —
_fragment_cache LRUCache[str, str] —
_template_hashes dict[str, str] —
_template_mtimes dict[str, int] —
_analysis_cache dict[str, TemplateMetadata] —
_structure_manifest_cache dict[str, TemplateStructureManifest] —
_cache_lock threading.RLock —
_terminal_caps TerminalCaps | None —
_extension_instances list[Any] —
_extension_tags dict[str, Any] —
_extension_compilers dict[str, Any] —
_extension_end_keywords frozenset[str] —
_filtered_globals dict[str, Any] | None —
_filtered_globals_source dict[str, Any] | None —

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
install_translations 1 ▼
Install a gettext translations object. The object must have gettext() and nget…
def install_translations(self, translations: Any) -> None

Install a gettext translations object.

The object must have gettext() and ngettext() methods (standard library gettext.GNUTranslations interface).

Parameters
Name Type Description
translations —
install_gettext_callables 2 ▼
Install gettext/ngettext functions directly. These are registered as template …
def install_gettext_callables(self, gettext: Any, ngettext: Any) -> None

Install gettext/ngettext functions directly.

These are registered as template globals_ and _n, and stored as_gettext/_ngettextfor compiler namespace access.

Parameters
Name Type Description
gettext —
ngettext —
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 If func was decorated with :func:kida.pure, the filter is automatically registered as pure, enabling compile-time evaluation when all inputs are statically known.

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.)

get_filtered_globals 0 dict[str, Any] ▼
Return globals dict with UNDEFINED values removed (cached). The result is cach…
def get_filtered_globals(self) -> dict[str, Any]

Return globals dict with UNDEFINED values removed (cached).

The result is cached, but the cache is automatically refreshed if the publicglobalsmapping has been mutated or replaced since the last computation.

Returns
dict[str, Any]
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 2 Template ▼
Load and cache a template by name.
def get_template(self, name: str, *, caller: str | None = None) -> Template
Parameters
Name Type Description
name —

Template identifier (e.g.,"index.html"). May start with ./ or ../ for resolution relative to the calling template — see caller.

caller —

Logical name of the template issuing the request. Required whenname is a relative path. Supplied automatically by {% include %}, {% extends %}, {% embed %}, and {% from ... import ... %}at render time.

Default:None
Returns
Template Compiled Template object
from_string 3 Template ▼
Compile a template from a string.
def from_string(self, source: str, name: str | None = None, *, static_context: dict[str, Any] | None = None) -> Template
Parameters
Name Type Description
source —

Template source code

name —

Template name for error messages and bytecode caching. When abytecode_cacheis configured, providing a name enables persistent caching of the compiled template. Without a name, the template is compiled fresh each time.

Default:None
static_context —

Values known at compile time. Expressions that depend only on these values are evaluated during compilation and replaced with constants in the bytecode. This enables near-str.format()speed for static regions.

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
get_template_structure 1 TemplateStructureManifes… ▼
Return lightweight manifest with extends/blocks/dependencies.
def get_template_structure(self, name: str) -> TemplateStructureManifest | None
Parameters
Name Type Description
name —
Returns
TemplateStructureManifest | None
render 3 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

*args —
**kwargs —
Returns
str Rendered template as string
render_string 3 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

*args —
**kwargs —
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. For ``.kida`` files …
def select_autoescape(self, name: str | None) -> bool

Determine if autoescape should be enabled for a template.

For.kidafiles the inner extension drives the decision: e.g. page.html.kida → .html → escape, README.md.kida → .md → no escape. A bare.kidafile (no inner extension) is treated as plain text (no escaping).

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 9 ▼
__post_init__ 0 ▼
Initialize derived configuration.
def __post_init__(self) -> None
_init_extensions 0 ▼
Initialize registered extensions.
def _init_extensions(self) -> None
_collect_imported_def_metadata 2 dict[str, Any] ▼
Return visible imported component definitions for static validation. Only lite…
def _collect_imported_def_metadata(self, ast: TemplateNode, current_name: str | None) -> dict[str, Any]

Return visible imported component definitions for static validation.

Only literal{% from "template" import name [as alias] %}imports are resolved. Dynamic imports and templates that fail to load are skipped so validation remains conservative.

Parameters
Name Type Description
ast —
current_name —
Returns
dict[str, Any]
_collect_component_call_warnings 2 list[Any] ▼
Recompute import-sensitive call diagnostics from the current loader state.
def _collect_component_call_warnings(self, ast: TemplateNode, name: str | None) -> list[Any]
Parameters
Name Type Description
ast —
name —
Returns
list[Any]
_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.
_resolve_alias 1 str ▼
Substitute ``@alias/path`` with the configured alias root. Raises TemplateNotF…
def _resolve_alias(self, name: str) -> str

Substitute@alias/pathwith the configured alias root.

Raises TemplateNotFoundError when the alias is unknown or when no aliases are configured on this Environment.

Parameters
Name Type Description
name —
Returns
str
_prepare_template_ast 5 TemplateNode ▼
Parse and optimize source identically for cold and cache fallback paths.
def _prepare_template_ast(self, source: str, name: str | None, filename: str | None, *, static_context: dict[str, Any] | None, should_escape: bool) -> TemplateNode
Parameters
Name Type Description
source —
name —
filename —
static_context —
should_escape —
Returns
TemplateNode
_compile 4 Template ▼
Compile template source to Template object. Uses bytecode cache when configure…
def _compile(self, source: str, name: str | None, filename: str | None, *, static_context: dict[str, Any] | None = 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).

Whenstatic_contextis provided, runs a partial evaluation pass before compilation to replace static expressions with constants.

Parameters
Name Type Description
source —
name —
filename —
static_context — Default:None
Returns
Template
_is_template_stale 1 bool ▼
Check if a cached template is stale (source changed). Uses a two-tier check: m…
def _is_template_stale(self, name: str) -> bool

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

Uses a two-tier check: mtime first (cheap stat call), then hash comparison only if mtime changed. This avoids reading and hashing the entire source on every get_template() call when auto_reload=True.

Parameters
Name Type Description
name —

Template identifier

Returns
bool True if template source changed, False if unchanged

Functions

_identity_gettext 1 str ▼
Default gettext: return message unchanged.
def _identity_gettext(message: str) -> str
Parameters
Name Type Description
message str
Returns
str
_identity_ngettext 3 str ▼
Default ngettext: select form by count.
def _identity_ngettext(singular: str, plural: str, n: int) -> str
Parameters
Name Type Description
singular str
plural str
n int
Returns
str
_consume_global 2 Any ▼
Template global: read a value from the nearest {% provide %} ancestor.
def _consume_global(key: str, default: Any = None) -> Any
Parameters
Name Type Description
key str
default Any Default:None
Returns
Any
_format_call_validation_parts 1 list[str] ▼
Format the individual problems in a component call validation result.
def _format_call_validation_parts(issue: Any) -> list[str]
Parameters
Name Type Description
issue Any
Returns
list[str]
_component_call_warning 2 Any ▼
Build a structured warning for a component call validation issue.
def _component_call_warning(issue: Any, template_name: str | None) -> Any
Parameters
Name Type Description
issue Any
template_name str | None
Returns
Any
_emit_template_warnings 1 None ▼
Emit structured template warnings through Python's warning surface.
def _emit_template_warnings(template_warnings: list[Any]) -> None
Parameters
Name Type Description
template_warnings list[Any]
_iter_nodes 1 Any ▼
Yield AST nodes depth-first without depending on a visitor class.
def _iter_nodes(root: Any) -> Any
Parameters
Name Type Description
root Any
Returns
Any
_hash_static_context 1 str | None ▼
Hash static context deterministically for bytecode cache keys.
def _hash_static_context(static_context: dict[str, Any] | None) -> str | None
Parameters
Name Type Description
static_context dict[str, Any] | None
Returns
str | None
_hash_compile_context 2 str | None ▼
Hash code- and compiler-warning-affecting cache configuration.
def _hash_compile_context(static_context: dict[str, Any] | None, *, jinja2_compat_warnings: bool) -> str | None
Parameters
Name Type Description
static_context dict[str, Any] | None
jinja2_compat_warnings bool
Returns
str | None