Source code for wheezy.template.ext.determined

import re
import typing

from wheezy.template.comp import Tuple
from wheezy.template.utils import find_balanced

RE_ARGS = re.compile(r'\s*(?P<expr>(([\'"]).*?\3|.+?))\s*\,')
RE_KWARGS = re.compile(
    r'\s*(?P<name>\w+)\s*=\s*(?P<expr>([\'"].*?[\'"]|.+?))\s*\,'
)
RE_STR_VALUE = re.compile(r'^[\'"](?P<value>.+)[\'"]$')
RE_INT_VALUE = re.compile(r"^(?P<value>(\d+))$")


# region: core extension


[docs]class DeterminedExtension(object): """Tranlates function calls between template engines. Strictly determined known calls are converted to preprocessor calls, e.g.:: @_('Name:') @path_for('default') @path_for('static', path='/static/css/site.css') Those that are not strictly determined are ignored and processed by runtime engine. """ def __init__( self, known_calls: typing.List[str], runtime_token_start: str = "@", token_start: str = "#", ) -> None: self.token_start = token_start self.pattern = re.compile( r"%s(%s)(?=\()" % (runtime_token_start, "|".join(known_calls)) ) self.preprocessors = [self.preprocess] def preprocess(self, source: str) -> str: result = [] start = 0 for m in self.pattern.finditer(source): pstart = m.end() pend = find_balanced(source, pstart) if determined(source[pstart + 1 : pend - 1]): name = m.group(1) result.append(source[start : m.start()]) result.append(self.token_start + "ctx['" + name + "']") start = pstart if start: result.append(source[start:]) return "".join(result) else: return source
[docs]def determined(expression: str) -> bool: """Checks if expresion is strictly determined. >>> determined("'default'") True >>> determined('name') False >>> determined("'default', id=id") False >>> determined("'default', lang=100") True >>> determined('') True """ args, kwargs = parse_params(expression) for arg in args: if not str_or_int(arg): return False for arg in kwargs.values(): if not str_or_int(arg): return False return True
[docs]def parse_kwargs(text: str) -> typing.Mapping[str, str]: """Parses key-value type of parameters. >>> parse_kwargs('id=item.id') {'id': 'item.id'} >>> sorted(parse_kwargs('lang="en", id=12').items()) [('id', '12'), ('lang', '"en"')] """ kwargs = {} for m in RE_KWARGS.finditer(text + ","): groups = m.groupdict() kwargs[groups["name"].rstrip("_")] = groups["expr"] return kwargs
[docs]def parse_args(text: str) -> typing.List[str]: """Parses argument type of parameters. >>> parse_args('') [] >>> parse_args('10, "x"') ['10', '"x"'] >>> parse_args("'x', 100") ["'x'", '100'] >>> parse_args('"default"') ['"default"'] """ args = [] for m in RE_ARGS.finditer(text + ","): args.append(m.group("expr")) return args
[docs]def parse_params( text: str, ) -> Tuple[typing.List[str], typing.Mapping[str, str]]: """Parses function parameters. >>> parse_params('') ([], {}) >>> parse_params('id=item.id') ([], {'id': 'item.id'}) >>> parse_params('"default"') (['"default"'], {}) >>> parse_params('"default", lang="en"') (['"default"'], {'lang': '"en"'}) """ if "=" in text: args = text.split("=")[0] if "," in args: args = args.rsplit(",", 1)[0] kwargs = text[len(args) :] return parse_args(args), parse_kwargs(kwargs) else: return [], parse_kwargs(text) else: return parse_args(text), {}
[docs]def str_or_int(text: str) -> bool: """Ensures ``text`` as string or int expression. >>> str_or_int('"default"') True >>> str_or_int("'Hello'") True >>> str_or_int('100') True >>> str_or_int('item.id') False """ m = RE_STR_VALUE.match(text) if m: return True else: m = RE_INT_VALUE.match(text) if m: return True return False