Handlebars template engine for composing LLM prompts, built on Pydantic.
make all— format, lint, typecheck, test with coveragemake test— run tests (parallel with pytest-xdist)make lint— ruff format check + ruff checkmake typecheck— pyright strict mode
- Only runtime dependency is pydantic (2.0+)
- 100% test coverage required
- All implementation modules are private (
_tokenizer.py, etc.) - Public API is exported from
__init__.py - Tests are ported from the Handlebars.js spec suite — maintain 1:1 correspondence
- Single quotes for code, double quotes for docstrings
- Google-style docstrings
- Python 3.10+ (use
X | Yunion syntax in annotations, butisinstance(x, (X, Y))in runtime checks) - No
except Exception— catch specific types - No imports inside functions
- Tokenizer (
_tokenizer.py) → Token stream - Parser (
_parser.py) → AST (_ast_nodes.py) - Compiler (
_compiler.py) → Output string
Templates may be untrusted. All context data is serialized to JSON-safe types via pydantic_core.to_jsonable_python before rendering — the template engine only sees dicts, lists, strings, numbers, bools, and None. Enforce depth limits for nesting.
Tests are ported from https://github.com/handlebars-lang/handlebars.js/tree/master/spec. When adding new features, find the corresponding JS spec tests and port them. Use assert render(template, context) == expected pattern. No test classes — standalone functions only.