A Go library that mimics Jinja's templating behavior.
This project aims to provide a reusable library with two main functions:
TemplateString(template string, context map[string]interface{}) (string, error): Evaluates a Jinja-like template string. Variables in the format{{ variable_name }}are replaced with values from the context map.EvaluateExpression(expression string, context map[string]interface{}) (interface{}, error): Evaluates a Jinja-like expression string using the provided context. An error is returned if the expression cannot be evaluated.
Additionally, the library will support:
- Built-in functions and filters comparable to those in Jinja (e.g.,
lookup,urlencode,map,default). - Basic flow control structures (e.g.,
{% for item in items %},{% if condition %}).
package main
import (
"fmt"
"github.com/AlexanderGrooff/jinja-go"
)
func main() {
context := map[string]interface{}{
"name": "World",
"isAdmin": true,
}
// TemplateString example
templated, err := jinja.TemplateString("Hello {{ name }}!", context)
if err != nil {
fmt.Printf("TemplateString Error: %v\n", err)
return
}
fmt.Println(templated) // Output: Hello World!
// EvaluateExpression example
isAdmin, err := jinja.EvaluateExpression("isAdmin", context)
if err != nil {
fmt.Printf("EvaluateExpression Error: %v\n", err)
return
}
fmt.Printf("Is Admin: %v\n", isAdmin) // Output: Is Admin: true
} Performance is critical for this library. We use benchmarking to ensure that changes don't negatively impact performance.
# Run benchmarks without saving results
make benchmark
# Run benchmarks and save as latest
make benchmark-save
# Compare latest benchmarks with previous
make benchmark-compare
# Save latest as the new previous (baseline)
make benchmark-save-as-previous
# Compare with another branch
make benchmark-branch branch=main
# Generate and save a benchmark report
make benchmark-report
# Run cross-language benchmarks against Python's Jinja2
make cross-benchmarkThe repository uses benchstat to compare benchmark results, and pre-commit hooks automatically run benchmarks and compare with previous results.
In addition to benchmarking, the library includes profiling tools to identify performance bottlenecks and optimize critical sections of code.
# Profile the complex_template with CPU, memory, and block profiling
make profile-complex
# Profile nested_loops template (one of the most performance-critical patterns)
make profile-nested-loops
# Profile all templates from the benchmark suite
make profile-all
# Run custom profiling
make profile ARGS="--template conditional --cpu --iterations 5000"After running a profile, analyze the results:
# Web-based visualization (most comprehensive)
go tool pprof -http=:8080 profile_results/complex_template/cpu.prof
# Text-based analysis
go tool pprof profile_results/template_name/cpu.prof
(pprof) top10 # Show top 10 functions by CPU usage
(pprof) list TemplateString # Show time spent in functionFor more detailed information on profiling and performance optimization guidelines, see performance.md.
You can directly compare this Go implementation against Python's Jinja2 and other Go-based Jinja-like libraries (such as Pongo2) using the cross-language benchmarking tools:
# Run with default settings
make cross-benchmark
# Run with custom iterations and output directory
./cmd/benchmark/run_benchmarks.sh --iterations 5000 --output-dir custom_benchmarks
# Run with custom template test cases
./cmd/benchmark/run_benchmarks.sh --templates path/to/custom_templates.jsonThe cross-benchmark tool:
- Runs identical templates through both the Python and Go implementations (including other Go libraries like Pongo2)
- Measures rendering time for each template
- Calculates the speed difference between implementations
- Generates a detailed comparison report
Custom template test cases can be defined in a JSON file following this format:
[
{
"name": "template_name",
"template": "Hello, {{ name }}!",
"context": {"name": "World"}
},
// More test cases...
]The comparison provides insight into performance characteristics of both implementations, which is useful for:
- Identifying areas where the Go implementation can be optimized
- Quantifying performance gains for various template features
- Tracking performance improvements over time
You can view the latest comparison report to see the current performance differences between all implementations.
-
Template Syntax
- Basic variable substitution (
{{ variable }}) - Comments (
{# comment #}) - Conditional statements (
{% if %},{% elif %},{% else %},{% endif %}) - Loop structures (
{% for item in items %},{% endfor %}) with loop variable support
- Basic variable substitution (
-
Expression Evaluation
- Basic literals (integers, floats, strings, booleans, null/None)
- Variable access and context lookup
- Pythonic data types:
- Lists (
[1, 2, 3]) - Dictionaries (
{'key': 'value'}) - Dictionary methods like
.get()(dict.get('key', 'default')) - String methods like
.format()("Hello, {}!".format("world"))
- Lists (
- Object/attribute access (
object.attribute) - Subscript access (
array[index],dict['key'], negative indices) - LALR (Look-Ahead LR) parser for robust expression evaluation
- Improved parsing performance and reliability
- Proper operator precedence handling
- Support for complex expressions such as
a * (b + c) / d
- Complex nested expression handling with multiple subscript operations
- Basic filters (e.g.,
{{ var | default('fallback') }})
-
Operators
- Arithmetic operators (
+,-,*,/,//(floor division),%(modulo),**(power)) - Unary operators (
not,-,+) - Comparison operators (
==,!=,>,<,>=,<=) - Logical operators (
and,or) with short-circuit evaluation - Identity operators (
is,is not) - Membership operators (
in) - String operations (concatenation, repetition)
- Arithmetic operators (
-
Filters
defaultfilterjoinfilterupperfilterlowerfiltercapitalizefilterreplacefiltertrimfilterlistfilterescapefiltermapfilteritemsfilterlookupfilter withfileandenvsources
-
Template Syntax
- Include support (
{% include 'page.html' %}) - Macro definitions (
{% macro %}/{% endmacro %}) - Block and extends for template inheritance (
{% block %},{% extends %}) - Set statements (
{% set %}) - With blocks (
{% with %}) - Loop controls (
{% break %},{% continue %}) - Whitespace control (using
-in tags like{%-and-%}) - Expression statements (
{% do expression %}) - Debug statements (
{% debug %})
- Include support (
-
Expression Evaluation
- More filters (e.g.,
{{ url | urlencode }}) - Tests (
{{ user is defined }},{{ user is not none }}) - String formatting and f-strings
- List comprehensions
- Generator expressions (iterables)
- Auto-escaping support
- More filters (e.g.,
-
Control Structures
- More complex control structures
-
Filters and Functions
- Additional common Jinja filters (
urlencode, etc.) - Additional lookup plugin types for the
lookupfilter - More built-in functions
- Complete set of built-in tests (
defined,none,iterable, etc.) - Translation/internationalization support (gettext)
- Additional common Jinja filters (
-
Advanced Features
- Macro definitions
- Include/import functionality
- Block/extends for template inheritance
- Context scoping and namespaces
- Custom tests and filters
- Auto-escaping configuration
- Error handling improvements - standardize error reporting across modules
- Handling of edge cases in string literals and escaping
- Better documentation of supported features
This project is licensed under the MIT License - see the LICENSE file for details.
The pre-commit hooks will:
- Run benchmarks before each commit
- Compare with previous benchmark results
- Show performance changes
Install pre-commit hooks with:
pre-commit install