diff --git a/jmespath/functions.py b/jmespath/functions.py index 627b569d..38dfa0c9 100644 --- a/jmespath/functions.py +++ b/jmespath/functions.py @@ -316,14 +316,19 @@ def _func_sort_by(self, array, expref): # either a string of a number. We then create a key function # that validates that type, which requires that remaining array # elements resolve to the same type as the first element. + # The type check is done on the resolved key, not on the array + # element, because that's the value the signature constrains. + keyfunc = self._create_key_func(expref, + ['number', 'string'], + 'sort_by') + required_key = keyfunc(array[0]) required_type = self._convert_to_jmespath_type( - type(expref.visit(expref.expression, array[0])).__name__) + type(required_key).__name__) if required_type not in ['number', 'string']: raise exceptions.JMESPathTypeError( - 'sort_by', array[0], required_type, ['string', 'number']) - keyfunc = self._create_key_func(expref, - [required_type], - 'sort_by') + 'sort_by', required_key, required_type, + ['string', 'number']) + keyfunc = self._create_key_func(expref, [required_type], 'sort_by') return list(sorted(array, key=keyfunc)) @signature({'types': ['array']}, {'types': ['expref']}) diff --git a/tests/test_functions.py b/tests/test_functions.py index a352d69e..0dfad824 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -55,3 +55,13 @@ def test_variadic_is_pluralized(self): self.assertEqual( str(exception), 'Expected at least 1 argument for function not_null(), received 0') + + def test_sort_by_reports_the_resolved_key_in_type_errors(self): + # The spec defines sort_by as sort_by(array, expression->number| + # expression->string), so the value that has the invalid type is the + # value the expression resolved to, not the array element it was + # resolved against. min_by/max_by already report the resolved key. + with self.assertRaises(exceptions.JMESPathTypeError) as e: + jmespath.search('sort_by(@, &k)', [{'k': [1, 2]}]) + self.assertIn('invalid type for value: [1, 2]', str(e.exception)) + self.assertIn('received: "array"', str(e.exception))