Templating¶
This guide explains how to write Jinja2 templates for the
sphinxnotes.render-based extension (sphinxnotes.render.ext,
sphinxnotes.any and etc.). You should already be comfortable with basic
Jinja2 syntax before reading this page.
What is a Template¶
A template is a Jinja2 text that defines how structured data is converted into reStructuredText or Markdown markup. The rendered text is then parsed by Sphinx and inserted into the document.
The way of defining template will vary depending on the extension you use.
For sphinxnotes.render.ext, you can use data.template or
data_define_directives.
Tip
Internally, template is a Template object.
It is provide by method
BaseDataDefineDirective.current_template()
or
BaseDataDefineRole.current_template()
What Data is Available¶
Your template receives data from two sources: main context and extra context.
Main Context¶
When you define data through a directive (such as data.define) or
role in your document, the template receives that data as its main context.
This is the data explicitly provided by the markup itself.
For example, when you use the data.define directive, the generated main
context looks like the Python dict on the right:
.. data.define:: mimi
:color: black and brown
I like fish!
{
'name': 'mimi',
'attrs': {'color': 'black and brown'},
'content': 'I like fish!'
# Lifted attrs
'color': 'black and brown',
}
The template receives the argument (mimi), options (:color: black ...),
and body content (I like fish!) as the main context.
The following template variables are available:
{{ name }}¶For directives, this refers to the directive argument.
Source.. data.template:: {{ name }} .. data.define:: This is the argument
ResultThis is the argument
For roles, this is not available.
{{ attrs }}¶For directives, this refers to directive options. It is a mapping of option field to value, so
{{ attrs.label }}and{{ attrs['label'] }}are equivalent.Source.. data.template:: Label is {{ attrs.label }}. .. data.define:: :label: Important
ResultLabel is Important.
For roles, this is not available.
Attribute values are lifted to the top-level template context when there is no name conflict. For example,
{{ label }}can be used instead of{{ attrs.label }}:Source.. data.template:: Label is {{ label }}. .. data.define:: :label: Important
ResultLabel is Important.
{{ content }}¶For directives, this refers to the directive body.
Source.. data.template:: {{ content }} .. data.define:: This is the body content.
ResultThis is the body content.
For roles, this refers to the interpreted text.
Source.. data.template:: {{ content }} :data:`This is the interpreted text`
Result
The type of each variable depends on the corresponding schema. Different
extensions define schemas differently. For example, the sphinxnotes.render.ext
extension defines the schema through the data.schema directive or
schema field of data_define_directives.
Tip
Internally, Main context is a ParsedData
object.
Directive or role subclassed from
BaseDataDefineDirective or
BaseDataDefineRole can generate main context.
Extra Context¶
Extra context provides access to pre-prepared structured data from external sources (such as Sphinx application, JSON file, and etc.). Unlike main context which comes from the directive/role itself, extra context lets you fetch data that was prepared beforehand.
Extra contexts are typically generated on demand at different construction stages,
so you need to declare them in advance, and load it in the template using the
load_extra() function:
The way of declaring extra context is vary depending on the extension you use.
For sphinxnotes.render.ext extension, data.template:extra,
data.render:extra and the templat.extra field of
data_define_directives are for this.
.. data.render::
:extra: doc
{% set doc = load_extra('doc') %}
Document Title: "{{ doc.title }}"
Document Title: “Templating”
Built-in Extra Contexts¶
The following extra contexts are available:
sphinx- Phase:
all
A proxy to the
sphinx.application.Sphinxobject.Source.. data.render:: :extra: sphinx {% set app = load_extra('sphinx') %} **{{ app.extensions | length }}** extensions are loaded.
Result74 extensions are loaded.
env- Phase:
all
A proxy to the
sphinx.environment.BuildEnvironmentobject.Source.. data.render:: :extra: env {% set env = load_extra('env') %} **{{ env.all_docs | length }}** documents found.
Result6 documents found.
markup- Phase:
parsing and later
Information about the current directive or role invocation, such as its type, name, source text, and line number.
Source.. data.render:: :extra: markup {% set m = load_extra('markup') | jsonify %} .. code:: {% for line in m.split('\n') -%} {{ line }} {% endfor %}
Result{ "type": "directive", "name": "data.render", "lineno": 251, "rawtext": ".. data.render::\n :extra: markup\n\n {%\n set m = load_extra('markup')\n | jsonify\n %}\n\n .. code::\n\n {% for line in m.split('\\n') -%}\n {{ line }}\n {% endfor %}" }
section- Phase:
parsing and later
A proxy to the current
docutils.nodes.sectionnode, when one exists.Source.. data.render:: :extra: section Section Title: "{{ load_extra('section').title }}"
Resultdoc- Phase:
parsing and later
A proxy to the current
docutils.notes.documentnode.Source.. data.render:: :extra: doc Document title: "{{ load_extra('doc').title }}".
ResultDocument title: “Templating”.
See also
TODO: the proxy object.
Built-in Filters¶
In addition to the Builtin Jinia Filters, this extension also provides the following filters:
rolesProduces role markup from a sequence of strings.
Source.. data.render:: {% set text = ['index', 'usage'] | roles('doc') | join(', ') %} :Text: ``{{ text }}`` :Rendered: {{ text }}
Result- Text:
:doc:`index`, :doc:`usage`- Rendered:
jsonifyConvert value to JSON.
Source.. data.render:: {% set text = {'name': 'mimi'} %} :Strify: ``{{ text }}`` :JSONify: ``{{ text | jsonify }}``
See also
Render Phases¶
Each template has a render phase that determines when it is processed:
parsing¶Render immediately while the directive or role is running. This is the default.
Choose this when the template only needs local information and does not rely on the final doctree or cross-document state.
Source.. data.render:: :on: parsing :extra: doc env {% set doc = load_extra('doc') %} {% set env = load_extra('env') %} - The current document has {{ doc.sections | length }} section(s). - The current project has {{ env.all_docs | length }} document(s).
ResultThe current document has 4 section(s).
The current project has 6 document(s).
parsed¶Render after the current document has been parsed.
Choose this when the template needs the complete doctree of the current document.
Source.. data.render:: :on: parsed :extra: doc env {% set doc = load_extra('doc') %} {% set env = load_extra('env') %} - The current document has {{ doc.sections | length }} section(s). - The current project has {{ env.all_docs | length }} document(s).
ResultThe current document has 6 section(s).
The current project has 6 document(s).
resolving¶Render late in the build, after references and other transforms are being resolved.
Choose this when the template depends on the document structure that is only stable near the end of the pipeline.
Source.. data.render:: :on: resolving :extra: doc env {% set doc = load_extra('doc') %} {% set env = load_extra('env') %} - The current document has {{ doc.sections | length }} section(s). - The current project has {{ env.all_docs | length }} document(s).
ResultThe current document has 6 section(s).
The current project has 8 document(s).
Debugging¶
Enable the debug option to see a detailed report when troubleshooting templates:
.. data.render::
:debug:
{{ 1 + 1 }}
2
This is especially useful when a template fails due to an undefined variable, unexpected data shape, or invalid generated markup.
Some Technical Details¶
Jinja Template¶
Templates are rendered in a sandboxed Jinja2 environment.
Undefined variables raise errors by default (
undefined=DebugUndefined)Extension
jinja2.ext.loopcontrols,jinja2.ext.doare enabled by default.