Template Language Guide

For now we have only a jinja generator for code generation. As such this guide will only discuss using nunavut with jinja templates. There are no immediate plans to support any other template syntax.



See nunavut.jinja and the language support modules within this one for detailed documentation on available filters and tests provided by nunavut.

The Jinja templates documentation is indispensible as nunavut embeds a full-featured version of jinja 2.

Each template has in its global environment the following:


The T global contains the type for the given template. For example:

{%- for attr in T.attributes %}
    {{ attr.data_type }}
{%- endfor %}


The time UTC as a Python datetime.datetime object. This is the system time right before the file generation step for the current template began. This will be the same time for included templates and parent templates.

Filters and Tests

In addition to the built-in Jinja filters and tests (again, see the Jinja templates documentation for details) pydsdl adds several global tests and filters to the template environment. See nunavut.jinja for full documentation on these. For example:

# typename filter returns the name of the value's type.
{{ field | typename }}

Language Support Filters

Nunavut also adds some language specific filters and tests. These are documented in the nunavut.jinja.lang module and are available in the global environment namespaced by language. For example:

{{T.full_name | c.macrofy}}
{{T.full_namespace | cpp.open_namespace}}

Template Mapping and Use

Templates are resolved as templates/path/[dsdltypename].j2 This means you must, typically, start with three templates under the templates directory given to the Generator instance



You can chose to use a single template Any.j2 but this may lead to more complex templates with many more control statements. By providing discreet templates named for top-level data types and using jinja template inheritance and includes your templates will be smaller and easier to maintain.

To share common formatting for these templates use Jinja template inheritance. For example, given a template common_header.j2:

 * UAVCAN data structure definition for libuavcan.
 * Autogenerated, do not edit.
 * Source file: {{T.source_file_path}}
 * Generated at: {{now_utc}}
 * Template: {{ self._TemplateReference__context.name }}
 * deprecated: {{T.deprecated}}
 * fixed_port_id: {{T.fixed_port_id}}
 * full_name: {{T.full_name}}
 * full_namespace: {{T.full_namespace}}

 #ifndef {{T.full_name | c.macrofy}}
 #define {{T.full_name | c.macrofy}}

 {%- block contents %}{% endblock -%}

 #endif // {{T.full_name | c.macrofy}}

 {{ T | yamlfy }}

… your three top-level templates would each start out with something like this:

{% extends "common_header.j2" %}
{% block contents %}
// generate stuff here
{% endblock %}

Resolving Types to Templates

You can apply the same logic used by the top level generator to recursively include templates by type if this seems useful for your project. Simply use the nunavut.jinja.Generator.filter_type_to_template() filter:

{%- for attribute in T.attributes %}
    {%* include attribute.data_type | type_to_template %}
{%- endfor %}

Namespace Templates

By setting the generate_namespace_types parameter of Generator to true the generator will invoke a template for the root namespace and all nested namespaces allowing for languages where namespaces are first class objects. For example:

root_namespace = build_namespace_tree(compound_types,

generator = Generator(root_namespace, True, templates_dir)

This could be used to generate python __init__.py files which would define each namespace as a python module.

The Generator will use the same template name resolution logic as used for pydsdl data types. For namespaces this will resolve first to a template named Namespace.j2 and then, if not found, Any.j2.