| # -*- coding: utf-8 -*- |
| """ |
| sphinx.jinja2glue |
| ~~~~~~~~~~~~~~~~~ |
| |
| Glue code for the jinja2 templating engine. |
| |
| :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. |
| :license: BSD, see LICENSE for details. |
| """ |
| |
| from os import path |
| from pprint import pformat |
| |
| from six import string_types |
| from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, \ |
| contextfunction |
| from jinja2.utils import open_if_exists |
| from jinja2.sandbox import SandboxedEnvironment |
| |
| from sphinx.application import TemplateBridge |
| from sphinx.util.osutil import mtimes_of_files |
| |
| |
| def _tobool(val): |
| if isinstance(val, string_types): |
| return val.lower() in ('true', '1', 'yes', 'on') |
| return bool(val) |
| |
| def _toint(val): |
| try: |
| return int(val) |
| except ValueError: |
| return 0 |
| |
| def accesskey(context, key): |
| """Helper to output each access key only once.""" |
| if '_accesskeys' not in context: |
| context.vars['_accesskeys'] = {} |
| if key and key not in context.vars['_accesskeys']: |
| context.vars['_accesskeys'][key] = 1 |
| return 'accesskey="%s"' % key |
| return '' |
| |
| class idgen(object): |
| def __init__(self): |
| self.id = 0 |
| def current(self): |
| return self.id |
| def __next__(self): |
| self.id += 1 |
| return self.id |
| next = __next__ # Python 2/Jinja compatibility |
| |
| |
| class SphinxFileSystemLoader(FileSystemLoader): |
| """ |
| FileSystemLoader subclass that is not so strict about '..' entries in |
| template names. |
| """ |
| |
| def get_source(self, environment, template): |
| for searchpath in self.searchpath: |
| filename = path.join(searchpath, template) |
| f = open_if_exists(filename) |
| if f is None: |
| continue |
| try: |
| contents = f.read().decode(self.encoding) |
| finally: |
| f.close() |
| |
| mtime = path.getmtime(filename) |
| def uptodate(): |
| try: |
| return path.getmtime(filename) == mtime |
| except OSError: |
| return False |
| return contents, filename, uptodate |
| raise TemplateNotFound(template) |
| |
| |
| |
| class BuiltinTemplateLoader(TemplateBridge, BaseLoader): |
| """ |
| Interfaces the rendering environment of jinja2 for use in Sphinx. |
| """ |
| |
| # TemplateBridge interface |
| |
| def init(self, builder, theme=None, dirs=None): |
| # create a chain of paths to search |
| if theme: |
| # the theme's own dir and its bases' dirs |
| pathchain = theme.get_dirchain() |
| # then the theme parent paths |
| loaderchain = pathchain + theme.themepath |
| elif dirs: |
| pathchain = list(dirs) |
| loaderchain = list(dirs) |
| else: |
| pathchain = [] |
| loaderchain = [] |
| |
| # prepend explicit template paths |
| self.templatepathlen = len(builder.config.templates_path) |
| if builder.config.templates_path: |
| cfg_templates_path = [path.join(builder.confdir, tp) |
| for tp in builder.config.templates_path] |
| pathchain[0:0] = cfg_templates_path |
| loaderchain[0:0] = cfg_templates_path |
| |
| # store it for use in newest_template_mtime |
| self.pathchain = pathchain |
| |
| # make the paths into loaders |
| self.loaders = [SphinxFileSystemLoader(x) for x in loaderchain] |
| |
| use_i18n = builder.app.translator is not None |
| extensions = use_i18n and ['jinja2.ext.i18n'] or [] |
| self.environment = SandboxedEnvironment(loader=self, |
| extensions=extensions) |
| self.environment.filters['tobool'] = _tobool |
| self.environment.filters['toint'] = _toint |
| self.environment.globals['debug'] = contextfunction(pformat) |
| self.environment.globals['accesskey'] = contextfunction(accesskey) |
| self.environment.globals['idgen'] = idgen |
| if use_i18n: |
| self.environment.install_gettext_translations( |
| builder.app.translator) |
| |
| def render(self, template, context): |
| return self.environment.get_template(template).render(context) |
| |
| def render_string(self, source, context): |
| return self.environment.from_string(source).render(context) |
| |
| def newest_template_mtime(self): |
| return max(mtimes_of_files(self.pathchain, '.html')) |
| |
| # Loader interface |
| |
| def get_source(self, environment, template): |
| loaders = self.loaders |
| # exclamation mark starts search from theme |
| if template.startswith('!'): |
| loaders = loaders[self.templatepathlen:] |
| template = template[1:] |
| for loader in loaders: |
| try: |
| return loader.get_source(environment, template) |
| except TemplateNotFound: |
| pass |
| raise TemplateNotFound(template) |