Files
Django_kube/Webcal-python/venv/lib/python3.11/site-packages/tatsu/rendering.py
T

142 lines
3.8 KiB
Python

"""
The Renderer class provides the infrastructure for generating template-based
code. It's used by the .grammars module for parser generation.
"""
from __future__ import annotations
import itertools
import string
from .util import indent, isiter, trim
def render(item, join='', **fields):
"""Render the given item"""
if item is None:
return ''
elif isinstance(item, str):
return item
elif isinstance(item, Renderer):
return item.render(join=join, **fields)
elif isiter(item):
return join.join(
render(e, **fields) for e in iter(item) if e is not None
)
elif isinstance(item, int | float):
return item
else:
return str(item)
class RenderingFormatter(string.Formatter):
def render(self, item, join='', **fields):
return render(item, join=join, **fields)
def format_field(self, value, format_spec):
if ':' not in format_spec:
return super().format_field(self.render(value), format_spec)
ind, sep, fmt = format_spec.split(':')
if sep == '\\n':
sep = '\n'
if not ind:
ind = 0
mult = 0
elif '*' in ind:
ind, mult = ind.split('*')
else:
mult = 4
ind = int(ind)
mult = int(mult)
if not fmt:
fmt = '%s'
if isiter(value):
return indent(
sep.join(fmt % self.render(v) for v in value), ind, mult,
)
else:
return indent(fmt % self.render(value), ind, mult)
class Renderer:
"""Renders the fileds in the current object using a template
provided statically, on the constructor, or as a parameter
to render().
Fields with a leading underscore are not made available to
the template. Additional fields may be made available by
overriding render_fields().
"""
template = '{__class__}'
_counter = itertools.count()
_formatter = RenderingFormatter()
def __init__(self, template=None):
if template is not None:
self.template = template
@classmethod
def counter(cls):
return next(cls._counter)
@classmethod
def reset_counter(cls):
Renderer._counter = itertools.count()
@property
def formatter(self):
return self._formatter
@formatter.setter
def formatter(self, value):
self._formatter = value
def rend(self, item, join='', **fields):
"""A shortcut for self._formatter.render()"""
return self._formatter.render(item, join=join, **fields)
def indent(self, item, ind=1, multiplier=4):
return indent(self.rend(item), indent=ind, multiplier=multiplier)
def trim(self, item, tabwidth=4):
return trim(self.rend(item), tabwidth=tabwidth)
def render_fields(self, fields):
"""Pre-render fields before rendering the template."""
return
def render(self, **fields):
template = fields.pop('template', None)
fields.update(__class__=self.__class__.__name__)
fields.update(
{k: v for k, v in vars(self).items() if not k.startswith('_')},
)
override = self.render_fields(
fields,
) # pylint: disable=assignment-from-none
if override is not None:
template = override
elif template is None:
template = self.template
try:
return self._formatter.format(trim(template), **fields)
except KeyError as e:
# find the missing key
keys = (p[1] for p in self._formatter.parse(template))
for key in keys:
if key and key not in fields:
raise KeyError(key, type(self)) from e
raise
def __str__(self):
return self.render()
def __repr__(self):
return str(self)