Python API Reference¶
Module Functions¶
render¶
Render a template string with a context dictionary:
from pydantic_handlebars import render
print(render('Hello {{name}}!', {'name': 'World'}))
#> Hello World!
Uses a default environment with standard Handlebars helpers (if, unless, each, with,
lookup, log).
Render a Handlebars template string with the given context.
This is a convenience function that uses a default environment with standard Handlebars helpers registered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The Handlebars template string. |
required |
context
|
dict[str, Any] | None
|
The data context for rendering. |
None
|
strict
|
bool
|
If |
False
|
open_delim
|
str
|
Open mustache delimiter. Defaults to |
'{{'
|
close_delim
|
str
|
Close mustache delimiter. Defaults to |
'}}'
|
Returns:
| Type | Description |
|---|---|
str
|
The rendered string. |
compile¶
Compile a template into a reusable callable. When called with just a source string, returns a
CompiledTemplate. When called with a source string and a type, validates the template against the
type's JSON schema and returns a CompiledTypedTemplate:
from pydantic_handlebars import compile
# Untyped compilation
template = compile('Hello {{name}}!')
print(template.render({'name': 'World'}))
#> Hello World!
from pydantic import BaseModel
from pydantic_handlebars import compile
class User(BaseModel):
name: str
age: int
# Typed compilation — validates template against User's schema
template = compile('Hello {{name}}, age {{age}}!', User)
print(template.render(User(name='Alice', age=30)))
#> Hello Alice, age 30!
Compile a Handlebars template string into a reusable callable.
When called with just a source string, returns a CompiledTemplate.
When called with a source string and a type, validates the template against
the type's JSON schema and returns a CompiledTypedTemplate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The Handlebars template string. |
required |
tp
|
type[Any] | None
|
Optional Pydantic model type or any type supported by TypeAdapter. |
None
|
serialize_as_any
|
bool
|
If True, serialize subclass fields instead of only
the declared type's fields. Only applies when |
False
|
Returns:
| Type | Description |
|---|---|
CompiledTemplate | CompiledTypedTemplate[Any]
|
A |
typed_compiler¶
Create a TypedCompiler for compiling multiple templates against the same type. Caches the type
adapter and JSON schema for efficiency:
from pydantic import BaseModel
from pydantic_handlebars import typed_compiler
class User(BaseModel):
name: str
age: int
compiler = typed_compiler(User)
greeting = compiler.compile('Hello {{name}}!')
info = compiler.compile('{{name}} is {{age}} years old.')
user = User(name='Alice', age=30)
print(greeting.render(user))
#> Hello Alice!
print(info.render(user))
#> Alice is 30 years old.
Create a typed compiler for repeated compilation against a type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tp
|
type[T]
|
The Pydantic model type or any type supported by TypeAdapter. |
required |
env
|
HandlebarsEnvironment | None
|
Optional HandlebarsEnvironment. Uses a default if not provided. |
None
|
optional_field_severity
|
IssueSeverity
|
Severity for optional field access issues. |
'error'
|
serialize_as_any
|
bool
|
If True, serialize subclass fields instead of only the declared type's fields. |
False
|
Returns:
| Type | Description |
|---|---|
TypedCompiler[T]
|
A |
check_template_compatibility¶
Validate a template (or list of templates) against a JSON schema without compiling:
from pydantic_handlebars import check_template_compatibility
schema = {
'type': 'object',
'properties': {'name': {'type': 'string'}},
'required': ['name'],
}
result = check_template_compatibility('Hello {{name}}!', schema)
print(result.is_compatible)
#> True
result = check_template_compatibility('{{missing_field}}', schema)
print(result.is_compatible)
#> False
print(result.issues[0].field_path)
#> missing_field
Check template compatibility with a JSON schema.
Parses the templates, walks their ASTs, and verifies every field reference exists in the provided JSON schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
templates
|
str | list[str]
|
A single template string or list of template strings. |
required |
json_schema
|
_JsonSchema
|
The JSON schema to validate against (e.g., from Pydantic's
|
required |
raise_on_error
|
bool
|
If True, raise |
False
|
optional_field_severity
|
IssueSeverity
|
Severity for fields not found in the schema when the
schema has properties but the field is not in |
'error'
|
helpers
|
set[str] | None
|
Additional helper names to treat as known (beyond built-in helpers). |
None
|
Returns:
| Type | Description |
|---|---|
CompatibilityResult
|
A |
Raises:
| Type | Description |
|---|---|
TemplateSchemaError
|
If |
extract_dependencies¶
Statically walk a template and return the set of top-level context field names it depends on. No rendering, no context required:
from pydantic_handlebars import extract_dependencies
extract_dependencies('{{greeting}}, {{user.name}}!')
#> {'greeting', 'user'}
# Scope-aware: `each` / `with` shift the context, so inner refs are not top-level deps.
extract_dependencies('{{#each items}}{{name}}{{/each}}')
#> {'items'}
# `../` walks back to the top scope and contributes a top-level dep:
extract_dependencies('{{#each items}}{{../top}}{{/each}}')
#> {'items', 'top'}
Return the set of top-level context field names source depends on.
Statically walks the template AST — no rendering or context is required. Useful for tooling that needs to know "what fields does this template expect" without actually executing it: dependency resolution, prompt composition systems, dataset generation, etc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Handlebars template source. |
required |
helpers
|
set[str] | None
|
Names of additional helpers to recognise. Names registered
as helpers are not treated as context references — for example,
|
None
|
include_extra_helpers
|
bool
|
When |
False
|
open_delim
|
str
|
Open mustache delimiter. Defaults to |
'{{'
|
close_delim
|
str
|
Close mustache delimiter. Defaults to |
'}}'
|
Returns:
| Type | Description |
|---|---|
set[str]
|
Set of top-level context field names referenced by the template. |
set[str]
|
Empty if the template only contains literal content, |
set[str]
|
variables, or references shadowed by block parameters. |
Raises:
| Type | Description |
|---|---|
HandlebarsParseError
|
If the template cannot be parsed. |
Examples:
extract_dependencies('Hello {{user_name}}!') == {'user_name'}
extract_dependencies('{{user.name}} <{{user.email}}>') == {'user'}
extract_dependencies('{{#if beta}}new{{else}}{{tagline}}{{/if}}') == {'beta', 'tagline'}
extract_dependencies('{{#each items}}{{name}}{{/each}}') == {'items'}
extract_dependencies('{{#each items}}{{../top}}{{/each}}') == {'items', 'top'}
Custom delimiters¶
HandlebarsEnvironment, render, compile, and extract_dependencies all accept matching
open_delim and close_delim keyword parameters (defaulting to {{ and }}). Custom delimiter
pairs let you embed a Handlebars rendering pass without disturbing a different set of placeholders
intended for a later pass:
from pydantic_handlebars import render
# Compose with @{...}@ — leaves {{...}} literals untouched for the next pass.
after_composition = render(
'You are helping @{role}@. Greet {{user_name}}.',
{'role': 'a designer'},
open_delim='@{', close_delim='}@',
)
print(after_composition)
#> You are helping a designer. Greet {{user_name}}.
print(render(after_composition, {'user_name': 'Alice'}))
#> You are helping a designer. Greet Alice.
All standard syntax — block helpers, whitespace control, comments, escape sequences, and the
ampersand-unescape variant — works under custom delimiters. Triple-stache ({{{...}}}) and raw
blocks ({{{{...}}}}) are only available with the default delimiter pair; under custom delimiters
use @{&x}@ for per-expression escape opt-out, or auto_escape=False on the environment.
The public delimiter parameters reject empty, identical, whitespace-edged, and expression-syntax-
conflicting ("'()=|~) pairs.
Strict mode¶
Pass strict=True to fail loudly when a template references a path that does not exist on the
context, instead of silently rendering as the empty string:
from pydantic_handlebars import HandlebarsRuntimeError, render
try:
render('Hello {{name}}!', {}, strict=True)
except HandlebarsRuntimeError as e:
print(e)
#> Strict mode: 'name' references missing context key 'name'
Strict mode treats missing keys as errors but lets explicit None values through. It applies to
block-helper arguments ({{#if missing}}) and @data variables the same way it applies to direct
lookups. Helper names, block params, and {{this}} are unaffected.
Strict mode can also be set at the environment level: HandlebarsEnvironment(strict=True).
Classes¶
HandlebarsEnvironment¶
An environment for rendering templates with custom helpers.
from pydantic_handlebars import HandlebarsEnvironment, HelperOptions
env = HandlebarsEnvironment(extra_helpers=True)
@env.helper
def shout(*args: object, options: HelperOptions) -> str:
return str(args[0]).upper() + '!!!'
print(env.render('{{shout name}}', {'name': 'world'}))
#> WORLD!!!
Pass extra_helpers=True to include additional utility, comparison, and boolean helpers beyond the
6 standard Handlebars.js helpers. See Built-in Helpers for the full list.
An environment for rendering Handlebars templates with custom helpers.
The environment manages a set of helpers (both built-in and user-registered) and provides methods for compiling and rendering templates.
Example
open_delim
property
¶
The open mustache delimiter this environment compiles templates with.
Defaults to {{. Templates compiled or rendered through this
environment look for this string to mark the start of an expression.
close_delim
property
¶
The close mustache delimiter this environment compiles templates with.
Defaults to }}. Non-default delimiter pairs disable the
triple-stache ({{{...}}}) and raw-block ({{{{...}}}})
variants since those forms only have unambiguous extensions for the
default pair.
strict
property
¶
Whether strict mode is enabled for templates rendered by this environment.
When True, referencing a path that does not exist on the context
raises :class:HandlebarsRuntimeError instead of silently rendering
as an empty string. An explicit None value in the context is still
allowed — strict mode only fires for genuinely missing keys.
auto_escape
property
¶
Whether HTML auto-escaping is enabled for {{expression}} output.
When False (the default), no escaping is applied — appropriate for
non-HTML use cases like prompt generation. When True,
double-stache expressions are HTML-escaped. Triple-stache
{{{expression}}} is always unescaped regardless of this setting.
helper(fn)
¶
Register a helper function.
Can be used as a decorator (with or without a name argument):
@env.helper
def my_helper(*args: object, options: HelperOptions) -> str:
return str(args[0]).upper()
@env.helper('custom-name')
def my_helper(*args: object, options: HelperOptions) -> str:
return str(args[0]).upper()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
F | str
|
The helper function, or a name string for use as a decorator factory. |
required |
Returns:
| Type | Description |
|---|---|
F | Callable[[F], F]
|
The registered function, or a decorator. |
register_helper(name, fn)
¶
Register a helper function with an explicit name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name to register the helper under. |
required |
fn
|
HelperFunc
|
The helper function. |
required |
unregister_helper(name)
¶
Unregister a helper by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the helper to remove. |
required |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the helper is not registered. |
compile(source)
¶
Compile a template string into a reusable callable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The Handlebars template string. |
required |
Returns:
| Type | Description |
|---|---|
CompiledTemplate
|
A |
render(source, context=None)
¶
Render a template string with the given context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The Handlebars template string. |
required |
context
|
Any
|
The data context for rendering. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The rendered string. |
CompiledTemplate¶
Returned by compile(source) (without a type). Call .render() with a context dict.
A compiled Handlebars template.
render(context=None)
¶
Render the template with the given context.
CompiledTypedTemplate¶
Returned by compile(source, Type). Call .render() with an instance of the type, or
.validate_and_render() with unvalidated data:
from pydantic import BaseModel
from pydantic_handlebars import compile
class User(BaseModel):
name: str
age: int
template = compile('Hello {{name}}!', User)
# .render() — expects a validated instance
print(template.render(User(name='Alice', age=30)))
#> Hello Alice!
# .validate_and_render() — validates input through Pydantic first
print(template.validate_and_render({'name': 'Bob', 'age': 25}))
#> Hello Bob!
A compiled template bound to a specific type.
Created by TypedCompiler.compile() or compile(source, tp).
render(data)
¶
Render with typed data (no runtime validation).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
T
|
The data to render. Must match type T. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The rendered template string. |
validate_and_render(data)
¶
Validate data through Pydantic, then render.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
The data to validate and render. Can be any type that Pydantic can coerce to T. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The rendered template string. |
TypedCompiler¶
Returned by typed_compiler(). Caches the type adapter and JSON schema for compiling multiple
templates against the same type.
Compiler for creating typed templates against a Pydantic model.
Caches the TypeAdapter and JSON schema for repeated compilation against the same type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tp
|
type[T]
|
The Pydantic model type or any type supported by TypeAdapter. |
required |
env
|
HandlebarsEnvironment | None
|
Optional HandlebarsEnvironment. Uses a default if not provided. |
None
|
optional_field_severity
|
IssueSeverity
|
Severity for optional field access issues. |
'error'
|
Example
json_schema
property
¶
The JSON schema derived from the type.
compile(source, *, raise_on_error=True)
¶
Compile a template, checking compatibility with the schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The Handlebars template string. |
required |
raise_on_error
|
bool
|
If True (default), raise on incompatible templates. |
True
|
Returns:
| Type | Description |
|---|---|
CompiledTypedTemplate[T]
|
A |
Raises:
| Type | Description |
|---|---|
TemplateSchemaError
|
If the template references fields not in the schema. |
HelperOptions¶
Passed to block helpers as the options keyword argument. Provides access to the block body,
inverse block, hash arguments, and data variables.
from pydantic_handlebars import HandlebarsEnvironment, HelperOptions
env = HandlebarsEnvironment()
@env.helper('repeat')
def repeat_helper(*args: object, options: HelperOptions) -> str:
times = int(options.hash.get('times', 1))
return ''.join(options.fn() for _ in range(times))
print(env.render('{{#repeat times=3}}Ho {{/repeat}}', {}))
#> Ho Ho Ho
Options passed to helper functions.
Attributes:
| Name | Type | Description |
|---|---|---|
fn |
Function to render the block content with a given context. |
|
inverse |
Function to render the else/inverse content with a given context. |
|
hash |
dict[str, Any]
|
Hash arguments passed to the helper. |
data |
dict[str, Any]
|
Data variables (@root, @index, etc.). |
block_params |
list[str]
|
Block parameter values. |
name
property
¶
Get the helper name (for the log helper).
SafeString¶
Marks a string as already HTML-safe when returned from helpers. When auto_escape=True, helper
return values wrapped in SafeString bypass HTML escaping:
from pydantic_handlebars import HandlebarsEnvironment, HelperOptions, SafeString
env = HandlebarsEnvironment(auto_escape=True)
@env.helper
def bold(*args: object, options: HelperOptions) -> SafeString:
return SafeString(f'<b>{args[0]}</b>')
print(env.render('{{bold name}}', {'name': 'Alice'}))
#> <b>Alice</b>
Note
SafeString values in context dicts are serialized to plain str before rendering, losing
the safe marker. SafeString is primarily useful as a return type from helpers.
A string subclass that marks content as safe (no HTML escaping).
When a SafeString is rendered in a {{expression}} context, it will
NOT be HTML-escaped, even though normal strings would be.
CompatibilityResult¶
Returned by check_template_compatibility. Contains is_compatible (bool) and issues (list of
TemplateIssue).
Result of checking template compatibility with a JSON schema.
Attributes:
| Name | Type | Description |
|---|---|---|
issues |
list[TemplateIssue]
|
List of all issues found during checking. |
is_compatible
property
¶
Whether the templates are compatible (no ERROR-level issues).
TemplateIssue¶
A single issue found during schema compatibility checking. Contains field_path, message, and
severity.
A single compatibility issue found during template checking.
Attributes:
| Name | Type | Description |
|---|---|---|
severity |
IssueSeverity
|
The severity level of the issue. |
message |
str
|
A human-readable description of the issue. |
field_path |
str
|
The dotted path to the field that caused the issue. |
template_index |
int
|
The index of the template that contains the issue. |
Exceptions¶
HandlebarsError¶
Base exception for all pydantic_handlebars errors.
HandlebarsParseError¶
Raised when a template cannot be parsed. Includes line and column attributes:
from pydantic_handlebars import HandlebarsParseError, render
try:
render('{{{unclosed}', {})
except HandlebarsParseError as e:
assert e.line is not None
assert e.column is not None
Raised when a template cannot be parsed.
Attributes:
| Name | Type | Description |
|---|---|---|
message |
The error message. |
|
line |
The line number where the error occurred (1-based). |
|
column |
The column number where the error occurred (1-based). |
HandlebarsRuntimeError¶
Raised during template rendering (e.g., exceeding depth limits).
TemplateSchemaError¶
Raised by compile(source, Type) when the template references fields not present in the type's
JSON schema:
from pydantic import BaseModel
from pydantic_handlebars import TemplateSchemaError, compile
class User(BaseModel):
name: str
try:
compile('{{missing}}', User)
except TemplateSchemaError as e:
print(e)
"""
1 error(s) found:
- missing: Field 'missing' not found in schema
"""
Utilities¶
escape_expression¶
HTML-escape a string using Handlebars escaping rules: