Source code for pysmith.contrib.web.template

import logging
import os

import jinja2

import pysmith.plugin_util


logger = logging.getLogger("pysmith.plugin.template")


[docs]class _BaseTemplate(object): """ This class is not intended to be instantiated directly, but instead just serves to hold common logic for both template plugins. :param str match_pattern: The pattern of files to process. :param globals: Global values to insert into the underlying :class:`~jinja2.Environment`. :type globals: dict(str, object) :param str global_include: The name of a template (to be retrieved using :meth:`~jinja2.Environment.get_template`) containing macros that will be included in the globals list of the :class:`~jinja2.Environment`. The macros will be available in all templates, but will not have access to the rendering context. :param environment_args: A dictionary of options to pass to the :class:`~jinja2.Environment` constructor. :type environment_args: dict(str, object) """ def __init__(self, *, match_pattern="*", globals=None, global_include=None, environment_args={}): self._match_pattern = match_pattern self._jinja = jinja2.Environment(**environment_args) if globals: self._jinja.globals.update(globals) if global_include: template = self._jinja.get_template(global_include) for key, val in template.module.__dict__.items(): if isinstance(val, jinja2.runtime.Macro): self._jinja.globals[key] = val def build(self, build_info): for file_name, file_info in build_info.get_files_by_pattern(self._match_pattern): output_name = self.process_file(build_info, file_name, file_info) if output_name is not None: build_info.rename_file(file_name, output_name) def process_file(self, build_info, file_name, file_info): # pragma: no cover raise NotImplementedError("process_file is not implemented")
[docs]class ContentTemplate(_BaseTemplate): """ Treats the contents of the files as a template and renders it. This can be used to do pre-processing on the source files. All parameters specified in :class:`_BaseTemplate` are valid for this class as well. When the template is rendered, the build info :attr:`~pysmith.BuildInfo.metadata` will be available as :code:`site`. """ def process_file(self, build_info, file_name, file_info): template = self._jinja.from_string(file_info.contents.decode()) file_info.contents = template.render(site=build_info.metadata).encode()
[docs]class LayoutTemplate(_BaseTemplate): """ Treats the contents of the files as a variable to pass into a template. The layout to use is selected by the :code:`layout_selector` parameter. When the template is rendered, the file :attr:`~pysmith.FileInfo.contents` will be available in the rendering context as :code:`contents`, the file :attr:`~pysmith.FileInfo.metadata` will be available as :code:`page`, and the build info :attr:`~pysmith.BuildInfo.metadata` will be available as :code:`site`. In addition to the parameters specified below, all parameters specified in :class:`_BaseTemplate` are valid for this class as well. :param layout_selector: The layout selector. If this is a string, it will be used as the key to look up the template in the file metadata. If this is a function, it will be executed to find the name of the template to use. :type layout_selector: str or func(:class:`~pysmith.FileInfo`) :param str output_extension: The extension to use for the output file. The file info will be moved to a new key in the files dictionary if the file needs to be renamed to have the correct extension. """ def __init__(self, *, layout_selector="layout", output_extension=".html", **kwargs): super().__init__(**kwargs) self._output_extension = output_extension self._layout_selector = pysmith.plugin_util.lambda_or_metadata_selector(layout_selector) def process_file(self, build_info, file_name, file_info): try: template_name = self._layout_selector(file_info) except Exception: logger.error("Could not find layout for {}".format(file_name)) return None template = self._jinja.get_template(template_name) file_info.contents = template.render(contents=file_info.contents.decode(), page=file_info.metadata, site=build_info.metadata).encode() file_name_parts = os.path.splitext(file_name) if file_name_parts[1] == self._output_extension: return None else: return file_name_parts[0] + self._output_extension