Skip to content

Commit 51ec96b

Browse files
authored
Merge pull request #46 from jg-rp/logical-expr
Fix type checks on logical expressions.
2 parents 5ac48c4 + 544476a commit 51ec96b

File tree

5 files changed

+36
-26
lines changed

5 files changed

+36
-26
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
## Version 0.10.3 (unreleased)
44

5+
**Changes**
6+
7+
- Changed the exception raised when attempting to compare a non-singular filter query from `JSONPathSyntaxError` to `JSONPathTypeError`.
8+
59
**Fixes**
610

711
- Fixed handling of relative and root queries when used as arguments to filter functions. Previously, when those queries resulted in an empty node list, we were converting them to an empty regular list before passing it to functions that accept _ValueType_ arguments. Now, in such cases, we convert empty node lists to the special result _Nothing_, which is required by the spec.
12+
- Fixed well-typedness checks on JSONPath logical expressions (those that involve `&&` or `||`) and non-singular filter queries. Previously we were erroneously applying the checks for comparison expressions to logical expressions too. Now non-singular queries in logical expressions act as an existence test. See [#45] (https://github.com/jg-rp/python-jsonpath/issues/45).
813

914
## Version 0.10.2
1015

jsonpath/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: 2023-present James Prior <jamesgr.prior@gmail.com>
22
#
33
# SPDX-License-Identifier: MIT
4-
__version__ = "0.10.2"
4+
__version__ = "0.10.3"

jsonpath/parse.py

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class Parser:
180180
TOKEN_RE: "=~",
181181
}
182182

183-
SINGULAR_QUERY_COMPARISON_OPERATORS = frozenset(
183+
COMPARISON_OPERATORS = frozenset(
184184
[
185185
"==",
186186
">=",
@@ -511,10 +511,7 @@ def parse_infix_expression(
511511
right = self.parse_filter_selector(stream, precedence)
512512
operator = self.BINARY_OPERATORS[tok.kind]
513513

514-
self._raise_for_non_singular_query(left, tok) # TODO: store tok on expression
515-
self._raise_for_non_singular_query(right, tok)
516-
517-
if operator in self.SINGULAR_QUERY_COMPARISON_OPERATORS:
514+
if self.env.well_typed and operator in self.COMPARISON_OPERATORS:
518515
self._raise_for_non_comparable_function(left, tok)
519516
self._raise_for_non_comparable_function(right, tok)
520517

@@ -666,26 +663,18 @@ def _decode_string_literal(self, token: Token) -> str:
666663

667664
return token.value
668665

669-
def _raise_for_non_singular_query(
670-
self, expr: FilterExpression, token: Token
671-
) -> None:
672-
if (
673-
self.env.well_typed
674-
and isinstance(expr, Path)
675-
and not expr.path.singular_query()
676-
):
677-
raise JSONPathSyntaxError(
678-
"non-singular query is not comparable", token=token
679-
)
680-
681666
def _raise_for_non_comparable_function(
682667
self, expr: FilterExpression, token: Token
683668
) -> None:
684-
if not self.env.well_typed or not isinstance(expr, FunctionExtension):
685-
return
686-
func = self.env.function_extensions.get(expr.name)
687-
if (
688-
isinstance(func, FilterFunction)
689-
and func.return_type != ExpressionType.VALUE
690-
):
691-
raise JSONPathTypeError(f"result of {expr.name}() is not comparable", token)
669+
if isinstance(expr, Path) and not expr.path.singular_query():
670+
raise JSONPathTypeError("non-singular query is not comparable", token=token)
671+
672+
if isinstance(expr, FunctionExtension):
673+
func = self.env.function_extensions.get(expr.name)
674+
if (
675+
isinstance(func, FilterFunction)
676+
and func.return_type != ExpressionType.VALUE
677+
):
678+
raise JSONPathTypeError(
679+
f"result of {expr.name}() is not comparable", token
680+
)

tests/test_env.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66

77
from jsonpath import JSONPathEnvironment
8+
from jsonpath import JSONPathTypeError
89

910

1011
@pytest.fixture()
@@ -170,3 +171,13 @@ class MyJSONPathEnvironment(JSONPathEnvironment):
170171
data = {"foo": {"a": 1, "b": 2, "c": 3}}
171172
assert env.findall("$.foo.*~", data) == ["a", "b", "c"]
172173
assert env.findall("$.foo.*", data) == [1, 2, 3]
174+
175+
176+
def test_disable_well_typed_checks() -> None:
177+
"""Test that we can disable checks for well-typedness."""
178+
env = JSONPathEnvironment(well_typed=True)
179+
with pytest.raises(JSONPathTypeError):
180+
env.compile("$[?@.* > 2]")
181+
182+
env = JSONPathEnvironment(well_typed=False)
183+
env.compile("$[?@.* > 2]")

tests/test_errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ def test_function_missing_param(env: JSONPathEnvironment) -> None:
2323
def test_function_too_many_params(env: JSONPathEnvironment) -> None:
2424
with pytest.raises(JSONPathTypeError):
2525
env.compile("$[?(length(@.a, @.b)==1)]")
26+
27+
28+
def test_non_singular_query_is_not_comparable(env: JSONPathEnvironment) -> None:
29+
with pytest.raises(JSONPathTypeError):
30+
env.compile("$[?@.* > 2]")

0 commit comments

Comments
 (0)