Support `.jinja` for theme static templates (#11916)

Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
James Addison 2024-04-23 10:40:24 +01:00 committed by GitHub
parent 5a55856dd1
commit 0806a00f05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 77 additions and 14 deletions

View File

@ -13,6 +13,12 @@ Deprecated
Features added
--------------
* #11165: Support the `officially recommended`_ ``.jinja`` suffix for template
files.
Patch by James Addison and Adam Turner
.. _officially recommended: https://jinja.palletsprojects.com/en/latest/templates/#template-file-extension
Bugs fixed
----------

View File

@ -247,12 +247,28 @@ template static files as well as HTML files. Therefore, Sphinx supports
so-called "static templates", like this:
If the name of a file in the ``static/`` directory of a theme (or in the user's
static path, for that matter) ends with ``_t``, it will be processed by the
template engine. The ``_t`` will be left from the final file name. For
example, the *classic* theme has a file ``static/classic.css_t`` which uses
templating to put the color options into the stylesheet. When a documentation
project is built with the classic theme, the output directory will contain a
``_static/classic.css`` file where all template tags have been processed.
static path) ends with ``.jinja`` or ``_t``, it will be processed by the
template engine. The suffix will be removed from the final file name.
For example, a theme with a ``static/theme_styles.css.jinja`` file could use
templating to put options into the stylesheet.
When a documentation project is built with that theme,
the output directory will contain a ``_static/theme_styles.css`` file
where all template tags have been processed.
.. versionchanged:: 7.4
The preferred suffix for static templates is now ``.jinja``, in line with
the Jinja project's `recommended file extension`_.
The ``_t`` file suffix for static templates is now considered 'legacy', and
support may eventually be removed.
If a static template with either a ``_t`` suffix or a ``.jinja`` suffix is
detected, it will be processed by the template engine, with the suffix
removed from the final file name.
.. _recommended file extension: https://jinja.palletsprojects.com/en/latest/templates/#template-file-extension
Use custom page metadata in HTML templates

View File

@ -1826,16 +1826,21 @@ Miscellany
.. hint::
As an experimental feature, Sphinx can use user-defined template file for
LaTeX source if you have a file named ``_templates/latex.tex_t`` in your
LaTeX source if you have a file named ``_templates/latex.tex.jinja`` in your
project.
Additional files ``longtable.tex_t``, ``tabulary.tex_t`` and
``tabular.tex_t`` can be added to ``_templates/`` to configure some aspects
of table rendering (such as the caption position).
Additional files ``longtable.tex.jinja``, ``tabulary.tex.jinja`` and
``tabular.tex.jinja`` can be added to ``_templates/`` to configure some
aspects of table rendering (such as the caption position).
.. versionadded:: 1.6
currently all template variables are unstable and undocumented.
.. versionchanged:: 7.4
Added support for the ``.jinja`` file extension, which is preferred.
The old file names remain supported.
(``latex.tex_t``, ``longtable.tex_t``, ``tabulary.tex_t``, and ``tabular.tex_t``)
.. raw:: latex
\endgroup

View File

@ -15,6 +15,19 @@ if TYPE_CHECKING:
from sphinx.util.typing import PathMatcher
def _template_basename(filename: str | os.PathLike[str]) -> str | None:
"""Given an input filename:
If the input looks like a template, then return the filename output should
be written to. Otherwise, return no result (None).
"""
basename = os.path.basename(filename)
if basename.lower().endswith('_t'):
return str(filename)[:-2]
elif basename.lower().endswith('.jinja'):
return str(filename)[:-6]
return None
def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLike[str],
context: dict[str, Any] | None = None,
renderer: BaseRenderer | None = None) -> None:
@ -37,14 +50,13 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
else:
destination = str(destination)
if os.path.basename(source).endswith(('_t', '_T')) and context is not None:
if _template_basename(source) and context is not None:
if renderer is None:
from sphinx.util.template import SphinxRenderer
renderer = SphinxRenderer()
with open(source, encoding='utf-8') as fsrc:
if destination.endswith(('_t', '_T')):
destination = destination[:-2]
destination = _template_basename(destination) or destination
with open(destination, 'w', encoding='utf-8') as fdst:
fdst.write(renderer.render_string(fsrc.read(), context))
else:

