Skip to content

Commit bdbc677

Browse files
committed
Add a strict argument to the JSONPath CLI
1 parent 6ce68d2 commit bdbc677

File tree

3 files changed

+159
-3
lines changed

3 files changed

+159
-3
lines changed

jsonpath/cli.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ def path_sub_command(parser: argparse.ArgumentParser) -> None: # noqa: D103
1919
parser.set_defaults(func=handle_path_command)
2020
group = parser.add_mutually_exclusive_group(required=True)
2121

22-
# TODO: add "strict" argument
23-
2422
group.add_argument(
2523
"-q",
2624
"--query",
@@ -62,6 +60,15 @@ def path_sub_command(parser: argparse.ArgumentParser) -> None: # noqa: D103
6260
help="Disables filter expression well-typedness checks.",
6361
)
6462

63+
parser.add_argument(
64+
"--strict",
65+
action="store_true",
66+
help=(
67+
"Compile and evaluate JSONPath expressions with strict "
68+
"compliance with RFC 9535."
69+
),
70+
)
71+
6572

6673
def pointer_sub_command(parser: argparse.ArgumentParser) -> None: # noqa: D103
6774
parser.set_defaults(func=handle_pointer_command)
@@ -251,6 +258,7 @@ def handle_path_command(args: argparse.Namespace) -> None: # noqa: PLR0912
251258
path = jsonpath.JSONPathEnvironment(
252259
unicode_escape=not args.no_unicode_escape,
253260
well_typed=not args.no_type_checks,
261+
strict=args.strict,
254262
).compile(query)
255263
except JSONPathSyntaxError as err:
256264
if args.debug:

