About

In the following we discuss jinja strings, which are the return entity of an entity in comp component system.

  1. Jinja Strings

  2. Delimiters

  3. Variables

  4. Rendering

  5. Factories

  6. Highlight

  7. Other Docs

Jinja Strings

The component system of comp is based in jinja2. This means that the components construct strings following jinja2 syntax. We have a specific type Jinja (which is a subtype of Str - the string type of typed) whose instances are the so-called jinja strings. These are Python strings preppended with the “jinja” keyword.

So, more precisely, an instance of Jinja is a string as follows (see jinja2 to discover the full valid syntax):

 1my_jinja_string = """jinja
 2[% for i in x %]
 3<some html>
 4    [% if y is True %]
 5    [% set w = "something" %]
 6    <more html>
 7    ...
 8[# my beautiful comment #]
 9    [[ z ]]
10    ...
11    </more html>
12    [% endif %]
13</some html>
14[% endfor %]
15"""

Delimiters

You should note that the jinja2 code provided in the previous jinja string uses delimiters differing from the standard ones: while {lib:jinja} uses {%, {{ and {# as default for blocks, variables and comments, respectively, we are using [, [[ and [#.

The reason is that as we will see in components documentation, in comp the jinja strings are typically returned by functions, and one would like to use variables of the function inside the return jinja string. This implies to consider the jinja strings as f-strings. But in f-strings, variables already use { as delimiters, causing certain conflict with the default {lib:jinja} delimiters.

If you don’t like using [ as the basic delimiter for jinja strings, you can set one of the following options, or some mix of them:

blocks

variables

comments

[% … %]

[[ … ]]

[# … #]

(% … %)

(( … ))

(# … #)

<% … %>

<< … >>

<# … #>

table 1: jinja delimiters

This is done by setting the following environment variables:

env

description

default value

COMP_JINJA_VAR_DELIM

delimiters for jinja vars

(“[[”, “]]”)

COMP_JINJA_BLOCK_DELIM

delimiters for jinja blocks

(“[%”, “%]”)

COMP_JINJA_COMMENT_DELIM

delimiters for jinja comments

(“[#”, “#]”)

table 2: jinja delimiters envs

Variables

When working with jinja strings we have two kinds of variables:

  1. inner jinja variables, which are defined as part of the jinja string through a [% set ... %] block;

  2. free jinja variables, which are not defined inside the jinja string.

So, for example, in the above jinja string, x, y and z are all free jinja variables, while w is an inner jinja var.

The variables of a jinja string are accessible as attributes corresponding to properties of the type Jinja:

attribute

meaning

.vars

dictionary with all vars organized by kind

.inner_vars

dict of inner vars and values of the jinja string

.free_vars

tuple of free vars of the jinja string

table 3

In the case of the above jinja string one would get:

1from comp import Jinja
2from some.where import my_jinja_string
3
4print(Jinja(my_jinja_string).vars)        # {"inner": {"w": "something"}, "free": ("x", "y", z")}
5print(Jinja(my_jinja_string).inner_vars)  # {"w": "something"}
6print(Jinja(my_jinja_string).free_vars)   # ("x", "y", "z")

Notice that the attributes are accessible only after initialization of the jinja strings in the Jinja type. However, following the {lib:functions over methods} philosophy of typed, the properties above are actually defined through typed functions:

attribute

typed function

.vars

jinja_vars

.inner_vars

jinja_inner_vars

.free_vars

jinja_free_vars

table 4

Therefore, the same action could be taken directly, i.e, without initialization:

1from comp import jinja_vars, jinja_inner_vars, jinja_free_vars
2from some.where import my_jinja_string
3
4print(jinja_vars(my_jinja_string))        # {"inner": {"w": "something"}, "free": ("x", "y", z")}
5print(jinja_inner_vars(my_jinja_string))  # {"w": "something"}
6print(jinja_free_vars(my_jinja_string))   # ("x", "y", "z")

Rendering

The main objective of using jinja strings is to create “dynamic HTML”, where the dynamic aspects is provided precisely by the existence of {f:free jinja vars}, which are leaved to be fixed a posteriori. The process of transforming jinja strings into raw HTML is called rendering.

Notice that the inner vars of a jinja string already comes equipped with a value, which is set in the moment of its definition. To be rendered we then need to set values to its free jinja vars. This is done by defining a jinja context, which is a dictionary whose keys are strings with the names of the free jinja vars, and the values are the values that will be set to them.

As example of jinja context for the above jinja string would be:

1my_context = {
2    "x": some_value,
3    "y": other_value,
4    "z": another_value
5}

One time defined the context, one renders a jinja string by making use of the .render method:

1from comp import Jinja
2from some.where import my_jinja_string
3
4Jinja(my_jinja_string).render(**my_context)

Or, without initialization, through the typed function render:

1from comp import render
2from some.where import my_jinja_string
3
4render(my_jinja_string)

Factories

Inside comp we find some jinja factories. These are type factories constructing subtypes of Jinja. In other words, are typed functions taking values into SUB(Jinja), the type of subtypes of Jinja.

Highlight

To conclude, let us talk briefly on how to improve syntax highlight while working with jinja strings. We have two points to consider:

  1. jinja strings are used inside files with Python filetype;

  2. jinja strings use delimiters differing from the standard jinja delimiters.

From the first point, to get a nice highlighting one needs to mix both Python and Jinja highlights. This can be done by means of creating a custom filetype or by maintaining the Python filetype and using some regex filtering that includes Jinja highlight inside Python highlighting precisely for jinja strings.

Since in comp the only non-pythonic feature we will need is highlighting, the second option is highly preferable. Indeed, notice that jinja strings are defined as:

  1. Python strings

  2. prefixed with jinja

  3. that contains jinja syntax.

The fact that they are prefixed with a special keyword was introduced precisely to allow an easy parsing of the region that will be highlighted with Jinja syntax. Indeed: it is the region in between a """jinja and a triple quotes """.

There are multiple solutions to embed syntax of a language inside the syntax of other language by giving certain delimiters. Such solutions depend on the development environment. For vim you can use vim-SyntaxRange. For neovim, try nvim-treesitter.

In the case of vim with SyntaxRange, to embed Jinja highlight with the given delimiters you can just add the following line to your .vimrc:

autocmd BufWinEnter,FileType python call SyntaxRange#Include('f"""jinja', '"""', 'jinja', 'NonText')

Concerning the second point above, before embedding Jinja syntax inside Python syntax, one first need to modify the Jinja syntax to highlight the correct delimiters. For vim users, you can use Vim-Jinja2-Syntax with minor modifications.

Other Docs

  1. overview

  2. components

  3. models

  4. rendering

  5. compatibility

  6. styles

  7. glossary

  8. changelog