Skip to content

Commit 6f2f583

Browse files
committed
Merge branch 'ordering-bug' into develop
* ordering-bug: Properly handle non numerical ordering operators Add compliance tests for non number comparisons
2 parents 95ed1f1 + abdb6ec commit 6f2f583

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

jmespath/visitor.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ def _is_special_integer_case(x, y):
3333
return x is True or x is False
3434

3535

36+
def _is_actual_number(x):
37+
# We need to handle python's quirkiness with booleans,
38+
# specifically:
39+
#
40+
# >>> isinstance(False, int)
41+
# True
42+
# >>> isinstance(True, int)
43+
# True
44+
if x is True or x is False:
45+
return False
46+
return isinstance(x, (float, int))
47+
48+
3649
class Options(object):
3750
"""Options to control how a JMESPath function is evaluated."""
3851
def __init__(self, dict_cls=None, custom_functions=None):
@@ -76,14 +89,14 @@ def default_visit(self, node, *args, **kwargs):
7689

7790
class TreeInterpreter(Visitor):
7891
COMPARATOR_FUNC = {
79-
'le': operator.le,
92+
'eq': _equals,
8093
'ne': lambda x, y: not _equals(x, y),
8194
'lt': operator.lt,
82-
'lte': operator.le,
83-
'eq': _equals,
8495
'gt': operator.gt,
96+
'lte': operator.le,
8597
'gte': operator.ge
8698
}
99+
_EQUALITY_OPS = ['eq', 'ne']
87100
MAP_TYPE = dict
88101

89102
def __init__(self, options=None):
@@ -115,11 +128,24 @@ def visit_field(self, node, value):
115128
return None
116129

117130
def visit_comparator(self, node, value):
131+
# Common case: comparator is == or !=
118132
comparator_func = self.COMPARATOR_FUNC[node['value']]
119-
return comparator_func(
120-
self.visit(node['children'][0], value),
121-
self.visit(node['children'][1], value)
122-
)
133+
if node['value'] in self._EQUALITY_OPS:
134+
return comparator_func(
135+
self.visit(node['children'][0], value),
136+
self.visit(node['children'][1], value)
137+
)
138+
else:
139+
# Ordering operators are only valid for numbers.
140+
# Evaluating any other type with a comparison operator
141+
# will yield a None value.
142+
left = self.visit(node['children'][0], value)
143+
right = self.visit(node['children'][1], value)
144+
num_types = (int, float)
145+
if not (_is_actual_number(left) and
146+
_is_actual_number(right)):
147+
return None
148+
return comparator_func(left, right)
123149

124150
def visit_current(self, node, value):
125151
return value

tests/compliance/boolean.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@
205205
"given": {
206206
"one": 1,
207207
"two": 2,
208-
"three": 3
208+
"three": 3,
209+
"emptylist": [],
210+
"boolvalue": false
209211
},
210212
"cases": [
211213
{
@@ -236,6 +238,22 @@
236238
"expression": "one != two",
237239
"result": true
238240
},
241+
{
242+
"expression": "emptylist < one",
243+
"result": null
244+
},
245+
{
246+
"expression": "emptylist < nullvalue",
247+
"result": null
248+
},
249+
{
250+
"expression": "emptylist < boolvalue",
251+
"result": null
252+
},
253+
{
254+
"expression": "one < boolvalue",
255+
"result": null
256+
},
239257
{
240258
"expression": "one < two && three > one",
241259
"result": true

0 commit comments

Comments
 (0)