"""Create LaTeX text from templates and data."""
from __future__ import print_function
import jinja2
import lena.core
import lena.flow
import lena.context
from lena.context import get_recursively as _get_recursively
_ljinja_env = {
"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 are not processed correctly,
https://github.com/pallets/jinja/issues/811
New class was suggested to be written for that.
"""
def __new__(cls, *args, **jkws):
# Jinja Template uses not __init__(), but __new__().
kws = jkws.copy()
kws.update(_ljinja_env)
# 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)
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
[docs]class RenderLaTeX(object):
"""Create LaTeX from templates and data."""
def __init__(self, select_template="", template_dir=".", select_data=None,
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`).
*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.
"""
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)
)
self._loader = jinja2.FileSystemLoader(template_dir)
self._environment = _Environment(loader=self._loader)
self._verbose = verbose
# print("templates:", self._environment.list_templates())
[docs] 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)
_update_recursively(context, {"output": {"filetype": "tex"}})
_update_recursively(context, {"output": {"fileext": "tex"}})
data = template.render(context)
yield (data, context)
else:
if verbose >= 2:
print("# RenderLaTeX: not selected", val)
yield val