Gonja Template Engine

repository·master·Indexed 41 days ago

https://github.com/nikolalohinski/gonja

A pure Go implementation of the Jinja template engine designed for compatibility with the original Python implementation. It supports Jinja-style control structures (if, for, set, block, extends), custom filters, and a PyString implementation to provide Python 3-like string manipulation behavior in Go.

Tokens
10.9K
Snippets
54
Records
69
Agent score
50%

What's inside Gonja

  1. Overview of PyString implementation

    master

    PyString is a feature in Gonja that attempts to provide string manipulation behavior as similar as possible to Python 3's string.Formatter and standard string methods. It is designed to bridge the gap for developers moving from Python to Gonja templates.

    Key Characteristics

    • Python 3 Parity: The implementation targets Python 3.X behavior. Python 2.X (string % dict) compatibility is explicitly out of scope.
    • Dialects: Because Python implementations can vary between versions, Gonja supports "dialects" via feature flags to achieve direct parity with specific Python versions.
  2. Use tests in conditional blocks and expressions

    master

    Tests in Gonja are used with the is operator to trigger conditional behavior within {% if %} blocks or expressions. They allow you to check the properties or types of variables.

    {% if variable is string %}
       This was a string: {{ variable }}
    {% elif variable is sequence %}
       This was a list: {{ variable | join(",") }}
    {% endif %}
  3. How to use filters in Gonja

    master

    Variables can be modified using filters. A filter is applied to a variable using the pipe symbol (|) and can accept optional arguments in parentheses. Filters can be chained together, where the output of one filter becomes the input for the next.

    Examples

    Chaining filters:

    {{ "a,b,c" | split(",") | tojson }}

    Assigning filtered values:

    {% set number = "123" | int %}

    Iterating over filtered sequences:

    {% for x in {"first": 1, "second": 2} | values %}
      {{- x }}
    {% endfor %}
    {{ "a,b,c" | split(",") | tojson }}
    
    {% set number = "123" | int %}
    
    {% for x in {"first": 1, "second": 2} | values %}
      {{- x }}
    {% endfor %}
  4. Implement template inheritance with `block` and `extends`

    master

    Template inheritance allows you to create a base 'skeleton' template using {% block %} tags. Child templates then use {% extends %} to inherit from the base and override specific blocks with their own content.

    Note: The {% extends %} tag must be the very first tag in the child template.

    {# base.html #}
    <!DOCTYPE html>
    <html>
    <head>
        {% block head %}{% endblock %}
        <title>{% block title %}{% endblock %}</title>
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
    </html>
    
    {# child.html #}
    {% extends "base.html" %}
    {% block title %}Index{% endblock %}
    {% block content %}
        <h1>Index</h1>
    {% endblock %}
  5. Understand string type compatibility and limitations

    master

    Gonja implements most Python str methods to provide high compatibility. However, due to differences between Go and Python (specifically regarding Unicode handling), some discrepancies may exist. For example, unicode.IsNumber("三") in Go returns false, whereas Python's char.isnumeric() returns true.

    Not implemented or differently implemented:

    • maketrans: Too complex.
    • translate: Too complex.
    • isidentifier: Not considered highly relevant outside of Python.
    • index: Use find instead (it behaves similarly but returns -1 instead of raising an error).
    • rindex: Use rfind instead.
  6. Use Python-style methods in Gonja templates

    master

    Gonja supports calling methods on native Python types using Go implementations. You can invoke these methods directly on string, integer, float, or boolean values within your templates using the standard Python dot notation.

    {{ "hello".upper() }}
  7. Understand gonja limitations

    master

    When using gonja, be aware of the following differences from the original Python Jinja implementation:

    • format filter: Uses Go's string formatting syntax instead of Python's. For example, {{ 3.14|stringformat:"%.2f" }} behaves like fmt.Sprintf("%.2f", 3.14).
    • Escaping: The escape filter is applied immediately. Consequently, the force_escape filter is not needed.
    • Type Methods: Only a subset of native Python type methods (bool, int, float, str, dict, and list) are re-implemented in Go, and their behavior may differ slightly from Python.
  8. Configure gonja without global variables for parallel rendering

    master
    When you need to perform parallel rendering within a single execution using different configuration sets, avoid using global variables. Instead, instantiate and configure gonja instances locally to ensure thread safety and configuration isolation. This allows multiple rendering processes to run concurrently with distinct settings without interfering with one another.
  9. Migrate from gonja v1 to v2

    master

    Upgrading from v1.X.X to v2 involves several breaking changes as the project now aims for closer compatibility with Python's Jinja engine:

    • Import Path: Change all imports from github.com/nikolalohinski/gonja to github.com/nikolalohinski/gonja/v2.
    • Global Variables/Functions:
      • DefaultEnv is now DefaultEnvironment (properties have changed).
      • Globals is now DefaultContext.
      • Globals (the concept) is now GlobalFunctions.
      • FromCache has been removed. Implement a custom Loader if caching is required.
    • Terminology: Statement is now ControlStructure.
    • Built-ins: Non-Python built-ins were removed. To restore them, add the github.com/NikolaLohinski/terraform-provider-jinja/lib dependency and update global variables in builtins/.
    • Template Execution: template.Execute now requires an io.Writer. Use template.ExecuteToString as a drop-in replacement if you want to return a string.
  10. Use a custom filter in Gonja templates

    master

    You can extend gonja functionality by registering custom filters. To use a custom filter, define a function that matches the expected signature and register it with the gonja environment. Once registered, the filter can be invoked within a template using the pipe (|) syntax.

    // Example pattern for registering and using a custom filter
    // 1. Define the filter function
    myFilter := func(ctx *gonja.Context, args ...interface{}) (interface{}, error) {
        // implementation
        return args[0], nil
    }
    
    // 2. Register it with the environment
    env := gonja.NewEnvironment()
    env.AddFilter("my_filter_name", myFilter)
    
    // 3. Use it in a template string
    template, _ := env.FromString("{{ value | my_filter_name }}")