From cf7da1843c45a4c2df7a749f7886a2d2ba0ee92a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 19:25:40 +0200 Subject: Adding upstream version 7.2.6. Signed-off-by: Daniel Baumann --- sphinx/util/template.py | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 sphinx/util/template.py (limited to 'sphinx/util/template.py') diff --git a/sphinx/util/template.py b/sphinx/util/template.py new file mode 100644 index 0000000..a16a7a1 --- /dev/null +++ b/sphinx/util/template.py @@ -0,0 +1,135 @@ +"""Templates utility functions for Sphinx.""" + +from __future__ import annotations + +import os +from functools import partial +from os import path +from typing import TYPE_CHECKING, Any, Callable + +from jinja2 import TemplateNotFound +from jinja2.loaders import BaseLoader +from jinja2.sandbox import SandboxedEnvironment + +from sphinx import package_dir +from sphinx.jinja2glue import SphinxFileSystemLoader +from sphinx.locale import get_translator +from sphinx.util import rst, texescape + +if TYPE_CHECKING: + from collections.abc import Sequence + + from jinja2.environment import Environment + + +class BaseRenderer: + def __init__(self, loader: BaseLoader | None = None) -> None: + self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n']) + self.env.filters['repr'] = repr + self.env.install_gettext_translations(get_translator()) + + def render(self, template_name: str, context: dict[str, Any]) -> str: + return self.env.get_template(template_name).render(context) + + def render_string(self, source: str, context: dict[str, Any]) -> str: + return self.env.from_string(source).render(context) + + +class FileRenderer(BaseRenderer): + def __init__(self, search_path: Sequence[str | os.PathLike[str]]) -> None: + if isinstance(search_path, (str, os.PathLike)): + search_path = [search_path] + else: + # filter "None" paths + search_path = list(filter(None, search_path)) + + loader = SphinxFileSystemLoader(search_path) + super().__init__(loader) + + @classmethod + def render_from_file(cls, filename: str, context: dict[str, Any]) -> str: + dirname = os.path.dirname(filename) + basename = os.path.basename(filename) + return cls(dirname).render(basename, context) + + +class SphinxRenderer(FileRenderer): + def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None) -> None: + if template_path is None: + template_path = os.path.join(package_dir, 'templates') + super().__init__(template_path) + + @classmethod + def render_from_file(cls, filename: str, context: dict[str, Any]) -> str: + return FileRenderer.render_from_file(filename, context) + + +class LaTeXRenderer(SphinxRenderer): + def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None, + latex_engine: str | None = None) -> None: + if template_path is None: + template_path = [os.path.join(package_dir, 'templates', 'latex')] + super().__init__(template_path) + + # use texescape as escape filter + escape = partial(texescape.escape, latex_engine=latex_engine) + self.env.filters['e'] = escape + self.env.filters['escape'] = escape + self.env.filters['eabbr'] = texescape.escape_abbr + + # use JSP/eRuby like tagging instead because curly bracket; the default + # tagging of jinja2 is not good for LaTeX sources. + self.env.variable_start_string = '<%=' + self.env.variable_end_string = '%>' + self.env.block_start_string = '<%' + self.env.block_end_string = '%>' + self.env.comment_start_string = '<#' + self.env.comment_end_string = '#>' + + +class ReSTRenderer(SphinxRenderer): + def __init__(self, template_path: Sequence[str | os.PathLike[str]] | None = None, + language: str | None = None) -> None: + super().__init__(template_path) + + # add language to environment + self.env.extend(language=language) + + # use texescape as escape filter + self.env.filters['e'] = rst.escape + self.env.filters['escape'] = rst.escape + self.env.filters['heading'] = rst.heading + + +class SphinxTemplateLoader(BaseLoader): + """A loader supporting template inheritance""" + + def __init__(self, confdir: str | os.PathLike[str], + templates_paths: Sequence[str | os.PathLike[str]], + system_templates_paths: Sequence[str | os.PathLike[str]]) -> None: + self.loaders = [] + self.sysloaders = [] + + for templates_path in templates_paths: + loader = SphinxFileSystemLoader(path.join(confdir, templates_path)) + self.loaders.append(loader) + + for templates_path in system_templates_paths: + loader = SphinxFileSystemLoader(templates_path) + self.loaders.append(loader) + self.sysloaders.append(loader) + + def get_source(self, environment: Environment, template: str) -> tuple[str, str, Callable]: + if template.startswith('!'): + # search a template from ``system_templates_paths`` + loaders = self.sysloaders + template = template[1:] + else: + loaders = self.loaders + + for loader in loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) -- cgit v1.2.3