Kida provides several ways to load templates.
FileSystemLoader
Load templates from filesystem directories:
from kida import Environment, FileSystemLoader
# Single directory
env = Environment(loader=FileSystemLoader("templates/"))
# Multiple directories (searched in order)
env = Environment(loader=FileSystemLoader([
"templates/",
"shared/templates/",
]))
# Load a template
template = env.get_template("page.html")
template = env.get_template("components/button.html")
Search Order
Directories are searched in order. First match wins:
loader = FileSystemLoader([
"themes/custom/", # Checked first
"themes/default/", # Fallback
])
List Templates
loader = FileSystemLoader("templates/")
templates = loader.list_templates()
# ['base.html', 'components/card.html', 'pages/home.html']
DictLoader
Load templates from an in-memory dictionary:
from kida import Environment, DictLoader
loader = DictLoader({
"base.html": "<html>{% block content %}{% end %}</html>",
"page.html": "{% extends 'base.html' %}{% block content %}Hi{% end %}",
})
env = Environment(loader=loader)
template = env.get_template("page.html")
Useful for:
- Testing
- Embedded templates
- Dynamic template generation
ChoiceLoader
Try multiple loaders in order, returning the first match. Useful for theme fallback patterns:
from kida import Environment, FileSystemLoader, ChoiceLoader
loader = ChoiceLoader([
FileSystemLoader("themes/custom/"),
FileSystemLoader("themes/default/"),
])
env = Environment(loader=loader)
# Looks in custom/ first, then falls back to default/
template = env.get_template("page.html")
You can mix loader types:
loader = ChoiceLoader([
DictLoader({"override.html": "<p>In-memory override</p>"}),
FileSystemLoader("templates/"),
])
PrefixLoader
Namespace templates by prefix, delegating to per-prefix loaders. Useful for plugin and multi-app architectures:
from kida import Environment, FileSystemLoader, PrefixLoader
loader = PrefixLoader({
"app": FileSystemLoader("templates/app/"),
"admin": FileSystemLoader("templates/admin/"),
"shared": DictLoader({"header.html": "<header>Shared</header>"}),
})
env = Environment(loader=loader)
# Routes by prefix (delimiter is "/")
env.get_template("app/index.html") # → templates/app/index.html
env.get_template("admin/users.html") # → templates/admin/users.html
env.get_template("shared/header.html") # → DictLoader
Custom delimiter:
loader = PrefixLoader({"app": loader1, "admin": loader2}, delimiter=":")
env.get_template("app:index.html")
PackageLoader
Load templates from an installed Python package. Usesimportlib.resourcesso templates are found regardless of installation path (pip, editable installs, zipped eggs):
from kida import Environment, PackageLoader
# Package structure:
# my_app/
# __init__.py
# templates/
# base.html
# pages/
# index.html
loader = PackageLoader("my_app", "templates")
env = Environment(loader=loader)
template = env.get_template("base.html")
template = env.get_template("pages/index.html")
Useful for:
- Framework default templates (admin, error pages)
- Distributable themes (
pip install my-theme) - Plugin templates namespaced by package
Combine withChoiceLoaderto allow user overrides:
from kida import ChoiceLoader, FileSystemLoader, PackageLoader
loader = ChoiceLoader([
FileSystemLoader("templates/"), # User overrides (checked first)
PackageLoader("my_framework", "defaults"), # Framework defaults (fallback)
])
FunctionLoader
Wrap any callable as a loader. The simplest way to create a custom loading strategy:
from kida import Environment, FunctionLoader
def load(name):
if name == "greeting.html":
return "Hello, {{ name }}!"
return None # Not found
env = Environment(loader=FunctionLoader(load))
template = env.get_template("greeting.html")
The function can return:
str— Template source (filename defaults to"<function>")tuple[str, str | None]—(source, filename)for custom error messagesNone— Template not found (raisesTemplateNotFoundError)
# With custom filename for better error messages
def load_from_cms(name):
source = cms_client.get_template(name)
if source:
return source, f"cms://{name}"
return None
env = Environment(loader=FunctionLoader(load_from_cms))
from_string()
Compile a template from a string (not cached):
env = Environment()
template = env.from_string("Hello, {{ name }}!")
html = template.render(name="World")
Custom Loaders
Implement the Loader protocol:
from kida import Environment, TemplateNotFoundError
class DatabaseLoader:
def __init__(self, connection):
self.conn = connection
def get_source(self, name: str) -> tuple[str, str | None]:
"""Return (source, filename) for template."""
row = self.conn.execute(
"SELECT source FROM templates WHERE name = ?", (name,)
).fetchone()
if not row:
raise TemplateNotFoundError(f"Template '{name}' not found")
return row[0], f"db://{name}"
def list_templates(self) -> list[str]:
"""Return all available template names."""
rows = self.conn.execute("SELECT name FROM templates").fetchall()
return sorted(row[0] for row in rows)
# Usage
env = Environment(loader=DatabaseLoader(db_connection))
Template Caching
Templates are compiled once and cached:
env = Environment(
loader=FileSystemLoader("templates/"),
cache_size=400, # Max cached templates
auto_reload=True, # Check for source changes
)
# Check cache stats
info = env.cache_info()
print(info["template"])
# {'size': 5, 'max_size': 400, 'hits': 100, 'misses': 5}
Auto-Reload
Withauto_reload=True(default), Kida checks if template source changed:
# Development: auto-reload enabled
env = Environment(auto_reload=True)
# Production: disable for performance
env = Environment(auto_reload=False)
Clear Cache
# Clear all templates
env.clear_template_cache()
# Clear specific templates
env.clear_template_cache(["base.html", "page.html"])
Bytecode Cache
For cold-start performance, enable bytecode caching:
from kida import Environment, FileSystemLoader
from kida.bytecode_cache import BytecodeCache
env = Environment(
loader=FileSystemLoader("templates/"),
bytecode_cache=BytecodeCache("__pycache__/kida/"),
)
Compiled bytecode is persisted to disk and loaded on subsequent runs.
See Also
- API Reference — Environment, loaders
- Custom Loaders — Build custom loaders
- Performance — Caching optimization