from __future__ import absolute_import, print_function, unicode_literals
import re import json as json from .shared import JSONTemplateError, TemplateError, DeleteMarker, string, to_str from . import shared from .six import viewitems from .parser import Parser, Tokenizer from .interpreter import Interpreter import functools from inspect import isfunction, isbuiltin
def interpolate(string, context):
mo = _interpolation_start_re.search(string) ifnot mo: return string
result = []
whileTrue:
result.append(string[: mo.start()]) if mo.group() != "$${":
string = string[mo.end() :]
parsed, offset = parse_until_terminator(string, context, "}") if isinstance(parsed, (list, dict)): raise TemplateError( "interpolation of '{}' produced an array or object".format(
string[:offset]
)
) if parsed isNone:
result.append("") else:
result.append(to_str(parsed))
string = string[offset + 1 :] else: # found `$${`
result.append("${")
string = string[mo.end() :]
mo = _interpolation_start_re.search(string) ifnot mo:
result.append(string) break return"".join(result)
def checkUndefinedProperties(template, allowed):
unknownKeys = []
combined = "|".join(allowed) + "$"
unknownKeys = [key for key in sorted(template) ifnot re.match(combined, key)] if unknownKeys: raise TemplateError(
allowed[0].replace("\\", "")
+ " has undefined properties: "
+ " ".join(unknownKeys)
)
@operator("$eval") def eval(template, context):
checkUndefinedProperties(template, [r"\$eval"]) ifnot isinstance(template["$eval"], string): raise TemplateError("$eval must be given a string expression") return parse(template["$eval"], context)
@operator("$flatten") def flatten(template, context):
checkUndefinedProperties(template, [r"\$flatten"])
value = renderValue(template["$flatten"], context) ifnot isinstance(value, list): raise TemplateError("$flatten value must evaluate to an array")
def gen(): for e in value: if isinstance(e, list): for e2 in e: yield e2 else: yield e
return list(gen())
@operator("$flattenDeep") def flattenDeep(template, context):
checkUndefinedProperties(template, [r"\$flattenDeep"])
value = renderValue(template["$flattenDeep"], context) ifnot isinstance(value, list): raise TemplateError("$flattenDeep value must evaluate to an array")
def gen(value): if isinstance(value, list): for e in value: for sub in gen(e): yield sub else: yield value
@operator("$let") def let(template, context):
checkUndefinedProperties(template, [r"\$let", "in"]) ifnot isinstance(template["$let"], dict): raise TemplateError("$let value must be an object")
subcontext = context.copy()
initial_result = renderValue(template["$let"], context) ifnot isinstance(initial_result, dict): raise TemplateError("$let value must be an object") for k, v in initial_result.items(): ifnot IDENTIFIER_RE.match(k): raise TemplateError( "top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/"
) else:
subcontext[k] = v try:
in_expression = template["in"] except KeyError: raise TemplateError("$let operator requires an `in` clause") return renderValue(in_expression, subcontext)
@operator("$map") def map(template, context):
EACH_RE = r"each\([a-zA-Z_][a-zA-Z0-9_]*(,\s*([a-zA-Z_][a-zA-Z0-9_]*))?\)"
checkUndefinedProperties(template, [r"\$map", EACH_RE])
value = renderValue(template["$map"], context) ifnot isinstance(value, list) andnot isinstance(value, dict): raise TemplateError("$map value must evaluate to an array or object")
is_obj = isinstance(value, dict)
each_keys = [k for k in template if k.startswith("each(")] if len(each_keys) != 1: raise TemplateError("$map requires exactly one other property, each(..)")
each_key = each_keys[0]
each_args = [x.strip() for x in each_key[5:-1].split(",")]
each_var = each_args[0]
each_idx = each_args[1] if len(each_args) > 1elseNone
each_template = template[each_key]
def gen(val):
subcontext = context.copy() for i, elt in enumerate(val): if each_idx isNone:
subcontext[each_var] = elt else:
subcontext[each_var] = elt["val"] if is_obj else elt
subcontext[each_idx] = elt["key"] if is_obj else i
elt = renderValue(each_template, subcontext) if elt isnot DeleteMarker: yield elt
if is_obj:
value = [{"key": v[0], "val": v[1]} for v in value.items()]
v = dict() for e in gen(value): ifnot isinstance(e, dict): raise TemplateError( "$map on objects expects {0} to evaluate to an object".format(
each_key
)
)
v.update(e) return v else: return list(gen(value))
@operator("$reduce") def reduce(template, context):
EACH_RE = r"each\([a-zA-Z_][a-zA-Z0-9_]*,\s*[a-zA-Z_][a-zA-Z0-9_]*(,\s*([a-zA-Z_][a-zA-Z0-9_]*))?\)"
checkUndefinedProperties(template, [r"\$reduce", "initial", EACH_RE])
value = renderValue(template["$reduce"], context) ifnot isinstance(value, list): raise TemplateError("$reduce value must evaluate to an array")
if len(template) != 3: raise TemplateError("$reduce must have exactly three properties")
each_keys = [k for k in template if k.startswith("each(")] if len(each_keys) != 1: raise TemplateError("$reduce requires each(..)")
each_key = each_keys[0]
each_args = [x.strip() for x in each_key[5:-1].split(",")]
each_acc = each_args[0]
each_var = each_args[1]
each_idx = each_args[2] if len(each_args) > 2elseNone
subcontext = context.copy() for i, elt in enumerate(value): if each_idx isNone:
subcontext[each_acc] = resultValue
subcontext[each_var] = elt else:
subcontext[each_acc] = resultValue
subcontext[each_var] = elt
subcontext[each_idx] = i
r = renderValue(each_template, subcontext) if r isnot DeleteMarker:
resultValue = r
return resultValue
@operator("$find") def find(template, context):
EACH_RE = r"each\([a-zA-Z_][a-zA-Z0-9_]*(,\s*([a-zA-Z_][a-zA-Z0-9_]*))?\)"
checkUndefinedProperties(template, [r"\$find", EACH_RE])
value = renderValue(template["$find"], context) ifnot isinstance(value, list): raise TemplateError("$find value must evaluate to an array")
each_keys = [k for k in template if k.startswith("each(")] if len(each_keys) != 1: raise TemplateError("$find requires exactly one other property, each(..)")
each_key = each_keys[0]
each_args = [x.strip() for x in each_key[5:-1].split(",")]
each_var = each_args[0]
each_idx = each_args[1] if len(each_args) > 1elseNone
each_template = template[each_key]
ifnot isinstance(each_template, string): raise TemplateError("each can evaluate string expressions only")
subcontext = context.copy() for i, elt in enumerate(value): if each_idx isNone:
subcontext[each_var] = elt else:
subcontext[each_var] = elt
subcontext[each_idx] = i
if parse(each_template, subcontext): return renderValue(elt, subcontext)
ifnot isinstance(template["$match"], dict): raise TemplateError("$match can evaluate objects only")
result = [] for condition in sorted(template["$match"]): if parse(condition, context):
result.append(renderValue(template["$match"][condition], context))
ifnot isinstance(template["$switch"], dict): raise TemplateError("$switch can evaluate objects only")
result = [] for condition in template["$switch"]: ifnot condition == "$default"and parse(condition, context):
result.append(renderValue(template["$switch"][condition], context))
if len(result) > 1: raise TemplateError("$switch can only have one truthy condition")
if len(result) == 0: if"$default"in template["$switch"]:
result.append(renderValue(template["$switch"]["$default"], context))
return result[0] if len(result) > 0else DeleteMarker
@operator("$merge") def merge(template, context):
checkUndefinedProperties(template, [r"\$merge"])
value = renderValue(template["$merge"], context) ifnot isinstance(value, list) ornot all(isinstance(e, dict) for e in value): raise TemplateError("$merge value must evaluate to an array of objects")
v = dict() for e in value:
v.update(e) return v
@operator("$mergeDeep") def merge(template, context):
checkUndefinedProperties(template, [r"\$mergeDeep"])
value = renderValue(template["$mergeDeep"], context) ifnot isinstance(value, list) ornot all(isinstance(e, dict) for e in value): raise TemplateError("$mergeDeep value must evaluate to an array of objects")
def merge(l, r): if isinstance(l, list) and isinstance(r, list): return l + r if isinstance(l, dict) and isinstance(r, dict):
res = l.copy() for k, v in viewitems(r): if k in l:
res[k] = merge(l[k], v) else:
res[k] = v return res return r
if len(value) == 0: return {} return functools.reduce(merge, value[1:], value[0])
@operator("$reverse") def reverse(template, context):
checkUndefinedProperties(template, [r"\$reverse"])
value = renderValue(template["$reverse"], context) ifnot isinstance(value, list): raise TemplateError("$reverse value must evaluate to an array of objects") return list(reversed(value))
@operator("$sort") def sort(template, context):
BY_RE = r"by\([a-zA-Z_][a-zA-Z0-9_]*\)"
checkUndefinedProperties(template, [r"\$sort", BY_RE])
value = renderValue(template["$sort"], context) ifnot isinstance(value, list): raise TemplateError("$sorted values to be sorted must have the same type")
# handle by(..) if given, producing a list of keys to be sorted
by_keys = [k for k in template if k.startswith("by(")] if len(by_keys) == 1:
by_key = by_keys[0]
by_var = by_key[3:-1]
by_expr = template[by_key]
def xform():
subcontext = context.copy() for e in value:
subcontext[by_var] = e yield parse(by_expr, subcontext)
sort_keys = list(xform()) elif len(by_keys) == 0:
sort_keys = value else: raise TemplateError("only one by(..) is allowed")
# check types of the values to be sorted all match try:
eltype = type(sort_keys[0]) except IndexError: return [] if eltype in (list, dict, bool, type(None)): raise TemplateError("$sorted values to be sorted must have the same type") ifnot all(isinstance(e, eltype) for e in sort_keys): raise TemplateError("$sorted values to be sorted must have the same type")
# If not using `by(..)`, just sort the values. if sort_keys is value: return sorted(value)
# Otherwise, index into the sort_keys array for each element. return list(
pair[1] for pair in sorted(enumerate(value), key=lambda pair: sort_keys[pair[0]])
)
def containsFunctions(rendered): if hasattr(rendered, "__call__"): returnTrue elif isinstance(rendered, list): for e in rendered: if containsFunctions(e): returnTrue returnFalse elif isinstance(rendered, dict): for k, v in viewitems(rendered): if containsFunctions(v): returnTrue returnFalse else: returnFalse
def renderValue(template, context): if isinstance(template, string): return interpolate(template, context)
elif isinstance(template, dict):
matches = [k for k in template if k in operators] if matches: if len(matches) > 1: raise TemplateError("only one operator allowed") return operators[matches[0]](template, context)
def updated(): for k, v in viewitems(template): if k.startswith("$$"):
k = k[1:] elif k.startswith("$") and IDENTIFIER_RE.match(k[1:]): raise TemplateError("$<identifier> is reserved; use $$<identifier>") else:
k = interpolate(k, context)
try:
v = renderValue(v, context) except JSONTemplateError as e: if IDENTIFIER_RE.match(k):
e.add_location(".{}".format(k)) else:
e.add_location("[{}]".format(json.dumps(k))) raise if v isnot DeleteMarker: yield k, v
return dict(updated())
elif isinstance(template, list):
def updated(): for i, e in enumerate(template): try:
v = renderValue(e, context) if v isnot DeleteMarker: yield v except JSONTemplateError as e:
e.add_location("[{}]".format(i)) raise
return list(updated())
else: return template
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.