Skip to content

Commit 8ac2080

Browse files
committed
fix: resolve issue #10 by raising JSONPathTypeError on mixed type sorting
1 parent 100bf6f commit 8ac2080

File tree

3 files changed

+55
-11
lines changed

3 files changed

+55
-11
lines changed

jsonpath/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
__version__ = "1.0.6"
99
__author__ = "sean2077"
1010

11-
from .jsonpath import ExprSyntaxError, JSONPath, compile, search
11+
from .jsonpath import ExprSyntaxError, JSONPath, JSONPathTypeError, compile, search
1212

13-
__all__ = ["JSONPath", "ExprSyntaxError", "compile", "search"]
13+
__all__ = ["JSONPath", "ExprSyntaxError", "JSONPathTypeError", "compile", "search"]

jsonpath/jsonpath.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class ExprSyntaxError(Exception):
2929
pass
3030

3131

32+
class JSONPathTypeError(Exception):
33+
pass
34+
35+
3236
class JSONPath:
3337
RESULT_TYPE = {
3438
"VALUE": "A list of specific values.",
@@ -189,14 +193,17 @@ def _getattr(obj: dict, path: str, *, convert_number_str=False):
189193

190194
@staticmethod
191195
def _sorter(obj, sortbys):
192-
for sortby in sortbys.split(",")[::-1]:
193-
if sortby.startswith("~"):
194-
obj.sort(
195-
key=lambda t, k=sortby: JSONPath._getattr(t[1], k[1:], convert_number_str=True),
196-
reverse=True,
197-
)
198-
else:
199-
obj.sort(key=lambda t, k=sortby: JSONPath._getattr(t[1], k, convert_number_str=True))
196+
try:
197+
for sortby in sortbys.split(",")[::-1]:
198+
if sortby.startswith("~"):
199+
obj.sort(
200+
key=lambda t, k=sortby: JSONPath._getattr(t[1], k[1:], convert_number_str=True),
201+
reverse=True,
202+
)
203+
else:
204+
obj.sort(key=lambda t, k=sortby: JSONPath._getattr(t[1], k, convert_number_str=True))
205+
except TypeError as e:
206+
raise JSONPathTypeError(f"not possible to compare str and int when sorting: {e}") from e
200207

201208
def _filter(self, obj, i: int, path: str, step: str):
202209
r = False

tests/test_issues.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from jsonpath import JSONPath
1+
import pytest
2+
3+
from jsonpath import JSONPath, JSONPathTypeError
24

35

46
def test_issue_17():
@@ -64,3 +66,38 @@ def test_issue_15_bracket_notation_in_filter():
6466
# Case 2: Dot notation in filter (already working, but good to keep)
6567
expr_dot = "$.tool.books.[*].properties.[*].[?(@.name=='moq')].value"
6668
assert JSONPath(expr_dot).parse(data) == ["x", "q"]
69+
70+
71+
def test_issue_10_mixed_type_sorting():
72+
# Case 1: Mixed types (int and str) - Should raise JSONPathTypeError
73+
data1 = [{"code": "N"}, {"code": 0}]
74+
with pytest.raises(JSONPathTypeError):
75+
JSONPath("$./(code)").parse(data1)
76+
77+
# Case 2: Numbers (numerical sort)
78+
data2 = [{"code": 10}, {"code": 2}]
79+
result2 = JSONPath("$./(code)").parse(data2)
80+
assert result2 == [{"code": 2}, {"code": 10}]
81+
82+
# Case 3: Strings (lexicographical sort)
83+
data3 = [{"code": "b"}, {"code": "a"}]
84+
result3 = JSONPath("$./(code)").parse(data3)
85+
assert result3 == [{"code": "a"}, {"code": "b"}]
86+
87+
# Case 4: Strings that look like numbers (converted to numbers by _getattr)
88+
data4 = [{"code": "10"}, {"code": "2"}]
89+
result4 = JSONPath("$./(code)").parse(data4)
90+
# "2" -> 2, "10" -> 10. 2 < 10.
91+
assert result4 == [{"code": "2"}, {"code": "10"}]
92+
93+
# Case 5: Mixed numbers and strings that look like numbers
94+
data5 = [{"code": 10}, {"code": "2"}]
95+
result5 = JSONPath("$./(code)").parse(data5)
96+
# "2" -> 2. 2 < 10.
97+
assert result5 == [{"code": "2"}, {"code": 10}]
98+
99+
# Case 6: Missing keys (None)
100+
# None vs int comparison in Python 3 raises TypeError
101+
data6 = [{"code": 1}, {"other": 2}]
102+
with pytest.raises(JSONPathTypeError):
103+
JSONPath("$./(code)").parse(data6)

0 commit comments

Comments
 (0)