View File

@ -0,0 +1 @@
AU REVOIR, KANIGGETS

View File

@ -0,0 +1,2 @@
<!-- testing legacy _t static templates -->
<html><project>{{ project | lower | escape }}</project></html>

View File

@ -1391,6 +1391,7 @@ def test_latex_table_custom_template_caseA(app, status, warning):
app.build(force_all=True)
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
assert 'SALUT LES COPAINS' in result
assert 'AU REVOIR, KANIGGETS' in result
@pytest.mark.sphinx('latex', testroot='latex-table',

View File

@ -109,6 +109,11 @@ def test_nested_zipped_theme(app, status, warning):
@pytest.mark.sphinx(testroot='theming', confoverrides={'html_theme': 'staticfiles'})
def test_staticfiles(app, status, warning):
app.build()
assert (app.outdir / '_static' / 'legacytmpl.html').exists()
assert (app.outdir / '_static' / 'legacytmpl.html').read_text(encoding='utf8') == (
'<!-- testing legacy _t static templates -->\n'
'<html><project>python</project></html>'
)
assert (app.outdir / '_static' / 'staticimg.png').exists()
assert (app.outdir / '_static' / 'statictmpl.html').exists()
assert (app.outdir / '_static' / 'statictmpl.html').read_text(encoding='utf8') == (

View File

@ -3,7 +3,7 @@
from unittest import mock
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.fileutil import copy_asset, copy_asset_file
from sphinx.util.fileutil import _template_basename, copy_asset, copy_asset_file
class DummyTemplateLoader(BuiltinTemplateLoader):
@ -101,3 +101,13 @@ def test_copy_asset(tmp_path):
assert not (destdir / '_static' / 'basic.css').exists()
assert (destdir / '_templates' / 'layout.html').exists()
assert not (destdir / '_templates' / 'sidebar.html').exists()
def test_template_basename():
assert _template_basename('asset.txt') is None
assert _template_basename('asset.txt.jinja') == 'asset.txt'
assert _template_basename('sidebar.html.jinja') == 'sidebar.html'
def test_legacy_template_basename():
assert _template_basename('asset.txt_t') == 'asset.txt'

View File

@ -42,7 +42,9 @@ METHOD_MAP = [
# Extraction from Python source files
('**.py', extract_python),
# Extraction from Jinja2 template files
('**/templates/latex/**.tex.jinja', extract_jinja2),
('**/templates/latex/**.tex_t', extract_jinja2),
('**/templates/latex/**.sty.jinja', extract_jinja2),
('**/templates/latex/**.sty_t', extract_jinja2),
# Extraction from Jinja2 HTML templates
('**/themes/**.html', extract_jinja2),
@ -50,6 +52,7 @@ METHOD_MAP = [
('**/themes/**.xml', extract_jinja2),
# Extraction from JavaScript files
('**.js', extract_javascript),
('**.js.jinja', extract_javascript),
('**.js_t', extract_javascript),
]
OPTIONS_MAP = {
@ -58,7 +61,9 @@ OPTIONS_MAP = {
'encoding': 'utf-8',
},
# Extraction from Jinja2 template files
'**/templates/latex/**.tex.jinja': TEX_DELIMITERS.copy(),
'**/templates/latex/**.tex_t': TEX_DELIMITERS.copy(),
'**/templates/latex/**.sty.jinja': TEX_DELIMITERS.copy(),
'**/templates/latex/**.sty_t': TEX_DELIMITERS.copy(),
# Extraction from Jinja2 HTML templates
'**/themes/**.html': {