http://ericholscher.comEric Holscher - Posts tagged training2024-02-28T16:22:29.176822+00:00ABloghttp://ericholscher.com/blog/2016/jul/25/integrating-jinja-rst-sphinx/The Power of Sphinx: Integrating Jinja with RST2016-07-25T09:00:00+00:00<section id="the-power-of-sphinx-integrating-jinja-with-rst">
<p><a class="reference external" href="http://www.sphinx-doc.org/en/stable/">Sphinx</a> is a super powerful tool.
This has its upsides and downsides.
One of the major downsides is that historically it has been built as a framework that allows users to do just about anything.
This is great,
except it also means that a lot of the specific value out of the modular design hasn’t been documented or made explicit to users.
I’m hoping to address some of this power in a set of blog posts.</p>
<p>Today I’ll cover integrating the templating language <a class="reference external" href="http://jinja.pocoo.org/docs/dev/templates/">Jinja</a> inside your RST files.
This is a really useful thing,
and allows a large number of powerful display semantics inside of your RST files.</p>
<p>Jinja is also the templating engine that Sphinx uses internally for rendering HTML.
This means you already have the library installed with Sphinx,
so no external dependency is required.</p>
<section id="the-extension">
<h2>The Extension</h2>
<p>Most of the power of Sphinx comes from the ability to plug into any part of the documentation building process.
Sphinx has an exhaustive list of <a class="reference external" href="https://www.sphinx-doc.org/en/master/extdev/appapi.html#events" title="(in Sphinx v7.3.0)"><span>Sphinx core events</span></a>,
which I recommend reading up on.
This extension will use the <code class="docutils literal notranslate"><span class="pre">source-read</span></code> hook,
which fires when the actual RST file is read from the disk.</p>
<p>From there it is the simple matter of taking the source that has been read,
and passing it through Jinja.
Here is the full extension:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">rstjinja</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">docname</span><span class="p">,</span> <span class="n">source</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""</span>
<span class="sd"> Render our pages as a jinja template for fancy templating goodness.</span>
<span class="sd"> """</span>
<span class="c1"># Make sure we're outputting HTML</span>
<span class="k">if</span> <span class="n">app</span><span class="o">.</span><span class="n">builder</span><span class="o">.</span><span class="n">format</span> <span class="o">!=</span> <span class="s1">'html'</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">src</span> <span class="o">=</span> <span class="n">source</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">rendered</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">builder</span><span class="o">.</span><span class="n">templates</span><span class="o">.</span><span class="n">render_string</span><span class="p">(</span>
<span class="n">src</span><span class="p">,</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">html_context</span>
<span class="p">)</span>
<span class="n">source</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">rendered</span>
<span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
<span class="n">app</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">"source-read"</span><span class="p">,</span> <span class="n">rstjinja</span><span class="p">)</span>
</pre></div>
</div>
<p>This simple extension gives us a ton of power.
Let’s talk through one example.</p>
</section>
<section id="generating-docs-from-data">
<h2>Generating Docs from Data</h2>
<p>A lot of times we have an external data set that we want to generate into our documentation.
Let’s say this is a list of team members that is maintained in a remote system,
and we want to include in our docs.
If we can get that data into JSON,
it’s pretty simple to add it into our project.</p>
<p>In our <code class="docutils literal notranslate"><span class="pre">conf.py</span></code>,
or another Sphinx extension,
we can do:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
<span class="n">staff</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">file</span><span class="p">(</span><span class="s1">'data/company-staff.json'</span><span class="p">))</span>
<span class="n">html_context</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'company_staff'</span><span class="p">:</span> <span class="n">staff</span>
<span class="p">}</span>
</pre></div>
</div>
<p>This gives us the <code class="docutils literal notranslate"><span class="pre">company_staff</span></code> variable in our HTML context,
which will be availabe from Jinja.
Then we can generate our <code class="docutils literal notranslate"><span class="pre">staff.rst</span></code> file:</p>
<div class="highlight-rst notranslate"><div class="highlight"><pre><span></span><span class="gh">Staff</span>
<span class="gh">=====</span>
{% for member in company_staff %}
{% if member.is_active %}
<span class="m">*</span> <span class="s">`{{ member.name }} </span><span class="si"><{{ member.url }}></span><span class="s">`_</span>
{% else %}
<span class="m">*</span> {{ member.name }} (Emeritus)
{% endif %}
{% endfor %}
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The Jinja templates will be rendered <em>before</em> the RST is processed.</p>
</div>
<p>Allowing ourselves to use Jinja inside of RST gives us a whole set of logic that isn’t available in the RST language itself.</p>
<p>This approach is incredibly powerful,
but please make sure you don’t overdo it!
Try to keep the Jinja logic simple,
and only apply it to things that alter the display of the page.</p>
</section>
<section id="want-more-tips">
<h2>Want more tips?</h2>
<p>I love talking and thinking about the power of Sphinx.
Along with blogging,
I provide Sphinx documentation training for companies.
We do half-day, full-day, and multi-day classes.
Shoot me an <a class="reference external" href="mailto:training%40readthedocs.com">email</a> or check out our <a class="reference external" href="http://www.sphinxtraining.com">sphinx training website</a> for more info.</p>
</section>
</section>
Sphinx is a super powerful tool.
This has its upsides and downsides.
One of the major downsides is that historically it has been built as a framework that allows users to do just about anything.
This is great,
except it also means that a lot of the specific value out of the modular design hasn’t been documented or made explicit to users.
I’m hoping to address some of this power in a set of blog posts.2016-07-25T09:00:00+00:00