Skip to content

Commit

Permalink
Merge pull request #46 from jg-rp/logical-expr
Browse files Browse the repository at this point in the history
Fix type checks on logical expressions.
  • Loading branch information
jg-rp authored Jan 2, 2024
2 parents 5ac48c4 + 544476a commit 51ec96b
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 26 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## Version 0.10.3 (unreleased)

**Changes**

- Changed the exception raised when attempting to compare a non-singular filter query from `JSONPathSyntaxError` to `JSONPathTypeError`.

**Fixes**

- 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.
- 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).

## Version 0.10.2

Expand Down
2 changes: 1 addition & 1 deletion jsonpath/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-present James Prior <[email protected]>
#
# SPDX-License-Identifier: MIT
__version__ = "0.10.2"
__version__ = "0.10.3"
39 changes: 14 additions & 25 deletions jsonpath/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Parser:
TOKEN_RE: "=~",
}

SINGULAR_QUERY_COMPARISON_OPERATORS = frozenset(
COMPARISON_OPERATORS = frozenset(
[
"==",
">=",
Expand Down Expand Up @@ -511,10 +511,7 @@ def parse_infix_expression(
right = self.parse_filter_selector(stream, precedence)
operator = self.BINARY_OPERATORS[tok.kind]

self._raise_for_non_singular_query(left, tok) # TODO: store tok on expression
self._raise_for_non_singular_query(right, tok)

if operator in self.SINGULAR_QUERY_COMPARISON_OPERATORS:
if self.env.well_typed and operator in self.COMPARISON_OPERATORS:
self._raise_for_non_comparable_function(left, tok)
self._raise_for_non_comparable_function(right, tok)

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

return token.value

def _raise_for_non_singular_query(
self, expr: FilterExpression, token: Token
) -> None:
if (
self.env.well_typed
and isinstance(expr, Path)
and not expr.path.singular_query()
):
raise JSONPathSyntaxError(
"non-singular query is not comparable", token=token
)

def _raise_for_non_comparable_function(
self, expr: FilterExpression, token: Token
) -> None:
if not self.env.well_typed or not isinstance(expr, FunctionExtension):
return
func = self.env.function_extensions.get(expr.name)
if (
isinstance(func, FilterFunction)
and func.return_type != ExpressionType.VALUE
):
raise JSONPathTypeError(f"result of {expr.name}() is not comparable", token)
if isinstance(expr, Path) and not expr.path.singular_query():
raise JSONPathTypeError("non-singular query is not comparable", token=token)

if isinstance(expr, FunctionExtension):
func = self.env.function_extensions.get(expr.name)
if (
isinstance(func, FilterFunction)
and func.return_type != ExpressionType.VALUE
):
raise JSONPathTypeError(
f"result of {expr.name}() is not comparable", token
)
11 changes: 11 additions & 0 deletions tests/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest

from jsonpath import JSONPathEnvironment
from jsonpath import JSONPathTypeError


@pytest.fixture()
Expand Down Expand Up @@ -170,3 +171,13 @@ class MyJSONPathEnvironment(JSONPathEnvironment):
data = {"foo": {"a": 1, "b": 2, "c": 3}}
assert env.findall("$.foo.*~", data) == ["a", "b", "c"]
assert env.findall("$.foo.*", data) == [1, 2, 3]


def test_disable_well_typed_checks() -> None:
"""Test that we can disable checks for well-typedness."""
env = JSONPathEnvironment(well_typed=True)
with pytest.raises(JSONPathTypeError):
env.compile("$[?@.* > 2]")

env = JSONPathEnvironment(well_typed=False)
env.compile("$[?@.* > 2]")
5 changes: 5 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ def test_function_missing_param(env: JSONPathEnvironment) -> None:
def test_function_too_many_params(env: JSONPathEnvironment) -> None:
with pytest.raises(JSONPathTypeError):
env.compile("$[?(length(@.a, @.b)==1)]")


def test_non_singular_query_is_not_comparable(env: JSONPathEnvironment) -> None:
with pytest.raises(JSONPathTypeError):
env.compile("$[?@.* > 2]")

0 comments on commit 51ec96b

Please sign in to comment.