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:
Andrew Halberstadt 2017-10-27 11:45:07 -04:00
Родитель 21b5b6db95
Коммит 795e68f9ef
10 изменённых файлов: 221 добавлений и 206 удалений

2
third_party/python/json-e/MANIFEST.in поставляемый
Просмотреть файл

@ -1,2 +0,0 @@
include jsone *.py
recursive-exclude test *

10
third_party/python/json-e/PKG-INFO поставляемый
Просмотреть файл

@ -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

10
third_party/python/json-e/jsone/__init__.py поставляемый
Просмотреть файл

@ -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:

194
third_party/python/json-e/jsone/builtins.py поставляемый
Просмотреть файл

@ -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):

84
third_party/python/json-e/jsone/render.py поставляемый
Просмотреть файл

@ -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

38
third_party/python/json-e/jsone/shared.py поставляемый
Просмотреть файл

@ -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()

4
third_party/python/json-e/setup.cfg поставляемый
Просмотреть файл

@ -1,4 +0,0 @@
[egg_info]
tag_build =
tag_date = 0

24
third_party/python/json-e/setup.py поставляемый
Просмотреть файл

@ -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",
]
)