зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1412136 - Upgrade third_party/python/jsone to version 2.3.1, r=dustin
This patch was generated by: $ hg rm third_party/python/jsone/* $ cd third_party/python/jsone $ pip wheel jsone $ unzip <downloaded wheel> MozReview-Commit-ID: BdjFByECFBT --HG-- extra : rebase_source : 4e82bd4ff8cad57579c4d3a5a1547878445dfcab
This commit is contained in:
Родитель
21b5b6db95
Коммит
795e68f9ef
|
@ -1,2 +0,0 @@
|
|||
include jsone *.py
|
||||
recursive-exclude test *
|
|
@ -1,10 +0,0 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: json-e
|
||||
Version: 2.1.1
|
||||
Summary: A data-structure parameterization system written for embedding context in JSON objects
|
||||
Home-page: https://taskcluster.github.io/json-e/
|
||||
Author: Dustin J. Mitchell
|
||||
Author-email: dustin@mozilla.com
|
||||
License: MPL2
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
|
@ -1,18 +1,20 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import datetime
|
||||
import re
|
||||
from .render import renderValue
|
||||
from .shared import JSONTemplateError, DeleteMarker
|
||||
from .builtins import builtins
|
||||
from .shared import JSONTemplateError, DeleteMarker, TemplateError
|
||||
from . import builtins
|
||||
|
||||
_context_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*$')
|
||||
|
||||
|
||||
def render(template, context):
|
||||
if not all(_context_re.match(c) for c in context):
|
||||
raise JSONTemplateError('top level keys of context must follow '
|
||||
raise TemplateError('top level keys of context must follow '
|
||||
'/[a-zA-Z_][a-zA-Z0-9_]*/')
|
||||
full_context = builtins.copy()
|
||||
full_context = {'now': datetime.datetime.utcnow()}
|
||||
full_context.update(builtins.build(full_context))
|
||||
full_context.update(context)
|
||||
rv = renderValue(template, full_context)
|
||||
if rv is DeleteMarker:
|
||||
|
|
|
@ -1,128 +1,116 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import math
|
||||
from .interpreter import ExpressionError
|
||||
from .shared import string, fromNow
|
||||
|
||||
builtins = {}
|
||||
from .shared import string, to_str, fromNow, JSONTemplateError
|
||||
|
||||
|
||||
def builtin(name, variadic=None, argument_tests=None, minArgs=None):
|
||||
def wrap(fn):
|
||||
def bad(reason=None):
|
||||
raise ExpressionError((reason or 'invalid arguments to {}').format(name))
|
||||
if variadic:
|
||||
def invoke(args):
|
||||
if minArgs:
|
||||
if len(args) < minArgs:
|
||||
bad("too few arguments to {}")
|
||||
for arg in args:
|
||||
if not variadic(arg):
|
||||
class BuiltinError(JSONTemplateError):
|
||||
pass
|
||||
|
||||
|
||||
def build(context):
|
||||
builtins = {}
|
||||
|
||||
def builtin(name, variadic=None, argument_tests=None, minArgs=None):
|
||||
def wrap(fn):
|
||||
def bad(reason=None):
|
||||
raise BuiltinError((reason or 'invalid arguments to {}').format(name))
|
||||
if variadic:
|
||||
def invoke(*args):
|
||||
if minArgs:
|
||||
if len(args) < minArgs:
|
||||
bad("too few arguments to {}")
|
||||
for arg in args:
|
||||
if not variadic(arg):
|
||||
bad()
|
||||
return fn(*args)
|
||||
|
||||
elif argument_tests:
|
||||
def invoke(*args):
|
||||
if len(args) != len(argument_tests):
|
||||
bad()
|
||||
return fn(*args)
|
||||
for t, arg in zip(argument_tests, args):
|
||||
if not t(arg):
|
||||
bad()
|
||||
return fn(*args)
|
||||
|
||||
elif argument_tests:
|
||||
def invoke(args):
|
||||
if len(args) != len(argument_tests):
|
||||
bad()
|
||||
for t, arg in zip(argument_tests, args):
|
||||
if not t(arg):
|
||||
bad()
|
||||
return fn(*args)
|
||||
else:
|
||||
def invoke(*args):
|
||||
return fn(*args)
|
||||
|
||||
else:
|
||||
def invoke(args):
|
||||
return fn(*args)
|
||||
builtins[name] = invoke
|
||||
return fn
|
||||
return wrap
|
||||
|
||||
builtins[name] = invoke
|
||||
return fn
|
||||
return wrap
|
||||
def is_number(v):
|
||||
return isinstance(v, (int, float)) and not isinstance(v, bool)
|
||||
|
||||
def is_string(v):
|
||||
return isinstance(v, string)
|
||||
|
||||
def is_number(v):
|
||||
return isinstance(v, (int, float)) and not isinstance(v, bool)
|
||||
def is_string_or_array(v):
|
||||
return isinstance(v, (string, list))
|
||||
|
||||
def anything(v):
|
||||
return isinstance(v, (string, int, float, list, dict)) or v is None or callable(v)
|
||||
|
||||
def is_string(v):
|
||||
return isinstance(v, string)
|
||||
# ---
|
||||
|
||||
builtin('min', variadic=is_number, minArgs=1)(min)
|
||||
builtin('max', variadic=is_number, minArgs=1)(max)
|
||||
builtin('sqrt', argument_tests=[is_number])(math.sqrt)
|
||||
builtin('abs', argument_tests=[is_number])(abs)
|
||||
|
||||
def is_string_or_array(v):
|
||||
return isinstance(v, (string, list))
|
||||
@builtin('ceil', argument_tests=[is_number])
|
||||
def ceil(v):
|
||||
return int(math.ceil(v))
|
||||
|
||||
@builtin('floor', argument_tests=[is_number])
|
||||
def floor(v):
|
||||
return int(math.floor(v))
|
||||
|
||||
def anything(v):
|
||||
return isinstance(v, (string, int, float, list, dict)) or v is None or callable(v)
|
||||
@builtin('lowercase', argument_tests=[is_string])
|
||||
def lowercase(v):
|
||||
return v.lower()
|
||||
|
||||
# ---
|
||||
@builtin('uppercase', argument_tests=[is_string])
|
||||
def lowercase(v):
|
||||
return v.upper()
|
||||
|
||||
builtin('len', argument_tests=[is_string_or_array])(len)
|
||||
builtin('str', argument_tests=[anything])(to_str)
|
||||
|
||||
builtin('min', variadic=is_number, minArgs=1)(min)
|
||||
builtin('max', variadic=is_number, minArgs=1)(max)
|
||||
builtin('sqrt', argument_tests=[is_number])(math.sqrt)
|
||||
builtin('abs', argument_tests=[is_number])(abs)
|
||||
@builtin('strip', argument_tests=[is_string])
|
||||
def strip(s):
|
||||
return s.strip()
|
||||
|
||||
@builtin('rstrip', argument_tests=[is_string])
|
||||
def rstrip(s):
|
||||
return s.rstrip()
|
||||
|
||||
@builtin('ceil', argument_tests=[is_number])
|
||||
def ceil(v):
|
||||
return int(math.ceil(v))
|
||||
@builtin('lstrip', argument_tests=[is_string])
|
||||
def lstrip(s):
|
||||
return s.lstrip()
|
||||
|
||||
@builtin('fromNow', variadic=is_string, minArgs=1)
|
||||
def fromNow_builtin(offset, reference=None):
|
||||
return fromNow(offset, reference or context.get('now'))
|
||||
|
||||
@builtin('floor', argument_tests=[is_number])
|
||||
def floor(v):
|
||||
return int(math.floor(v))
|
||||
@builtin('typeof', argument_tests=[anything])
|
||||
def typeof(v):
|
||||
if isinstance(v, bool):
|
||||
return 'boolean'
|
||||
elif isinstance(v, string):
|
||||
return 'string'
|
||||
elif isinstance(v, (int, float)):
|
||||
return 'number'
|
||||
elif isinstance(v, list):
|
||||
return 'array'
|
||||
elif isinstance(v, dict):
|
||||
return 'object'
|
||||
elif v is None:
|
||||
return None
|
||||
elif callable(v):
|
||||
return 'function'
|
||||
|
||||
|
||||
@builtin('lowercase', argument_tests=[is_string])
|
||||
def lowercase(v):
|
||||
return v.lower()
|
||||
|
||||
|
||||
@builtin('uppercase', argument_tests=[is_string])
|
||||
def lowercase(v):
|
||||
return v.upper()
|
||||
|
||||
|
||||
builtin('len', argument_tests=[is_string_or_array])(len)
|
||||
|
||||
|
||||
@builtin('str', argument_tests=[anything])
|
||||
def to_str(v):
|
||||
if isinstance(v, bool):
|
||||
return {True: 'true', False: 'false'}[v]
|
||||
elif isinstance(v, list):
|
||||
return ','.join(to_str(e) for e in v)
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
|
||||
@builtin('str', argument_tests=[anything])
|
||||
def to_str(v):
|
||||
if isinstance(v, bool):
|
||||
return {True: 'true', False: 'false'}[v]
|
||||
elif isinstance(v, list):
|
||||
return ','.join(to_str(e) for e in v)
|
||||
elif v is None:
|
||||
return 'null'
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
|
||||
builtin('fromNow', argument_tests=[is_string])(fromNow)
|
||||
|
||||
@builtin('typeof', argument_tests=[anything])
|
||||
def typeof(v):
|
||||
if isinstance(v, bool):
|
||||
return 'boolean'
|
||||
elif isinstance(v, string):
|
||||
return 'string'
|
||||
elif isinstance(v, (int, float)):
|
||||
return 'number'
|
||||
elif isinstance(v, list):
|
||||
return 'array'
|
||||
elif isinstance(v, dict):
|
||||
return 'object'
|
||||
elif v is None:
|
||||
return None
|
||||
elif callable(v):
|
||||
return 'function'
|
||||
return builtins
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
from .prattparser import PrattParser, infix, prefix
|
||||
from .shared import JSONTemplateError, string
|
||||
from .shared import TemplateError, string
|
||||
import operator
|
||||
import json
|
||||
|
||||
|
@ -21,11 +21,8 @@ OPERATORS = {
|
|||
}
|
||||
|
||||
|
||||
class ExpressionError(JSONTemplateError):
|
||||
|
||||
@classmethod
|
||||
def expectation(cls, operator, expected):
|
||||
return cls('{} expected {}'.format(operator, expected))
|
||||
def expectationError(operator, expected):
|
||||
return TemplateError('{} expected {}'.format(operator, expected))
|
||||
|
||||
|
||||
class ExpressionEvaluator(PrattParser):
|
||||
|
@ -67,7 +64,7 @@ class ExpressionEvaluator(PrattParser):
|
|||
|
||||
def parse(self, expression):
|
||||
if not isinstance(expression, string):
|
||||
raise ExpressionError('expression to be evaluated must be a string')
|
||||
raise TemplateError('expression to be evaluated must be a string')
|
||||
return super(ExpressionEvaluator, self).parse(expression)
|
||||
|
||||
@prefix('number')
|
||||
|
@ -83,14 +80,14 @@ class ExpressionEvaluator(PrattParser):
|
|||
def uminus(self, token, pc):
|
||||
v = pc.parse('unary')
|
||||
if not isNumber(v):
|
||||
raise ExpressionError.expectation('unary -', 'number')
|
||||
raise expectationError('unary -', 'number')
|
||||
return -v
|
||||
|
||||
@prefix("+")
|
||||
def uplus(self, token, pc):
|
||||
v = pc.parse('unary')
|
||||
if not isNumber(v):
|
||||
raise ExpressionError.expectation('unary +', 'number')
|
||||
raise expectationError('unary +', 'number')
|
||||
return v
|
||||
|
||||
@prefix("identifier")
|
||||
|
@ -98,7 +95,7 @@ class ExpressionEvaluator(PrattParser):
|
|||
try:
|
||||
return self.context[token.value]
|
||||
except KeyError:
|
||||
raise ExpressionError('no context value named "{}"'.format(token.value))
|
||||
raise TemplateError('no context value named "{}"'.format(token.value))
|
||||
|
||||
@prefix("null")
|
||||
def null(self, token, pc):
|
||||
|
@ -133,23 +130,23 @@ class ExpressionEvaluator(PrattParser):
|
|||
@infix("+")
|
||||
def plus(self, left, token, pc):
|
||||
if not isinstance(left, (string, int, float)) or isinstance(left, bool):
|
||||
raise ExpressionError.expectation('+', 'number or string')
|
||||
raise expectationError('+', 'number or string')
|
||||
right = pc.parse(token.kind)
|
||||
if not isinstance(right, (string, int, float)) or isinstance(right, bool):
|
||||
raise ExpressionError.expectation('+', 'number or string')
|
||||
raise expectationError('+', 'number or string')
|
||||
if type(right) != type(left) and \
|
||||
(isinstance(left, string) or isinstance(right, string)):
|
||||
raise ExpressionError.expectation('+', 'matching types')
|
||||
raise expectationError('+', 'matching types')
|
||||
return left + right
|
||||
|
||||
@infix('-', '*', '/', '**')
|
||||
def arith(self, left, token, pc):
|
||||
op = token.kind
|
||||
if not isNumber(left):
|
||||
raise ExpressionError.expectation(op, 'number')
|
||||
raise expectationError(op, 'number')
|
||||
right = pc.parse({'**': '**-right-associative'}.get(op))
|
||||
if not isNumber(right):
|
||||
raise ExpressionError.expectation(op, 'number')
|
||||
raise expectationError(op, 'number')
|
||||
return OPERATORS[op](left, right)
|
||||
|
||||
@infix("[")
|
||||
|
@ -177,19 +174,19 @@ class ExpressionEvaluator(PrattParser):
|
|||
@infix(".")
|
||||
def property_dot(self, left, token, pc):
|
||||
if not isinstance(left, dict):
|
||||
raise ExpressionError.expectation('.', 'object')
|
||||
raise expectationError('.', 'object')
|
||||
k = pc.require('identifier').value
|
||||
try:
|
||||
return left[k]
|
||||
except KeyError:
|
||||
raise ExpressionError('{} not found in {}'.format(k, json.dumps(left)))
|
||||
raise TemplateError('{} not found in {}'.format(k, json.dumps(left)))
|
||||
|
||||
@infix("(")
|
||||
def function_call(self, left, token, pc):
|
||||
if not callable(left):
|
||||
raise ExpressionError('function call', 'callable')
|
||||
raise TemplateError('function call', 'callable')
|
||||
args = parseList(pc, ',', ')')
|
||||
return left(args)
|
||||
return left(*args)
|
||||
|
||||
@infix('==', '!=', '||', '&&')
|
||||
def equality_and_logic(self, left, token, pc):
|
||||
|
@ -203,7 +200,7 @@ class ExpressionEvaluator(PrattParser):
|
|||
right = pc.parse(op)
|
||||
if type(left) != type(right) or \
|
||||
not (isinstance(left, (int, float, string)) and not isinstance(left, bool)):
|
||||
raise ExpressionError.expectation(op, 'matching types')
|
||||
raise expectationError(op, 'matching types')
|
||||
return OPERATORS[op](left, right)
|
||||
|
||||
@infix("in")
|
||||
|
@ -211,16 +208,16 @@ class ExpressionEvaluator(PrattParser):
|
|||
right = pc.parse(token.kind)
|
||||
if isinstance(right, dict):
|
||||
if not isinstance(left, string):
|
||||
raise ExpressionError.expectation('in-object', 'string on left side')
|
||||
raise expectationError('in-object', 'string on left side')
|
||||
elif isinstance(right, string):
|
||||
if not isinstance(left, string):
|
||||
raise ExpressionError.expectation('in-string', 'string on left side')
|
||||
raise expectationError('in-string', 'string on left side')
|
||||
elif not isinstance(right, list):
|
||||
raise ExpressionError.expectation('in', 'Array, string, or object on right side')
|
||||
raise expectationError('in', 'Array, string, or object on right side')
|
||||
try:
|
||||
return left in right
|
||||
except TypeError:
|
||||
raise ExpressionError.expectation('in', 'scalar value, collection')
|
||||
raise expectationError('in', 'scalar value, collection')
|
||||
|
||||
|
||||
def isNumber(v):
|
||||
|
@ -268,22 +265,22 @@ def accessProperty(value, a, b, is_interval):
|
|||
try:
|
||||
return value[a:b]
|
||||
except TypeError:
|
||||
raise ExpressionError.expectation('[..]', 'integer')
|
||||
raise expectationError('[..]', 'integer')
|
||||
else:
|
||||
try:
|
||||
return value[a]
|
||||
except IndexError:
|
||||
raise ExpressionError('index out of bounds')
|
||||
raise TemplateError('index out of bounds')
|
||||
except TypeError:
|
||||
raise ExpressionError.expectation('[..]', 'integer')
|
||||
raise expectationError('[..]', 'integer')
|
||||
|
||||
if not isinstance(value, dict):
|
||||
raise ExpressionError.expectation('[..]', 'object, array, or string')
|
||||
raise expectationError('[..]', 'object, array, or string')
|
||||
if not isinstance(a, string):
|
||||
raise ExpressionError.expectation('[..]', 'string index')
|
||||
raise expectationError('[..]', 'string index')
|
||||
|
||||
try:
|
||||
return value[a]
|
||||
except KeyError:
|
||||
return None
|
||||
#raise ExpressionError('{} not found in {}'.format(a, json.dumps(value)))
|
||||
#raise TemplateError('{} not found in {}'.format(a, json.dumps(value)))
|
||||
|
|
|
@ -2,11 +2,11 @@ from __future__ import absolute_import, print_function, unicode_literals
|
|||
|
||||
import re
|
||||
from collections import namedtuple
|
||||
from .shared import JSONTemplateError
|
||||
from .shared import TemplateError
|
||||
from .six import with_metaclass, viewitems
|
||||
|
||||
|
||||
class SyntaxError(JSONTemplateError):
|
||||
class SyntaxError(TemplateError):
|
||||
|
||||
@classmethod
|
||||
def unexpected(cls, got, exp):
|
||||
|
|
|
@ -2,11 +2,11 @@ from __future__ import absolute_import, print_function, unicode_literals
|
|||
|
||||
import re
|
||||
import json as json
|
||||
from .shared import JSONTemplateError, DeleteMarker, string
|
||||
from .shared import JSONTemplateError, TemplateError, DeleteMarker, string, to_str
|
||||
from . import shared
|
||||
from . import builtins
|
||||
from .interpreter import ExpressionEvaluator
|
||||
from .six import viewitems
|
||||
import functools
|
||||
|
||||
operators = {}
|
||||
|
||||
|
@ -38,8 +38,9 @@ def interpolate(string, context):
|
|||
string = string[mo.end():]
|
||||
parsed, offset = evaluator.parseUntilTerminator(string, '}')
|
||||
if isinstance(parsed, (list, dict)):
|
||||
raise JSONTemplateError('cannot interpolate array/object: ' + string)
|
||||
result.append(builtins.to_str(parsed))
|
||||
raise TemplateError(
|
||||
"interpolation of '{}' produced an array or object".format(string[:offset]))
|
||||
result.append(to_str(parsed))
|
||||
string = string[offset + 1:]
|
||||
else: # found `$${`
|
||||
result.append('${')
|
||||
|
@ -62,7 +63,7 @@ def eval(template, context):
|
|||
def flatten(template, context):
|
||||
value = renderValue(template['$flatten'], context)
|
||||
if not isinstance(value, list):
|
||||
raise JSONTemplateError('$flatten value must evaluate to an array of arrays')
|
||||
raise TemplateError('$flatten value must evaluate to an array')
|
||||
|
||||
def gen():
|
||||
for e in value:
|
||||
|
@ -78,7 +79,7 @@ def flatten(template, context):
|
|||
def flattenDeep(template, context):
|
||||
value = renderValue(template['$flattenDeep'], context)
|
||||
if not isinstance(value, list):
|
||||
raise JSONTemplateError('$flatten value must evaluate to an array')
|
||||
raise TemplateError('$flattenDeep value must evaluate to an array')
|
||||
|
||||
def gen(value):
|
||||
if isinstance(value, list):
|
||||
|
@ -94,9 +95,11 @@ def flattenDeep(template, context):
|
|||
@operator('$fromNow')
|
||||
def fromNow(template, context):
|
||||
offset = renderValue(template['$fromNow'], context)
|
||||
reference = renderValue(template['from'], context) if 'from' in template else context.get('now')
|
||||
|
||||
if not isinstance(offset, string):
|
||||
raise JSONTemplateError("$fromnow expects a string")
|
||||
return shared.fromNow(offset)
|
||||
raise TemplateError("$fromNow expects a string")
|
||||
return shared.fromNow(offset, reference)
|
||||
|
||||
|
||||
@operator('$if')
|
||||
|
@ -122,13 +125,13 @@ def jsonConstruct(template, context):
|
|||
def let(template, context):
|
||||
variables = renderValue(template['$let'], context)
|
||||
if not isinstance(variables, dict):
|
||||
raise JSONTemplateError("$let value must evaluate to an object")
|
||||
raise TemplateError("$let value must evaluate to an object")
|
||||
subcontext = context.copy()
|
||||
subcontext.update(variables)
|
||||
try:
|
||||
in_expression = template['in']
|
||||
except KeyError:
|
||||
raise JSONTemplateError("$let operator requires an `in` clause")
|
||||
raise TemplateError("$let operator requires an `in` clause")
|
||||
return renderValue(in_expression, subcontext)
|
||||
|
||||
|
||||
|
@ -136,13 +139,13 @@ def let(template, context):
|
|||
def map(template, context):
|
||||
value = renderValue(template['$map'], context)
|
||||
if not isinstance(value, list) and not isinstance(value, dict):
|
||||
raise JSONTemplateError("$map value must evaluate to an array or object")
|
||||
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 JSONTemplateError("$map requires exactly one other property, each(..)")
|
||||
raise TemplateError("$map requires exactly one other property, each(..)")
|
||||
each_key = each_keys[0]
|
||||
each_var = each_key[5:-1]
|
||||
each_template = template[each_key]
|
||||
|
@ -171,18 +174,40 @@ def map(template, context):
|
|||
def merge(template, context):
|
||||
value = renderValue(template['$merge'], context)
|
||||
if not isinstance(value, list) or not all(isinstance(e, dict) for e in value):
|
||||
raise JSONTemplateError("$reverse value must evaluate to an array of objects")
|
||||
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):
|
||||
value = renderValue(template['$mergeDeep'], context)
|
||||
if not isinstance(value, list) or not 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):
|
||||
value = renderValue(template['$reverse'], context)
|
||||
if not isinstance(value, list):
|
||||
raise JSONTemplateError("$reverse value must evaluate to an array")
|
||||
raise TemplateError("$reverse value must evaluate to an array")
|
||||
return list(reversed(value))
|
||||
|
||||
|
||||
|
@ -190,7 +215,7 @@ def reverse(template, context):
|
|||
def sort(template, context):
|
||||
value = renderValue(template['$sort'], context)
|
||||
if not isinstance(value, list):
|
||||
raise JSONTemplateError("$sort value must evaluate to an array")
|
||||
raise TemplateError("$sort value must evaluate to an array")
|
||||
|
||||
# handle by(..) if given, applying the schwartzian transform
|
||||
by_keys = [k for k in template if k.startswith('by(')]
|
||||
|
@ -208,7 +233,7 @@ def sort(template, context):
|
|||
elif len(by_keys) == 0:
|
||||
to_sort = [(e, e) for e in value]
|
||||
else:
|
||||
raise JSONTemplateError('only one by(..) is allowed')
|
||||
raise TemplateError('only one by(..) is allowed')
|
||||
|
||||
# check types
|
||||
try:
|
||||
|
@ -216,9 +241,9 @@ def sort(template, context):
|
|||
except IndexError:
|
||||
return []
|
||||
if eltype in (list, dict, bool, type(None)):
|
||||
raise JSONTemplateError('$sort values must be sortable')
|
||||
raise TemplateError('$sort values must be sortable')
|
||||
if not all(isinstance(e[0], eltype) for e in to_sort):
|
||||
raise JSONTemplateError('$sorted values must all have the same type')
|
||||
raise TemplateError('$sorted values must all have the same type')
|
||||
|
||||
# unzip the schwartzian transform
|
||||
return list(e[1] for e in sorted(to_sort))
|
||||
|
@ -232,7 +257,7 @@ def renderValue(template, context):
|
|||
matches = [k for k in template if k in operators]
|
||||
if matches:
|
||||
if len(matches) > 1:
|
||||
raise JSONTemplateError("only one operator allowed")
|
||||
raise TemplateError("only one operator allowed")
|
||||
return operators[matches[0]](template, context)
|
||||
|
||||
def updated():
|
||||
|
@ -241,14 +266,29 @@ def renderValue(template, context):
|
|||
k = k[1:]
|
||||
else:
|
||||
k = interpolate(k, context)
|
||||
v = renderValue(v, context)
|
||||
try:
|
||||
v = renderValue(v, context)
|
||||
except JSONTemplateError as e:
|
||||
if re.match('^[a-zA-Z][a-zA-Z0-9]*$', k):
|
||||
e.add_location('.{}'.format(k))
|
||||
else:
|
||||
e.add_location('[{}]'.format(json.dumps(k)))
|
||||
raise
|
||||
if v is not DeleteMarker:
|
||||
yield k, v
|
||||
return dict(updated())
|
||||
|
||||
elif isinstance(template, list):
|
||||
rendered = (renderValue(e, context) for e in template)
|
||||
return [e for e in rendered if e is not DeleteMarker]
|
||||
def updated():
|
||||
for i, e in enumerate(template):
|
||||
try:
|
||||
v = renderValue(e, context)
|
||||
if v is not DeleteMarker:
|
||||
yield v
|
||||
except JSONTemplateError as e:
|
||||
e.add_location('[{}]'.format(i))
|
||||
raise
|
||||
return list(updated())
|
||||
|
||||
else:
|
||||
return template
|
||||
|
|
|
@ -3,15 +3,28 @@ from __future__ import absolute_import, print_function, unicode_literals
|
|||
import re
|
||||
import datetime
|
||||
|
||||
# this will be overridden in tests
|
||||
utcnow = datetime.datetime.utcnow
|
||||
|
||||
|
||||
class DeleteMarker:
|
||||
pass
|
||||
|
||||
|
||||
class JSONTemplateError(Exception):
|
||||
def __init__(self, message):
|
||||
super(JSONTemplateError, self).__init__(message)
|
||||
self.location = []
|
||||
|
||||
def add_location(self, loc):
|
||||
self.location.insert(0, loc)
|
||||
|
||||
def __str__(self):
|
||||
location = ' at template' + ''.join(self.location)
|
||||
return "{}{}: {}".format(
|
||||
self.__class__.__name__,
|
||||
location if self.location else '',
|
||||
self.args[0])
|
||||
|
||||
|
||||
class TemplateError(JSONTemplateError):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -28,7 +41,7 @@ FROMNOW_RE = re.compile(''.join([
|
|||
]))
|
||||
|
||||
|
||||
def fromNow(offset):
|
||||
def fromNow(offset, reference):
|
||||
# copied from taskcluster-client.py
|
||||
# We want to handle past dates as well as future
|
||||
future = True
|
||||
|
@ -72,12 +85,27 @@ def fromNow(offset):
|
|||
seconds=seconds,
|
||||
)
|
||||
|
||||
return stringDate(utcnow() + delta if future else utcnow() - delta)
|
||||
if isinstance(reference, str):
|
||||
reference = datetime.datetime.strptime(reference, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
elif reference is None:
|
||||
reference = datetime.datetime.utcnow()
|
||||
return stringDate(reference + delta if future else reference - delta)
|
||||
|
||||
|
||||
datefmt_re = re.compile(r'(\.[0-9]{3})[0-9]*(\+00:00)?')
|
||||
|
||||
|
||||
def to_str(v):
|
||||
if isinstance(v, bool):
|
||||
return {True: 'true', False: 'false'}[v]
|
||||
elif isinstance(v, list):
|
||||
return ','.join(to_str(e) for e in v)
|
||||
elif v is None:
|
||||
return 'null'
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
|
||||
def stringDate(date):
|
||||
# Convert to isoFormat
|
||||
string = date.isoformat()
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import json
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
package_json = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'package.json')
|
||||
with open(package_json) as f:
|
||||
version = json.load(f)['version']
|
||||
|
||||
setup(name='json-e',
|
||||
version=version,
|
||||
description='A data-structure parameterization system written for embedding context in JSON objects',
|
||||
author='Dustin J. Mitchell',
|
||||
url='https://taskcluster.github.io/json-e/',
|
||||
author_email='dustin@mozilla.com',
|
||||
packages=['jsone'],
|
||||
test_suite='nose.collector',
|
||||
license='MPL2',
|
||||
tests_require=[
|
||||
"hypothesis",
|
||||
"nose",
|
||||
"PyYAML",
|
||||
"python-dateutil",
|
||||
]
|
||||
)
|
Загрузка…
Ссылка в новой задаче