diff --git a/jmespath/compat.py b/jmespath/compat.py index 2ed0fe78..e73cb61a 100644 --- a/jmespath/compat.py +++ b/jmespath/compat.py @@ -15,7 +15,9 @@ def __new__(cls, name, this_bases, d): if PY2: text_type = unicode string_type = basestring + iteritems = dict.iteritems from itertools import izip_longest as zip_longest + from itertools import imap as map def with_str_method(cls): """Class decorator that handles __str__ compat between py2 and py3.""" @@ -50,6 +52,8 @@ def get_methods(cls): else: text_type = str string_type = str + iteritems = dict.items + map = map from itertools import zip_longest def with_str_method(cls): diff --git a/jmespath/functions.py b/jmespath/functions.py index ab922822..2edf5ef5 100644 --- a/jmespath/functions.py +++ b/jmespath/functions.py @@ -2,8 +2,11 @@ import json from jmespath import exceptions +from jmespath.compat import get_methods +from jmespath.compat import iteritems +from jmespath.compat import map from jmespath.compat import string_type as STRING_TYPE -from jmespath.compat import get_methods, with_metaclass +from jmespath.compat import with_metaclass # python types -> jmespath types @@ -280,6 +283,14 @@ def _func_sort(self, arg): def _func_sum(self, arg): return sum(arg) + @signature({'types': ['object']}) + def _func_items(self, arg): + return list(map(list, iteritems(arg))) + + @signature({'types': ['array']}) + def _func_from_items(self, items): + return dict(items) + @signature({"types": ['object']}) def _func_keys(self, arg): # To be consistent with .values() @@ -339,6 +350,10 @@ def _func_max_by(self, array, expref): 'min_by') return max(array, key=keyfunc) + @signature({'types': ['array'], 'variadic': True}) + def _func_zip(self, *arguments): + return list(map(list, zip(*arguments))) + def _create_key_func(self, expref, allowed_types, function_name): def keyfunc(x): result = expref.visit(expref.expression, x) diff --git a/tests/compliance/functions.json b/tests/compliance/functions.json index a749dda2..149cb88e 100644 --- a/tests/compliance/functions.json +++ b/tests/compliance/functions.json @@ -12,6 +12,7 @@ "empty_list": [], "empty_hash": {}, "objects": {"foo": "bar", "bar": "baz"}, + "items": [["a", "first"], ["b", "second"], ["c", "third"]], "null_key": null }, "cases": [ @@ -175,6 +176,22 @@ "expression": "floor(str)", "error": "invalid-type" }, + { + "expression": "sort_by(items(objects), &[0])", + "result": [["bar", "baz"], ["foo", "bar"]] + }, + { + "expression": "items(empty_hash)", + "result": [] + }, + { + "expression": "items(numbers)", + "error": "invalid-type" + }, + { + "expression": "from_items(items)", + "result": {"a": "first", "b": "second", "c": "third"} + }, { "expression": "length('abc')", "result": 3 @@ -189,7 +206,7 @@ }, { "expression": "length(@)", - "result": 12 + "result": 13 }, { "expression": "length(strings[0])", @@ -575,6 +592,18 @@ "expression": "not_null()", "error": "invalid-arity" }, + { + "expression": "zip(strings, numbers)", + "result": [["a", -1], ["b", 3], ["c", 4]] + }, + { + "expression": "zip(strings, numbers, decimals)", + "result": [["a", -1, 1.01], ["b", 3, 1.2], ["c", 4, -1.5]] + }, + { + "expression": "zip(str)", + "error": "invalid-type" + }, { "description": "function projection on single arg function", "expression": "numbers[].to_string(@)",