Skip to content

Commit ade1134

Browse files
committed
Handle filter literals outside comparison or function expressions
1 parent cdfce6b commit ade1134

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

jsonpath_rfc9535/environment.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
from .exceptions import JSONPathNameError
2020
from .exceptions import JSONPathTypeError
2121
from .filter_expressions import ComparisonExpression
22+
from .filter_expressions import FilterExpressionLiteral
2223
from .filter_expressions import FilterQuery
2324
from .filter_expressions import FunctionExtension
24-
from .filter_expressions import JSONPathLiteral
2525
from .filter_expressions import LogicalExpression
2626
from .function_extensions import ExpressionType
2727
from .function_extensions import FilterFunction
@@ -185,7 +185,7 @@ def check_well_typedness(
185185
arg = args[idx]
186186
if typ == ExpressionType.VALUE:
187187
if not (
188-
isinstance(arg, JSONPathLiteral)
188+
isinstance(arg, FilterExpressionLiteral)
189189
or (isinstance(arg, FilterQuery) and arg.query.singular_query())
190190
or (self._function_return_type(arg) == ExpressionType.VALUE)
191191
):

jsonpath_rfc9535/filter_expressions.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def evaluate(self, context: FilterContext) -> bool:
7070
LITERAL_T = TypeVar("LITERAL_T")
7171

7272

73-
class JSONPathLiteral(Expression, Generic[LITERAL_T]):
73+
class FilterExpressionLiteral(Expression, Generic[LITERAL_T]):
7474
"""Base class for filter expression literals."""
7575

7676
__slots__ = ("value",)
@@ -93,13 +93,13 @@ def evaluate(self, _: FilterContext) -> LITERAL_T:
9393
return self.value
9494

9595

96-
class BooleanLiteral(JSONPathLiteral[bool]):
96+
class BooleanLiteral(FilterExpressionLiteral[bool]):
9797
"""A Boolean `true` or `false`."""
9898

9999
__slots__ = ()
100100

101101

102-
class StringLiteral(JSONPathLiteral[str]):
102+
class StringLiteral(FilterExpressionLiteral[str]):
103103
"""A string literal."""
104104

105105
__slots__ = ()
@@ -108,19 +108,19 @@ def __str__(self) -> str:
108108
return json.dumps(self.value)
109109

110110

111-
class IntegerLiteral(JSONPathLiteral[int]):
111+
class IntegerLiteral(FilterExpressionLiteral[int]):
112112
"""An integer literal."""
113113

114114
__slots__ = ()
115115

116116

117-
class FloatLiteral(JSONPathLiteral[float]):
117+
class FloatLiteral(FilterExpressionLiteral[float]):
118118
"""A float literal."""
119119

120120
__slots__ = ()
121121

122122

123-
class NullLiteral(JSONPathLiteral[None]):
123+
class NullLiteral(FilterExpressionLiteral[None]):
124124
"""A null literal."""
125125

126126
__slots__ = ()

jsonpath_rfc9535/parse.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .filter_expressions import ComparisonExpression
2222
from .filter_expressions import Expression
2323
from .filter_expressions import FilterExpression
24+
from .filter_expressions import FilterExpressionLiteral
2425
from .filter_expressions import FilterQuery
2526
from .filter_expressions import FloatLiteral
2627
from .filter_expressions import FunctionExtension
@@ -396,6 +397,13 @@ def parse_filter(self, stream: TokenStream) -> Filter:
396397
f"result of {expr.name}() must be compared", token=tok
397398
)
398399

400+
if isinstance(expr, FilterExpressionLiteral):
401+
raise JSONPathSyntaxError(
402+
"filter expression literals outside of "
403+
"function expressions must be compared",
404+
token=expr.token,
405+
)
406+
399407
return Filter(
400408
env=self.env,
401409
token=tok,
@@ -444,7 +452,19 @@ def parse_infix_expression(
444452
self._raise_for_non_comparable_function(right, tok)
445453
return ComparisonExpression(tok, left, operator, right)
446454

447-
# TODO: check for valid basic expressions
455+
if isinstance(left, FilterExpressionLiteral):
456+
raise JSONPathSyntaxError(
457+
"filter expression literals outside of "
458+
"function expressions must be compared",
459+
token=left.token,
460+
)
461+
if isinstance(right, FilterExpressionLiteral):
462+
raise JSONPathSyntaxError(
463+
"filter expression literals outside of "
464+
"function expressions must be compared",
465+
token=right.token,
466+
)
467+
448468
return LogicalExpression(tok, left, operator, right)
449469

450470
def parse_grouped_expression(self, stream: TokenStream) -> Expression:

tests/test_errors.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import operator
12
from typing import Any
23
from typing import List
4+
from typing import NamedTuple
35

46
import pytest
57

@@ -60,3 +62,33 @@ class MockEnv(JSONPathEnvironment):
6062

6163
with pytest.raises(JSONPathRecursionError):
6264
env.find(query, data)
65+
66+
67+
class FilterLiteralTestCase(NamedTuple):
68+
description: str
69+
query: str
70+
71+
72+
BAD_FILTER_LITERAL_TEST_CASES: List[FilterLiteralTestCase] = [
73+
FilterLiteralTestCase("just true", "$[?true]"),
74+
FilterLiteralTestCase("just string", "$[?'foo']"),
75+
FilterLiteralTestCase("just int", "$[?2]"),
76+
FilterLiteralTestCase("just float", "$[?2.2]"),
77+
FilterLiteralTestCase("just null", "$[?null]"),
78+
FilterLiteralTestCase("literal and literal", "$[?true and false]"),
79+
FilterLiteralTestCase("literal or literal", "$[?true or false]"),
80+
FilterLiteralTestCase("comparison and literal", "$[?true == false and false]"),
81+
FilterLiteralTestCase("comparison or literal", "$[?true == false or false]"),
82+
FilterLiteralTestCase("literal and comparison", "$[?true and true == false]"),
83+
FilterLiteralTestCase("literal or comparison", "$[?false or true == false]"),
84+
]
85+
86+
87+
@pytest.mark.parametrize(
88+
"case", BAD_FILTER_LITERAL_TEST_CASES, ids=operator.attrgetter("description")
89+
)
90+
def test_filter_literals_must_be_compared(
91+
env: JSONPathEnvironment, case: FilterLiteralTestCase
92+
) -> None:
93+
with pytest.raises(JSONPathSyntaxError):
94+
env.compile(case.query)

0 commit comments

Comments
 (0)