Template compiler and interpreter¶
Import Template, TemplateProgram, render_template, TemplateFilter, and
TEMPLATE_FILTERS from rmote.templates. The classes below implement the
compiled representation; the package exports the same class objects.
See Templating for the restricted language, trust boundaries, and examples. Filters are documented in Template filters.
Template tokens, parsing, and compiled intermediate representation.
- class rmote.templates.engine.AttributeExpression(value, name)[source]¶
Bases:
ResolvableExpression- resolve(context)[source]¶
Allow lookup expressions to retain missing values for
default.- Return type:
- value: Expression¶
- class rmote.templates.engine.BinaryExpression(operator, left, right)[source]¶
Bases:
Expression- left: Expression¶
- right: Expression¶
- class rmote.templates.engine.Branch(condition, body)[source]¶
Bases:
object- body: tuple[TemplateNode, ...]¶
- condition: Expression | None¶
- class rmote.templates.engine.BreakNode(position)[source]¶
Bases:
TemplateNode
- class rmote.templates.engine.CodeToken(type, string, position)[source]¶
Bases:
objectOne code lexeme with its token type and absolute source offset.
- class rmote.templates.engine.CollectionExpression(kind, items)[source]¶
Bases:
Expression- items: tuple[Expression, ...]¶
- class rmote.templates.engine.CommandToken(value, position, name, tokens)[source]¶
Bases:
TemplateTokenA command name and argument tokens, separated once by the lexer.
- class rmote.templates.engine.CommentToken(value, position)[source]¶
Bases:
TemplateTokenA comment retained for inspection but omitted from execution.
- class rmote.templates.engine.ComparisonExpression(operands, operators)[source]¶
Bases:
Expression- operands: tuple[Expression, ...]¶
- class rmote.templates.engine.ConditionalExpression(condition, positive, negative)[source]¶
Bases:
Expression- condition: Expression¶
- negative: Expression¶
- positive: Expression¶
- class rmote.templates.engine.ContinueNode(position)[source]¶
Bases:
TemplateNode
- class rmote.templates.engine.DictionaryExpression(items)[source]¶
Bases:
Expression- items: tuple[tuple[Expression, Expression], ...]¶
- class rmote.templates.engine.Expression[source]¶
Bases:
objectA compiled expression with no access to Python globals or callables.
- class rmote.templates.engine.ExpressionParser(tokens, filters)[source]¶
Bases:
objectPratt parser for the expression allowlist, including postfix filters.
- COMPARISON_PRECEDENCE = 40¶
- CONDITIONAL_PRECEDENCE = 5¶
- LITERALS = {'False': False, 'None': None, 'True': True, 'false': False, 'none': None, 'true': True}¶
- PRECEDENCE = {'!=': 40, '%': 60, '*': 60, '**': 80, '+': 50, '-': 50, '.': 100, '/': 60, '//': 60, '<': 40, '<=': 40, '==': 40, '>': 40, '>=': 40, '[': 100, 'and': 20, 'if': 5, 'in': 40, 'is': 40, 'is not': 40, 'not in': 40, 'or': 10, '|': 90}¶
- PREFIX_PRECEDENCE = {'+': 95, '-': 95, 'not': 35}¶
- sequence(closing, parse_item, first=())[source]¶
Read comma-separated items with an optional trailing comma.
- class rmote.templates.engine.ExpressionToken(value, position, tokens)[source]¶
Bases:
TemplateTokenAn interpolation and its code tokens, ready for the expression parser.
- class rmote.templates.engine.FilterExpression(name, value, arguments, keywords)[source]¶
Bases:
ResolvableExpression- arguments: tuple[Expression, ...]¶
- keywords: tuple[tuple[str, Expression], ...]¶
- resolve(context)[source]¶
Allow lookup expressions to retain missing values for
default.- Return type:
- value: Expression¶
- class rmote.templates.engine.ForNode(position, target, iterable, body, otherwise)[source]¶
Bases:
TemplateNode- body: tuple[TemplateNode, ...]¶
- iterable: Expression¶
- otherwise: tuple[TemplateNode, ...]¶
- target: AssignmentTarget¶
- class rmote.templates.engine.IfNode(position, branches)[source]¶
Bases:
TemplateNode
- class rmote.templates.engine.LiteralExpression(value)[source]¶
Bases:
Expression
- class rmote.templates.engine.LookupExpression(value, key)[source]¶
Bases:
ResolvableExpression- key: Expression¶
- resolve(context)[source]¶
Allow lookup expressions to retain missing values for
default.- Return type:
- value: Expression¶
- class rmote.templates.engine.OutputNode(position, expression)[source]¶
Bases:
TemplateNode- expression: Expression¶
- class rmote.templates.engine.RenderContext(values, filters)[source]¶
Bases:
objectPer-render values, trusted filter instances, and output buffer.
- class rmote.templates.engine.ResolvableExpression[source]¶
Bases:
ExpressionA lookup or filter whose missing result can be consumed by
default.
- class rmote.templates.engine.SetNode(position, target, expression)[source]¶
Bases:
TemplateNode- expression: Expression¶
- target: AssignmentTarget¶
- class rmote.templates.engine.SliceExpression(start, stop, step)[source]¶
Bases:
Expression- start: Expression | None¶
- step: Expression | None¶
- stop: Expression | None¶
- class rmote.templates.engine.Template(template, filters=mappingproxy({'lower': <class 'rmote.templates.filters.LowerFilter'>, 'upper': <class 'rmote.templates.filters.UpperFilter'>, 'capitalize': <class 'rmote.templates.filters.CapitalizeFilter'>, 'trim': <class 'rmote.templates.filters.TrimFilter'>, 'replace': <class 'rmote.templates.filters.ReplaceFilter'>, 'join': <class 'rmote.templates.filters.JoinFilter'>, 'length': <class 'rmote.templates.filters.LengthFilter'>, 'count': <class 'rmote.templates.filters.LengthFilter'>, 'default': <class 'rmote.templates.filters.DefaultFilter'>, 'd': <class 'rmote.templates.filters.DefaultFilter'>, 'sort': <class 'rmote.templates.filters.SortFilter'>, 'unique': <class 'rmote.templates.filters.UniqueFilter'>, 'first': <class 'rmote.templates.filters.FirstFilter'>, 'last': <class 'rmote.templates.filters.LastFilter'>, 'reverse': <class 'rmote.templates.filters.ReverseFilter'>, 'list': <class 'rmote.templates.filters.ListFilter'>, 'int': <class 'rmote.templates.filters.IntFilter'>, 'float': <class 'rmote.templates.filters.FloatFilter'>, 'string': <class 'rmote.templates.filters.StringFilter'>, 'tojson': <class 'rmote.templates.filters.ToJSONFilter'>, 'ipaddress': <class 'rmote.templates.filters.IPAddressFilter'>}))[source]¶
Bases:
objectCompile a restricted template language to reusable intermediate nodes.
- Parameters:
template (
Text) – Jinja-like source with expressions, if/for/set and filters.filters (
Mapping[Text,type[TemplateFilter]]) – Complete name-to-class mapping; defaults to TEMPLATE_FILTERS.
Construction tokenizes and compiles the template once.
programexposes the resulting immutableTemplateProgram. Rendering only invokes that program with fresh variable bindings and filter instances. Context containers are checked without copying, and a container is checked when the template first reads its name, so a value the template never reads costs nothing. A whole frozen snapshot, passed as the only positional argument, is accepted as it is. Pickle transfers the program and filter classes; remote rendering does not reparse the source.There are no Python statements, arbitrary calls, or private attributes. Context values must be plain data. Registered filters and pickle inputs are trusted code; this language is not an OS or resource-isolation sandbox.
>>> Template("{{ names | unique | sort | join(', ') }}").render(names=["b", "a", "b"]) 'a, b' >>> Template("listen {{ port }}{% if ssl %} ssl{% endif %};").render(port=443, ssl=True) 'listen 443 ssl;'
- static compile(template, filters=(('capitalize', <class 'rmote.templates.filters.CapitalizeFilter'>), ('count', <class 'rmote.templates.filters.LengthFilter'>), ('d', <class 'rmote.templates.filters.DefaultFilter'>), ('default', <class 'rmote.templates.filters.DefaultFilter'>), ('first', <class 'rmote.templates.filters.FirstFilter'>), ('float', <class 'rmote.templates.filters.FloatFilter'>), ('int', <class 'rmote.templates.filters.IntFilter'>), ('ipaddress', <class 'rmote.templates.filters.IPAddressFilter'>), ('join', <class 'rmote.templates.filters.JoinFilter'>), ('last', <class 'rmote.templates.filters.LastFilter'>), ('length', <class 'rmote.templates.filters.LengthFilter'>), ('list', <class 'rmote.templates.filters.ListFilter'>), ('lower', <class 'rmote.templates.filters.LowerFilter'>), ('replace', <class 'rmote.templates.filters.ReplaceFilter'>), ('reverse', <class 'rmote.templates.filters.ReverseFilter'>), ('sort', <class 'rmote.templates.filters.SortFilter'>), ('string', <class 'rmote.templates.filters.StringFilter'>), ('tojson', <class 'rmote.templates.filters.ToJSONFilter'>), ('trim', <class 'rmote.templates.filters.TrimFilter'>), ('unique', <class 'rmote.templates.filters.UniqueFilter'>), ('upper', <class 'rmote.templates.filters.UpperFilter'>)))[source]¶
Cache compilation by source and filter classes, returning callable IR.
- Return type:
- classmethod from_program(program)[source]¶
Restore an already compiled program without lexing or parsing.
- Return type:
- render(context=None, /, **ctx)[source]¶
Invoke the compiled program using plain context data.
Keyword values are checked when the template first reads them. A mapping passed as the only positional argument is used as the whole context; a frozen snapshot of
rmote.immutable.freeze()is then accepted as it is, because it holds plain data by construction.- Return type:
- exception rmote.templates.engine.TemplateBreak[source]¶
Bases:
ExceptionInternal control-flow signal for a loop break.
- exception rmote.templates.engine.TemplateContinue[source]¶
Bases:
ExceptionInternal control-flow signal for a loop continue.
- class rmote.templates.engine.TemplateLexer(source)[source]¶
Bases:
objectSplit template text into typed tokens without interpreting expressions.
- code(start, opening, kind, closing)[source]¶
Tokenize one tag through its closing delimiter, retaining code tokens.
- class rmote.templates.engine.TemplateNode(position)[source]¶
Bases:
objectAn executable instruction in the immutable template program.
- class rmote.templates.engine.TemplateParser(source, tokens, filters)[source]¶
Bases:
objectCompile typed source tokens into nested executable instructions.
- TERMINATORS = frozenset({'end', 'endfor', 'endif'})¶
- class rmote.templates.engine.TemplateProgram(source, nodes, filters)[source]¶
Bases:
objectImmutable, pickleable IR. Invocation never parses or compiles source.
- __call__(context=None, /, **values)[source]¶
Render the program. A frozen snapshot passed whole is not re-checked.
- Return type:
- property active: tuple[tuple[str, type[TemplateFilter]], ...]¶
The filters this template names, found once for the program.
A render builds an instance of each of these and of no other. The whole mapping stays in
filtersfor transfer and for pickle, including a program pickled before this answer existed.
- nodes: tuple[TemplateNode, ...]¶
- source: TemplateSource¶
- class rmote.templates.engine.TemplateSource(text)[source]¶
Bases:
objectSource text and diagnostics at an exact character offset.
- exception rmote.templates.engine.TemplateSyntaxError[source]¶
Bases:
SyntaxErrorA syntax error already located in the original template source.
- class rmote.templates.engine.TemplateToken(value, position)[source]¶
Bases:
objectA lexical token retaining its source position and unmodified contents.
- class rmote.templates.engine.TextNode(position, text)[source]¶
Bases:
TemplateNode
- class rmote.templates.engine.TextToken(value, position)[source]¶
Bases:
TemplateTokenLiteral output, after processing source whitespace and escaped delimiters.
- class rmote.templates.engine.UnaryExpression(operator, value)[source]¶
Bases:
Expression- value: Expression¶
- class rmote.templates.engine.VariableExpression(name)[source]¶
Bases:
ResolvableExpression
- rmote.templates.engine.checked_filters(filters)[source]¶
Check filter names and classes once for one set of pairs.
The answer depends only on the pairs, so it is cached by them. A refused set raises instead of entering the cache, and is refused again next time.
- rmote.templates.engine.named_filters(nodes)[source]¶
Collect the filters a program names, by walking its nodes once.