python-jsonpath 0.10.1__tar.gz → 0.10.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/PKG-INFO +4 -1
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/README.md +3 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/__about__.py +1 -1
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/filter.py +13 -1
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/length.py +10 -4
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/lex.py +27 -12
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/parse.py +17 -29
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/.gitignore +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/LICENSE.txt +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/__init__.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/__main__.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/_data.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/cli.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/env.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/exceptions.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/__init__.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/arguments.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/count.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/filter_function.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/is_instance.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/keys.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/match.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/search.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/typeof.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/value.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/match.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/patch.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/path.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/pointer.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/py.typed +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/selectors.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/stream.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/token.py +0 -0
- {python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-jsonpath
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.3
|
|
4
4
|
Summary: JSONPath, JSON Pointer and JSON Patch for Python.
|
|
5
5
|
Project-URL: Documentation, https://jg-rp.github.io/python-jsonpath/
|
|
6
6
|
Project-URL: Issues, https://github.com/jg-rp/python-jsonpath/issues
|
|
@@ -36,6 +36,9 @@ A flexible JSONPath engine for Python with JSON Pointer and JSON Patch.
|
|
|
36
36
|
<a href="https://github.com/jg-rp/python-jsonpath/actions">
|
|
37
37
|
<img src="https://img.shields.io/github/actions/workflow/status/jg-rp/python-jsonpath/tests.yaml?branch=main&label=tests&style=flat-square" alt="Tests">
|
|
38
38
|
</a>
|
|
39
|
+
<a href="https://pypi.org/project/python-jsonpath">
|
|
40
|
+
<img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/python-jsonpath?style=flat-square">
|
|
41
|
+
</a>
|
|
39
42
|
<br>
|
|
40
43
|
<a href="https://pypi.org/project/python-jsonpath">
|
|
41
44
|
<img src="https://img.shields.io/pypi/v/python-jsonpath.svg?style=flat-square" alt="PyPi - Version">
|
|
@@ -11,6 +11,9 @@ A flexible JSONPath engine for Python with JSON Pointer and JSON Patch.
|
|
|
11
11
|
<a href="https://github.com/jg-rp/python-jsonpath/actions">
|
|
12
12
|
<img src="https://img.shields.io/github/actions/workflow/status/jg-rp/python-jsonpath/tests.yaml?branch=main&label=tests&style=flat-square" alt="Tests">
|
|
13
13
|
</a>
|
|
14
|
+
<a href="https://pypi.org/project/python-jsonpath">
|
|
15
|
+
<img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/python-jsonpath?style=flat-square">
|
|
16
|
+
</a>
|
|
14
17
|
<br>
|
|
15
18
|
<a href="https://pypi.org/project/python-jsonpath">
|
|
16
19
|
<img src="https://img.shields.io/pypi/v/python-jsonpath.svg?style=flat-square" alt="PyPi - Version">
|
|
@@ -623,7 +623,19 @@ class FunctionExtension(FilterExpression):
|
|
|
623
623
|
if func.arg_types[idx] != ExpressionType.NODES and isinstance(
|
|
624
624
|
arg, NodeList
|
|
625
625
|
):
|
|
626
|
-
|
|
626
|
+
if len(arg) == 0:
|
|
627
|
+
# If the query results in an empty nodelist, the
|
|
628
|
+
# argument is the special result Nothing.
|
|
629
|
+
_args.append(UNDEFINED)
|
|
630
|
+
elif len(arg) == 1:
|
|
631
|
+
# If the query results in a nodelist consisting of a
|
|
632
|
+
# single node, the argument is the value of the node
|
|
633
|
+
_args.append(arg[0].obj)
|
|
634
|
+
else:
|
|
635
|
+
# This should not be possible as a non-singular query
|
|
636
|
+
# would have been rejected when checking function
|
|
637
|
+
# well-typedness.
|
|
638
|
+
_args.append(arg)
|
|
627
639
|
else:
|
|
628
640
|
_args.append(arg)
|
|
629
641
|
return _args
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""The standard `length` function extension."""
|
|
2
2
|
from collections.abc import Sized
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Union
|
|
4
4
|
|
|
5
|
+
from jsonpath.filter import UNDEFINED
|
|
6
|
+
from jsonpath.filter import _Undefined
|
|
5
7
|
from jsonpath.function_extensions import ExpressionType
|
|
6
8
|
from jsonpath.function_extensions import FilterFunction
|
|
7
9
|
|
|
@@ -12,9 +14,13 @@ class Length(FilterFunction):
|
|
|
12
14
|
arg_types = [ExpressionType.VALUE]
|
|
13
15
|
return_type = ExpressionType.VALUE
|
|
14
16
|
|
|
15
|
-
def __call__(self, obj: Sized) ->
|
|
16
|
-
"""Return an object's length
|
|
17
|
+
def __call__(self, obj: Sized) -> Union[int, _Undefined]:
|
|
18
|
+
"""Return an object's length.
|
|
19
|
+
|
|
20
|
+
If the object does not have a length, the special _Nothing_ value is
|
|
21
|
+
returned.
|
|
22
|
+
"""
|
|
17
23
|
try:
|
|
18
24
|
return len(obj)
|
|
19
25
|
except TypeError:
|
|
20
|
-
return
|
|
26
|
+
return UNDEFINED
|
|
@@ -66,10 +66,34 @@ if TYPE_CHECKING:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
class Lexer:
|
|
69
|
-
"""Tokenize a JSONPath string.
|
|
69
|
+
"""Tokenize a JSONPath string.
|
|
70
|
+
|
|
71
|
+
Some customization can be achieved by subclassing _Lexer_ and setting
|
|
72
|
+
class attributes. Then setting `lexer_class` on a `JSONPathEnvironment`.
|
|
73
|
+
|
|
74
|
+
Attributes:
|
|
75
|
+
key_pattern: The regular expression pattern used to match mapping
|
|
76
|
+
keys/properties.
|
|
77
|
+
logical_not_pattern: The regular expression pattern used to match
|
|
78
|
+
logical negation tokens. By default, `not` and `!` are
|
|
79
|
+
equivalent.
|
|
80
|
+
logical_and_pattern: The regular expression pattern used to match
|
|
81
|
+
logical _and_ tokens. By default, `and` and `&&` are equivalent.
|
|
82
|
+
logical_or_pattern: The regular expression pattern used to match
|
|
83
|
+
logical _or_ tokens. By default, `or` and `||` are equivalent.
|
|
84
|
+
"""
|
|
70
85
|
|
|
71
86
|
key_pattern = r"[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*"
|
|
72
87
|
|
|
88
|
+
# `not` or !
|
|
89
|
+
logical_not_pattern = r"(?:not|!)"
|
|
90
|
+
|
|
91
|
+
# && or `and`
|
|
92
|
+
logical_and_pattern = r"(?:&&|and)"
|
|
93
|
+
|
|
94
|
+
# || or `or`
|
|
95
|
+
logical_or_pattern = r"(?:\|\||or)"
|
|
96
|
+
|
|
73
97
|
def __init__(self, *, env: JSONPathEnvironment) -> None:
|
|
74
98
|
self.env = env
|
|
75
99
|
|
|
@@ -85,15 +109,6 @@ class Lexer:
|
|
|
85
109
|
r"(?::\s*(?P<G_LSLICE_STEP>\-?\d*))?"
|
|
86
110
|
)
|
|
87
111
|
|
|
88
|
-
# `not` or !
|
|
89
|
-
self.logical_not_pattern = r"(?:not|!)"
|
|
90
|
-
|
|
91
|
-
# && or `and`
|
|
92
|
-
self.bool_and_pattern = r"(?:&&|and)"
|
|
93
|
-
|
|
94
|
-
# || or `or`
|
|
95
|
-
self.bool_or_pattern = r"(?:\|\||or)"
|
|
96
|
-
|
|
97
112
|
# /pattern/ or /pattern/flags
|
|
98
113
|
self.re_pattern = r"/(?P<G_RE>.+?)/(?P<G_RE_FLAGS>[aims]*)"
|
|
99
114
|
|
|
@@ -114,8 +129,8 @@ class Lexer:
|
|
|
114
129
|
(TOKEN_FLOAT, r"-?\d+\.\d*(?:e[+-]?\d+)?"),
|
|
115
130
|
(TOKEN_INT, r"-?\d+(?P<G_EXP>e[+\-]?\d+)?\b"),
|
|
116
131
|
(TOKEN_DDOT, r"\.\."),
|
|
117
|
-
(TOKEN_AND, self.
|
|
118
|
-
(TOKEN_OR, self.
|
|
132
|
+
(TOKEN_AND, self.logical_and_pattern),
|
|
133
|
+
(TOKEN_OR, self.logical_or_pattern),
|
|
119
134
|
(TOKEN_ROOT, re.escape(self.env.root_token)),
|
|
120
135
|
(TOKEN_SELF, re.escape(self.env.self_token)),
|
|
121
136
|
(TOKEN_KEY, re.escape(self.env.key_token)),
|
|
@@ -158,7 +158,7 @@ class Parser:
|
|
|
158
158
|
TOKEN_LG: PRECEDENCE_RELATIONAL,
|
|
159
159
|
TOKEN_LT: PRECEDENCE_RELATIONAL,
|
|
160
160
|
TOKEN_NE: PRECEDENCE_RELATIONAL,
|
|
161
|
-
TOKEN_NOT:
|
|
161
|
+
TOKEN_NOT: PRECEDENCE_PREFIX,
|
|
162
162
|
TOKEN_OR: PRECEDENCE_LOGICAL,
|
|
163
163
|
TOKEN_RE: PRECEDENCE_RELATIONAL,
|
|
164
164
|
TOKEN_RPAREN: PRECEDENCE_LOWEST,
|
|
@@ -180,7 +180,7 @@ class Parser:
|
|
|
180
180
|
TOKEN_RE: "=~",
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
COMPARISON_OPERATORS = frozenset(
|
|
184
184
|
[
|
|
185
185
|
"==",
|
|
186
186
|
">=",
|
|
@@ -446,6 +446,7 @@ class Parser:
|
|
|
446
446
|
)
|
|
447
447
|
|
|
448
448
|
if stream.peek.kind != TOKEN_RBRACKET:
|
|
449
|
+
# TODO: error message .. expected a comma or logical operator
|
|
449
450
|
stream.expect_peek(TOKEN_COMMA)
|
|
450
451
|
stream.next_token()
|
|
451
452
|
|
|
@@ -499,9 +500,7 @@ class Parser:
|
|
|
499
500
|
assert tok.kind == TOKEN_NOT
|
|
500
501
|
return PrefixExpression(
|
|
501
502
|
operator="!",
|
|
502
|
-
right=self.parse_filter_selector(
|
|
503
|
-
stream, precedence=self.PRECEDENCE_LOGICALRIGHT
|
|
504
|
-
),
|
|
503
|
+
right=self.parse_filter_selector(stream, precedence=self.PRECEDENCE_PREFIX),
|
|
505
504
|
)
|
|
506
505
|
|
|
507
506
|
def parse_infix_expression(
|
|
@@ -512,10 +511,7 @@ class Parser:
|
|
|
512
511
|
right = self.parse_filter_selector(stream, precedence)
|
|
513
512
|
operator = self.BINARY_OPERATORS[tok.kind]
|
|
514
513
|
|
|
515
|
-
self.
|
|
516
|
-
self._raise_for_non_singular_query(right, tok)
|
|
517
|
-
|
|
518
|
-
if operator in self.SINGULAR_QUERY_COMPARISON_OPERATORS:
|
|
514
|
+
if self.env.well_typed and operator in self.COMPARISON_OPERATORS:
|
|
519
515
|
self._raise_for_non_comparable_function(left, tok)
|
|
520
516
|
self._raise_for_non_comparable_function(right, tok)
|
|
521
517
|
|
|
@@ -667,26 +663,18 @@ class Parser:
|
|
|
667
663
|
|
|
668
664
|
return token.value
|
|
669
665
|
|
|
670
|
-
def _raise_for_non_singular_query(
|
|
671
|
-
self, expr: FilterExpression, token: Token
|
|
672
|
-
) -> None:
|
|
673
|
-
if (
|
|
674
|
-
self.env.well_typed
|
|
675
|
-
and isinstance(expr, Path)
|
|
676
|
-
and not expr.path.singular_query()
|
|
677
|
-
):
|
|
678
|
-
raise JSONPathSyntaxError(
|
|
679
|
-
"non-singular query is not comparable", token=token
|
|
680
|
-
)
|
|
681
|
-
|
|
682
666
|
def _raise_for_non_comparable_function(
|
|
683
667
|
self, expr: FilterExpression, token: Token
|
|
684
668
|
) -> None:
|
|
685
|
-
if
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
if (
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/filter_function.py
RENAMED
|
File without changes
|
{python_jsonpath-0.10.1 → python_jsonpath-0.10.3}/jsonpath/function_extensions/is_instance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|