tests/test_cli.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Test cases for the command line interface."""
2+
23
import argparse
34
import json
45
import pathlib
@@ -291,6 +292,50 @@ def test_json_path(
291292
assert len(json.load(fd)) == 4 # noqa: PLR2004
292293

293294

295+
def test_json_path_strict(
296+
parser: argparse.ArgumentParser,
297+
sample_target: str,
298+
outfile: str,
299+
) -> None:
300+
"""Test a valid JSONPath."""
301+
args = parser.parse_args(
302+
[
303+
"--debug",
304+
"path",
305+
"-q",
306+
"price_cap", # No root identifier is an error in strict mode.
307+
"-f",
308+
sample_target,
309+
"-o",
310+
outfile,
311+
"--strict",
312+
]
313+
)
314+
315+
with pytest.raises(JSONPathSyntaxError):
316+
handle_path_command(args)
317+
318+
args = parser.parse_args(
319+
[
320+
"path",
321+
"-q",
322+
"$.price_cap", # With a root identifier is OK.
323+
"-f",
324+
sample_target,
325+
"-o",
326+
outfile,
327+
"--strict",
328+
]
329+
)
330+
331+
handle_path_command(args)
332+
args.output.flush()
333+
334+
with open(outfile, "r") as fd:
335+
rv = json.load(fd)
336+
assert rv == [10]
337+
338+
294339
def test_pointer_command_invalid_target(
295340
parser: argparse.ArgumentParser,
296341
invalid_target: str,

tests/test_convenience_api.py

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,104 @@
1-
# TODO:
1+
import asyncio
2+
from typing import List
3+
4+
import pytest
5+
6+
import jsonpath
7+
8+
9+
def test_convenience_compile() -> None:
10+
# Implicit root identifier works by default, but not when strict=True.
11+
path = jsonpath.compile("a.*")
12+
assert isinstance(path, jsonpath.JSONPath)
13+
assert path.findall({"a": [1, 2, 3]}) == [1, 2, 3]
14+
15+
16+
def test_convenience_compile_strict() -> None:
17+
with pytest.raises(jsonpath.JSONPathSyntaxError):
18+
jsonpath.compile("a.*", strict=True)
19+
20+
path = jsonpath.compile("$.a.*", strict=True)
21+
assert isinstance(path, jsonpath.JSONPath)
22+
assert path.findall({"a": [1, 2, 3]}) == [1, 2, 3]
23+
24+
25+
def test_convenience_findall() -> None:
26+
assert jsonpath.findall("a.*", {"a": [1, 2, 3]}) == [1, 2, 3]
27+
28+
29+
def test_convenience_findall_strict() -> None:
30+
with pytest.raises(jsonpath.JSONPathSyntaxError):
31+
jsonpath.findall("a.*", {"a": [1, 2, 3]}, strict=True)
32+
33+
assert jsonpath.findall("$.a.*", {"a": [1, 2, 3]}, strict=True) == [1, 2, 3]
34+
35+
36+
def test_convenience_findall_async() -> None:
37+
async def coro() -> List[object]:
38+
return await jsonpath.findall_async("a.*", {"a": [1, 2, 3]})
39+
40+
assert asyncio.run(coro()) == [1, 2, 3]
41+
42+
43+
def test_convenience_findall_async_strict() -> None:
44+
async def coro() -> List[object]:
45+
with pytest.raises(jsonpath.JSONPathSyntaxError):
46+
await jsonpath.findall_async("a.*", {"a": [1, 2, 3]}, strict=True)
47+
48+
return await jsonpath.findall_async("$.a.*", {"a": [1, 2, 3]}, strict=True)
49+
50+
assert asyncio.run(coro()) == [1, 2, 3]
51+
52+
53+
def test_convenience_finditer() -> None:
54+
matches = list(jsonpath.finditer("a.*", {"a": [1, 2, 3]}))
55+
assert [m.obj for m in matches] == [1, 2, 3]
56+
57+
58+
def test_convenience_finditer_strict() -> None:
59+
with pytest.raises(jsonpath.JSONPathSyntaxError):
60+
list(jsonpath.finditer("a.*", {"a": [1, 2, 3]}, strict=True))
61+
62+
matches = list(jsonpath.finditer("$.a.*", {"a": [1, 2, 3]}, strict=True))
63+
assert [m.obj for m in matches] == [1, 2, 3]
64+
65+
66+
def test_convenience_finditer_async_strict() -> None:
67+
async def coro() -> List[object]:
68+
with pytest.raises(jsonpath.JSONPathSyntaxError):
69+
await jsonpath.finditer_async("a.*", {"a": [1, 2, 3]}, strict=True)
70+
71+
it = await jsonpath.finditer_async("$.a.*", {"a": [1, 2, 3]}, strict=True)
72+
return [m.obj async for m in it]
73+
74+
assert asyncio.run(coro()) == [1, 2, 3]
75+
76+
77+
def test_convenience_match() -> None:
78+
match = jsonpath.match("a.*", {"a": [1, 2, 3]})
79+
assert isinstance(match, jsonpath.JSONPathMatch)
80+
assert match.obj == 1
81+
82+
83+
def test_convenience_match_strict() -> None:
84+
with pytest.raises(jsonpath.JSONPathSyntaxError):
85+
jsonpath.match("a.*", {"a": [1, 2, 3]}, strict=True)
86+
87+
match = jsonpath.match("$.a.*", {"a": [1, 2, 3]})
88+
assert isinstance(match, jsonpath.JSONPathMatch)
89+
assert match.obj == 1
90+
91+
92+
def test_convenience_query() -> None:
93+
query = jsonpath.query("a.*", {"a": [1, 2, 3]})
94+
assert isinstance(query, jsonpath.Query)
95+
assert list(query.values()) == [1, 2, 3]
96+
97+
98+
def test_convenience_query_strict() -> None:
99+
with pytest.raises(jsonpath.JSONPathSyntaxError):
100+
jsonpath.query("a.*", {"a": [1, 2, 3]}, strict=True)
101+
102+
query = jsonpath.query("$.a.*", {"a": [1, 2, 3]})
103+
assert isinstance(query, jsonpath.Query)
104+
assert list(query.values()) == [1, 2, 3]

0 commit comments

Comments
 (0)