Исходный код lena.output.render_latex

"""Create LaTeX text from templates and data."""
from __future__ import print_function

import jinja2

import lena.context
import lena.core
import lena.flow
from lena.context import get_recursively as _get_recursively


jinja_syntax_latex = {
    "block_start_string": r'\BLOCK{',
    "block_end_string": '}',
    "variable_start_string": r'\VAR{',
    "variable_end_string": '}',
    "comment_start_string": r'\#{',
    "comment_end_string": '}',
    "line_statement_prefix": '%-',
    "line_comment_prefix": '%#',
    "trim_blocks": True,
    "lstrip_blocks": True,
    "autoescape": False,
}


class _Template(jinja2.Template):
    # this class is not used. Environment is used.
    """Adapt jijna2 template to LaTeX.

    Working with not completely rendered environments may be a problem. 
    For example, undefined variable's attributes
    were not processed correctly before Jinja 2.11
    (https://github.com/pallets/jinja/issues/811)
    ChainableUndefined (2.11) was suggested for that.
    """

    def __new__(cls, *args, **jkws):
        # Jinja Template uses not __init__(), but __new__().
        kws = jkws.copy()
        # todo: maybe remove this class,
        # or update jinja env with kwargs here (see _Environment)
        kws.update(jinja_syntax_latex)
        # how to efficiently merge two dicts: https://stackoverflow.com/a/26853961/952234
        # print kws
        sucls = super(_Template, cls)
        return sucls.__new__(sucls, *args, **kws)


class _Environment(jinja2.Environment):
    """Jinja environment."""

    def __init__(self, *args, **jkws):
        # kws = jkws.copy()
        # kws.update(_ljinja_env)
        kws = jinja_syntax_latex.copy()
        kws.update(jkws)
        super(_Environment, self).__init__(*args, **kws)
        # Python 3
        # super().__init__(*args, **kws)


def _is_csv(value):
    """Test whether context.output.filetype is "csv"."""
    context = lena.flow.get_context(value)
    return _get_recursively(
        context, "output.filetype", None
    ) == "csv"


def _select_template_or_default(val, default=""):
    data, context = lena.flow.get_data_context(val)
    out_template = _get_recursively(context, "output.template", None)
    if out_template:
        return out_template
    if not default:
        raise lena.core.LenaRuntimeError(
            "context contains no template and empty default provided, "
            "{} is context".format(context)
        )
    return default


[документация]class RenderLaTeX(object): """Create LaTeX from templates and data.""" def __init__(self, select_template="", template_dir=".", select_data=None, environment=None, from_data=False, verbose=0): """*select_template* is a string or a callable. If a string, it is the name of the template to be used (unless *context.output.template* overwrites that). If *select_template* is a callable, it must accept a value from the flow and return template name. If *select_template* is an empty string (default) and no template could be found in the context, :exc:`.LenaRuntimeError` is raised. *template_dir* is the path to the directory with templates (used by jinja2.FileSystemLoader). By default, it is the current directory. *select_data* is a callable to choose data to be rendered. It should accept a value from flow and return boolean. By default CSV files are selected (see :meth:`run`). *environment* allows user-defined initialisation of jinja Environment. One can use that to add custom `filters <https://jinja.palletsprojects.com/en/latest/api/#writing-filters>`_, tests, global functions, etc. In that case one must set *template_dir* for that environment manually. Example user initialisation: .. code-block:: python import jinja2 from lena.output import RenderLaTeX, jinja_syntax_latex # import user settings, filters and globals def render_latex(): \"\"\"Construct RenderLaTeX to be used in analysis sequences.\"\"\" loader = jinja2.FileSystemLoader(TEMPLATE_PATH) environment = jinja2.Environment( loader=loader, **jinja_syntax_latex ) environment.filters.update(FILTERS) environment.globals.update(GLOBALS) return RenderLaTeX( select_template=select_template, environment=environment ) Usually template context is stored in the context part of values. Sometimes, however, the data part contains the needed information (for example, during creation of tables). Set *from_data* to ``True`` to render the data part. *verbose* controls the verbosity of output. If it is 1, selected values are printed during :meth:`run`. If it is 2 or higher, not selected values are printed as well. """ # todo: verbose play role of debug, maybe rename that? # See other verbose elements as well. if isinstance(select_template, str): self._select_template = lambda _: \ _select_template_or_default(_, default=select_template) elif callable(select_template): self._select_template = select_template else: raise lena.core.LenaTypeError( "select_template must be a string or a callable, " "{}".format(select_template) + "provided" ) if select_data is None: self._select_data = _is_csv elif callable(select_data): self._select_data = select_data else: raise lena.core.LenaTypeError( "select_template must be a string or a callable, " "{} provided".format(select_template) ) if environment is None: loader = jinja2.FileSystemLoader(template_dir) self._environment = _Environment(loader=loader) else: if template_dir != '.': raise lena.core.LenaValueError( "only one of template_dir or environment " "must be provided, not both" ) self._environment = environment # this could be useful, but it can be done # simply by nesting context in the flow. # self._context_name = context_name self._from_data = from_data self._verbose = verbose
[документация] def run(self, flow): """Render values from *flow* to LaTeX. If no custom *select_data* was initialized, values with *context.output.filetype* equal to *"csv"* are selected by default. Rendered LaTeX text is yielded as the data part of the tuple (use :class:`.Write` to write that to the filesystem). *context.output.filetype* updates to *"tex"*. Not selected values pass unchanged. """ _update_recursively = lena.context.update_recursively verbose = self._verbose select_data = self._select_data for val in flow: if select_data(val): if verbose: print("# RenderLaTeX: selected", val) template = self._environment.get_template(self._select_template(val)) context = lena.flow.get_context(val) if not self._from_data: render_context = context else: render_context = lena.flow.get_data(val) _update_recursively(context, {"output": {"filetype": "tex"}}) _update_recursively(context, {"output": {"fileext": "tex"}}) # data filename is rendered from context, # e.g. item.output.filepath. # data part is not used during rendering, # but can be used for selection. # if not self._context_name: data = template.render(render_context) # else: # data = template.render({self._context_name: render_context}) yield (data, context) else: if verbose >= 2: print("# RenderLaTeX: not selected", val) yield val