python-jsonpath 1.3.2__py3-none-any.whl → 2.0.1__py3-none-any.whl
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.
- jsonpath/__about__.py +1 -1
- jsonpath/__init__.py +290 -8
- jsonpath/_types.py +31 -0
- jsonpath/cli.py +11 -1
- jsonpath/env.py +100 -47
- jsonpath/exceptions.py +78 -7
- jsonpath/filter.py +78 -84
- jsonpath/function_extensions/__init__.py +4 -2
- jsonpath/function_extensions/_pattern.py +112 -0
- jsonpath/function_extensions/keys.py +27 -8
- jsonpath/function_extensions/match.py +11 -13
- jsonpath/function_extensions/search.py +11 -13
- jsonpath/function_extensions/starts_with.py +21 -0
- jsonpath/lex.py +113 -64
- jsonpath/lru_cache.py +130 -0
- jsonpath/match.py +13 -0
- jsonpath/parse.py +448 -302
- jsonpath/patch.py +8 -3
- jsonpath/path.py +48 -84
- jsonpath/pointer.py +52 -55
- jsonpath/segments.py +131 -0
- jsonpath/selectors.py +448 -482
- jsonpath/stream.py +68 -70
- jsonpath/token.py +59 -61
- jsonpath/unescape.py +134 -0
- {python_jsonpath-1.3.2.dist-info → python_jsonpath-2.0.1.dist-info}/METADATA +7 -3
- python_jsonpath-2.0.1.dist-info/RECORD +42 -0
- python_jsonpath-1.3.2.dist-info/RECORD +0 -36
- {python_jsonpath-1.3.2.dist-info → python_jsonpath-2.0.1.dist-info}/WHEEL +0 -0
- {python_jsonpath-1.3.2.dist-info → python_jsonpath-2.0.1.dist-info}/entry_points.txt +0 -0
- {python_jsonpath-1.3.2.dist-info → python_jsonpath-2.0.1.dist-info}/licenses/LICENSE.txt +0 -0
jsonpath/selectors.py
CHANGED
|
@@ -11,23 +11,22 @@ from typing import TYPE_CHECKING
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
from typing import AsyncIterable
|
|
13
13
|
from typing import Iterable
|
|
14
|
-
from typing import List
|
|
15
14
|
from typing import Optional
|
|
16
|
-
from typing import TypeVar
|
|
17
15
|
from typing import Union
|
|
18
16
|
|
|
19
17
|
from .exceptions import JSONPathIndexError
|
|
18
|
+
from .exceptions import JSONPathSyntaxError
|
|
20
19
|
from .exceptions import JSONPathTypeError
|
|
20
|
+
from .match import NodeList
|
|
21
21
|
from .serialize import canonical_string
|
|
22
22
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
24
|
from .env import JSONPathEnvironment
|
|
25
|
-
from .filter import
|
|
25
|
+
from .filter import FilterExpression
|
|
26
26
|
from .match import JSONPathMatch
|
|
27
|
+
from .path import JSONPath
|
|
27
28
|
from .token import Token
|
|
28
29
|
|
|
29
|
-
# ruff: noqa: D102
|
|
30
|
-
|
|
31
30
|
|
|
32
31
|
class JSONPathSelector(ABC):
|
|
33
32
|
"""Base class for all JSONPath segments and selectors."""
|
|
@@ -39,13 +38,11 @@ class JSONPathSelector(ABC):
|
|
|
39
38
|
self.token = token
|
|
40
39
|
|
|
41
40
|
@abstractmethod
|
|
42
|
-
def resolve(self,
|
|
41
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
43
42
|
"""Apply the segment/selector to each node in _matches_.
|
|
44
43
|
|
|
45
44
|
Arguments:
|
|
46
|
-
|
|
47
|
-
a lazy _NodeList_, as described in RFC 9535, but each match carries
|
|
48
|
-
more than the node's value and location.
|
|
45
|
+
node: A node matched by preceding segments/selectors.
|
|
49
46
|
|
|
50
47
|
Returns:
|
|
51
48
|
The `JSONPathMatch` instances created by applying this selector to each
|
|
@@ -53,39 +50,25 @@ class JSONPathSelector(ABC):
|
|
|
53
50
|
"""
|
|
54
51
|
|
|
55
52
|
@abstractmethod
|
|
56
|
-
def resolve_async(
|
|
57
|
-
self, matches: AsyncIterable[JSONPathMatch]
|
|
58
|
-
) -> AsyncIterable[JSONPathMatch]:
|
|
53
|
+
def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
59
54
|
"""An async version of `resolve`."""
|
|
60
55
|
|
|
61
56
|
|
|
62
|
-
class
|
|
63
|
-
"""
|
|
57
|
+
class NameSelector(JSONPathSelector):
|
|
58
|
+
"""Select at most one object member value given an object member name."""
|
|
64
59
|
|
|
65
|
-
__slots__ = ("name",
|
|
60
|
+
__slots__ = ("name",)
|
|
66
61
|
|
|
67
|
-
def __init__(
|
|
68
|
-
self,
|
|
69
|
-
*,
|
|
70
|
-
env: JSONPathEnvironment,
|
|
71
|
-
token: Token,
|
|
72
|
-
name: str,
|
|
73
|
-
shorthand: bool,
|
|
74
|
-
) -> None:
|
|
62
|
+
def __init__(self, *, env: JSONPathEnvironment, token: Token, name: str) -> None:
|
|
75
63
|
super().__init__(env=env, token=token)
|
|
76
64
|
self.name = name
|
|
77
|
-
self.shorthand = shorthand
|
|
78
65
|
|
|
79
66
|
def __str__(self) -> str:
|
|
80
|
-
return (
|
|
81
|
-
f"[{canonical_string(self.name)}]"
|
|
82
|
-
if self.shorthand
|
|
83
|
-
else f"{canonical_string(self.name)}"
|
|
84
|
-
)
|
|
67
|
+
return canonical_string(self.name)
|
|
85
68
|
|
|
86
69
|
def __eq__(self, __value: object) -> bool:
|
|
87
70
|
return (
|
|
88
|
-
isinstance(__value,
|
|
71
|
+
isinstance(__value, NameSelector)
|
|
89
72
|
and self.name == __value.name
|
|
90
73
|
and self.token == __value.token
|
|
91
74
|
)
|
|
@@ -93,50 +76,25 @@ class PropertySelector(JSONPathSelector):
|
|
|
93
76
|
def __hash__(self) -> int:
|
|
94
77
|
return hash((self.name, self.token))
|
|
95
78
|
|
|
96
|
-
def resolve(self,
|
|
97
|
-
|
|
98
|
-
if not isinstance(match.obj, Mapping):
|
|
99
|
-
continue
|
|
100
|
-
|
|
79
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
80
|
+
if isinstance(node.obj, Mapping):
|
|
101
81
|
with suppress(KeyError):
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
parent=match,
|
|
106
|
-
parts=match.parts + (self.name,),
|
|
107
|
-
path=match.path + f"[{canonical_string(self.name)}]",
|
|
108
|
-
root=match.root,
|
|
109
|
-
)
|
|
110
|
-
match.add_child(_match)
|
|
111
|
-
yield _match
|
|
112
|
-
|
|
113
|
-
async def resolve_async(
|
|
114
|
-
self, matches: AsyncIterable[JSONPathMatch]
|
|
115
|
-
) -> AsyncIterable[JSONPathMatch]:
|
|
116
|
-
async for match in matches:
|
|
117
|
-
if not isinstance(match.obj, Mapping):
|
|
118
|
-
continue
|
|
82
|
+
match = node.new_child(self.env.getitem(node.obj, self.name), self.name)
|
|
83
|
+
node.add_child(match)
|
|
84
|
+
yield match
|
|
119
85
|
|
|
86
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
87
|
+
if isinstance(node.obj, Mapping):
|
|
120
88
|
with suppress(KeyError):
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
obj=await self.env.getitem_async(match.obj, self.name),
|
|
124
|
-
parent=match,
|
|
125
|
-
parts=match.parts + (self.name,),
|
|
126
|
-
path=match.path + f"[{canonical_string(self.name)}]",
|
|
127
|
-
root=match.root,
|
|
89
|
+
match = node.new_child(
|
|
90
|
+
await self.env.getitem_async(node.obj, self.name), self.name
|
|
128
91
|
)
|
|
129
|
-
|
|
130
|
-
yield
|
|
92
|
+
node.add_child(match)
|
|
93
|
+
yield match
|
|
131
94
|
|
|
132
95
|
|
|
133
96
|
class IndexSelector(JSONPathSelector):
|
|
134
|
-
"""Select
|
|
135
|
-
|
|
136
|
-
Considering we don't require mapping (JSON object) keys/properties to
|
|
137
|
-
be quoted, and that we support mappings with numeric keys, we also check
|
|
138
|
-
to see if the "index" is a mapping key, which is non-standard.
|
|
139
|
-
"""
|
|
97
|
+
"""Select at most one array element value given an index."""
|
|
140
98
|
|
|
141
99
|
__slots__ = ("index", "_as_key")
|
|
142
100
|
|
|
@@ -172,122 +130,139 @@ class IndexSelector(JSONPathSelector):
|
|
|
172
130
|
return len(obj) + self.index
|
|
173
131
|
return self.index
|
|
174
132
|
|
|
175
|
-
def resolve(self,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
_match = self.env.match_class(
|
|
212
|
-
filter_context=match.filter_context(),
|
|
213
|
-
obj=await self.env.getitem_async(match.obj, self._as_key),
|
|
214
|
-
parent=match,
|
|
215
|
-
parts=match.parts + (self._as_key,),
|
|
216
|
-
path=f"{match.path}['{self.index}']",
|
|
217
|
-
root=match.root,
|
|
218
|
-
)
|
|
219
|
-
match.add_child(_match)
|
|
220
|
-
yield _match
|
|
221
|
-
elif isinstance(match.obj, Sequence) and not isinstance(match.obj, str):
|
|
222
|
-
norm_index = self._normalized_index(match.obj)
|
|
223
|
-
with suppress(IndexError):
|
|
224
|
-
_match = self.env.match_class(
|
|
225
|
-
filter_context=match.filter_context(),
|
|
226
|
-
obj=await self.env.getitem_async(match.obj, self.index),
|
|
227
|
-
parent=match,
|
|
228
|
-
parts=match.parts + (norm_index,),
|
|
229
|
-
path=match.path + f"[{norm_index}]",
|
|
230
|
-
root=match.root,
|
|
231
|
-
)
|
|
232
|
-
match.add_child(_match)
|
|
233
|
-
yield _match
|
|
133
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
134
|
+
# Optionally try string representation of int
|
|
135
|
+
if not self.env.strict and isinstance(node.obj, Mapping):
|
|
136
|
+
# Try the string representation of the index as a key.
|
|
137
|
+
with suppress(KeyError):
|
|
138
|
+
match = node.new_child(
|
|
139
|
+
self.env.getitem(node.obj, self._as_key), self.index
|
|
140
|
+
)
|
|
141
|
+
node.add_child(match)
|
|
142
|
+
yield match
|
|
143
|
+
if isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
144
|
+
norm_index = self._normalized_index(node.obj)
|
|
145
|
+
with suppress(IndexError):
|
|
146
|
+
match = node.new_child(
|
|
147
|
+
self.env.getitem(node.obj, self.index), norm_index
|
|
148
|
+
)
|
|
149
|
+
node.add_child(match)
|
|
150
|
+
yield match
|
|
151
|
+
|
|
152
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
153
|
+
if not self.env.strict and isinstance(node.obj, Mapping):
|
|
154
|
+
# Try the string representation of the index as a key.
|
|
155
|
+
with suppress(KeyError):
|
|
156
|
+
match = node.new_child(
|
|
157
|
+
await self.env.getitem_async(node.obj, self._as_key), self.index
|
|
158
|
+
)
|
|
159
|
+
node.add_child(match)
|
|
160
|
+
yield match
|
|
161
|
+
if isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
162
|
+
norm_index = self._normalized_index(node.obj)
|
|
163
|
+
with suppress(IndexError):
|
|
164
|
+
match = node.new_child(
|
|
165
|
+
await self.env.getitem_async(node.obj, self.index), norm_index
|
|
166
|
+
)
|
|
167
|
+
node.add_child(match)
|
|
168
|
+
yield match
|
|
234
169
|
|
|
235
170
|
|
|
236
|
-
class
|
|
237
|
-
"""Select
|
|
171
|
+
class KeySelector(JSONPathSelector):
|
|
172
|
+
"""Select at most one name from an object member, given the name.
|
|
173
|
+
|
|
174
|
+
The key selector is introduced to facilitate valid normalized paths for nodes
|
|
175
|
+
produced by the "keys selector" and the "keys filter selector". It is not expected
|
|
176
|
+
to be of much use elsewhere.
|
|
238
177
|
|
|
239
178
|
NOTE: This is a non-standard selector.
|
|
179
|
+
|
|
180
|
+
See https://jg-rp.github.io/json-p3/guides/jsonpath-extra#key-selector.
|
|
240
181
|
"""
|
|
241
182
|
|
|
242
|
-
__slots__ = ("
|
|
183
|
+
__slots__ = ("key",)
|
|
243
184
|
|
|
244
|
-
def __init__(
|
|
245
|
-
self, *, env: JSONPathEnvironment, token: Token, shorthand: bool
|
|
246
|
-
) -> None:
|
|
185
|
+
def __init__(self, *, env: JSONPathEnvironment, token: Token, key: str) -> None:
|
|
247
186
|
super().__init__(env=env, token=token)
|
|
248
|
-
self.
|
|
187
|
+
self.key = key
|
|
249
188
|
|
|
250
189
|
def __str__(self) -> str:
|
|
190
|
+
return f"{self.env.keys_selector_token}{canonical_string(self.key)}"
|
|
191
|
+
|
|
192
|
+
def __eq__(self, __value: object) -> bool:
|
|
251
193
|
return (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
194
|
+
isinstance(__value, KeySelector)
|
|
195
|
+
and self.token == __value.token
|
|
196
|
+
and self.key == __value.key
|
|
255
197
|
)
|
|
256
198
|
|
|
199
|
+
def __hash__(self) -> int:
|
|
200
|
+
return hash((self.token, self.key))
|
|
201
|
+
|
|
202
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
203
|
+
if isinstance(node.obj, Mapping) and self.key in node.obj:
|
|
204
|
+
match = node.__class__(
|
|
205
|
+
filter_context=node.filter_context(),
|
|
206
|
+
obj=self.key,
|
|
207
|
+
parent=node,
|
|
208
|
+
parts=node.parts + (f"{self.env.keys_selector_token}{self.key}",),
|
|
209
|
+
path=f"{node.path}[{self}]",
|
|
210
|
+
root=node.root,
|
|
211
|
+
)
|
|
212
|
+
node.add_child(match)
|
|
213
|
+
yield match
|
|
214
|
+
|
|
215
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
216
|
+
for _node in self.resolve(node):
|
|
217
|
+
yield _node
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class KeysSelector(JSONPathSelector):
|
|
221
|
+
"""Select all names from an object's name/value members.
|
|
222
|
+
|
|
223
|
+
NOTE: This is a non-standard selector.
|
|
224
|
+
|
|
225
|
+
See https://jg-rp.github.io/json-p3/guides/jsonpath-extra#keys-selector
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
__slots__ = ()
|
|
229
|
+
|
|
230
|
+
def __init__(self, *, env: JSONPathEnvironment, token: Token) -> None:
|
|
231
|
+
super().__init__(env=env, token=token)
|
|
232
|
+
|
|
233
|
+
def __str__(self) -> str:
|
|
234
|
+
return self.env.keys_selector_token
|
|
235
|
+
|
|
257
236
|
def __eq__(self, __value: object) -> bool:
|
|
258
237
|
return isinstance(__value, KeysSelector) and self.token == __value.token
|
|
259
238
|
|
|
260
239
|
def __hash__(self) -> int:
|
|
261
240
|
return hash(self.token)
|
|
262
241
|
|
|
263
|
-
def _keys(self,
|
|
264
|
-
if isinstance(
|
|
265
|
-
for
|
|
266
|
-
|
|
267
|
-
filter_context=
|
|
242
|
+
def _keys(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
243
|
+
if isinstance(node.obj, Mapping):
|
|
244
|
+
for key in node.obj:
|
|
245
|
+
match = node.__class__(
|
|
246
|
+
filter_context=node.filter_context(),
|
|
268
247
|
obj=key,
|
|
269
|
-
parent=
|
|
270
|
-
parts=
|
|
271
|
-
path=f"{
|
|
272
|
-
root=
|
|
248
|
+
parent=node,
|
|
249
|
+
parts=node.parts + (f"{self.env.keys_selector_token}{key}",),
|
|
250
|
+
path=f"{node.path}[{self.env.keys_selector_token}{canonical_string(key)}]",
|
|
251
|
+
root=node.root,
|
|
273
252
|
)
|
|
274
|
-
|
|
275
|
-
yield
|
|
253
|
+
node.add_child(match)
|
|
254
|
+
yield match
|
|
276
255
|
|
|
277
|
-
def resolve(self,
|
|
278
|
-
|
|
279
|
-
yield from self._keys(match)
|
|
256
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
257
|
+
yield from self._keys(node)
|
|
280
258
|
|
|
281
|
-
async def resolve_async(
|
|
282
|
-
self
|
|
283
|
-
|
|
284
|
-
async for match in matches:
|
|
285
|
-
for _match in self._keys(match):
|
|
286
|
-
yield _match
|
|
259
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
260
|
+
for match in self._keys(node):
|
|
261
|
+
yield match
|
|
287
262
|
|
|
288
263
|
|
|
289
264
|
class SliceSelector(JSONPathSelector):
|
|
290
|
-
"""
|
|
265
|
+
"""Select array elements given a start index, a stop index and a step."""
|
|
291
266
|
|
|
292
267
|
__slots__ = ("slice",)
|
|
293
268
|
|
|
@@ -327,258 +302,191 @@ class SliceSelector(JSONPathSelector):
|
|
|
327
302
|
):
|
|
328
303
|
raise JSONPathIndexError("index out of range", token=self.token)
|
|
329
304
|
|
|
330
|
-
def resolve(self,
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
continue
|
|
305
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
306
|
+
if not isinstance(node.obj, Sequence) or self.slice.step == 0:
|
|
307
|
+
return
|
|
334
308
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
parent=match,
|
|
343
|
-
parts=match.parts + (norm_index,),
|
|
344
|
-
path=f"{match.path}[{norm_index}]",
|
|
345
|
-
root=match.root,
|
|
346
|
-
)
|
|
347
|
-
match.add_child(_match)
|
|
348
|
-
yield _match
|
|
349
|
-
|
|
350
|
-
async def resolve_async(
|
|
351
|
-
self, matches: AsyncIterable[JSONPathMatch]
|
|
352
|
-
) -> AsyncIterable[JSONPathMatch]:
|
|
353
|
-
async for match in matches:
|
|
354
|
-
if not isinstance(match.obj, Sequence) or self.slice.step == 0:
|
|
355
|
-
continue
|
|
356
|
-
|
|
357
|
-
for norm_index, obj in zip( # noqa: B905
|
|
358
|
-
range(*self.slice.indices(len(match.obj))),
|
|
359
|
-
await self.env.getitem_async(match.obj, self.slice),
|
|
360
|
-
):
|
|
361
|
-
_match = self.env.match_class(
|
|
362
|
-
filter_context=match.filter_context(),
|
|
363
|
-
obj=obj,
|
|
364
|
-
parent=match,
|
|
365
|
-
parts=match.parts + (norm_index,),
|
|
366
|
-
path=f"{match.path}[{norm_index}]",
|
|
367
|
-
root=match.root,
|
|
368
|
-
)
|
|
369
|
-
match.add_child(_match)
|
|
370
|
-
yield _match
|
|
309
|
+
for norm_index, obj in zip( # noqa: B905
|
|
310
|
+
range(*self.slice.indices(len(node.obj))),
|
|
311
|
+
self.env.getitem(node.obj, self.slice),
|
|
312
|
+
):
|
|
313
|
+
match = node.new_child(obj, norm_index)
|
|
314
|
+
node.add_child(match)
|
|
315
|
+
yield match
|
|
371
316
|
|
|
317
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
318
|
+
if not isinstance(node.obj, Sequence) or self.slice.step == 0:
|
|
319
|
+
return
|
|
372
320
|
|
|
373
|
-
|
|
374
|
-
|
|
321
|
+
for norm_index, obj in zip( # noqa: B905
|
|
322
|
+
range(*self.slice.indices(len(node.obj))),
|
|
323
|
+
await self.env.getitem_async(node.obj, self.slice),
|
|
324
|
+
):
|
|
325
|
+
match = node.new_child(obj, norm_index)
|
|
326
|
+
node.add_child(match)
|
|
327
|
+
yield match
|
|
375
328
|
|
|
376
|
-
__slots__ = ("shorthand",)
|
|
377
329
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
self.shorthand = shorthand
|
|
330
|
+
class WildcardSelector(JSONPathSelector):
|
|
331
|
+
"""Select nodes of all children of an object or array."""
|
|
332
|
+
|
|
333
|
+
__slots__ = ()
|
|
383
334
|
|
|
384
335
|
def __str__(self) -> str:
|
|
385
|
-
return "
|
|
336
|
+
return "*"
|
|
386
337
|
|
|
387
338
|
def __eq__(self, __value: object) -> bool:
|
|
388
|
-
return isinstance(__value,
|
|
339
|
+
return isinstance(__value, WildcardSelector) and self.token == __value.token
|
|
389
340
|
|
|
390
341
|
def __hash__(self) -> int:
|
|
391
342
|
return hash(self.token)
|
|
392
343
|
|
|
393
|
-
def resolve(self,
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
for key, val in match.obj.items():
|
|
428
|
-
_match = self.env.match_class(
|
|
429
|
-
filter_context=match.filter_context(),
|
|
430
|
-
obj=val,
|
|
431
|
-
parent=match,
|
|
432
|
-
parts=match.parts + (key,),
|
|
433
|
-
path=match.path + f"[{canonical_string(key)}]",
|
|
434
|
-
root=match.root,
|
|
435
|
-
)
|
|
436
|
-
match.add_child(_match)
|
|
437
|
-
yield _match
|
|
438
|
-
elif isinstance(match.obj, Sequence):
|
|
439
|
-
for i, val in enumerate(match.obj):
|
|
440
|
-
_match = self.env.match_class(
|
|
441
|
-
filter_context=match.filter_context(),
|
|
442
|
-
obj=val,
|
|
443
|
-
parent=match,
|
|
444
|
-
parts=match.parts + (i,),
|
|
445
|
-
path=f"{match.path}[{i}]",
|
|
446
|
-
root=match.root,
|
|
447
|
-
)
|
|
448
|
-
match.add_child(_match)
|
|
449
|
-
yield _match
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
class RecursiveDescentSelector(JSONPathSelector):
|
|
453
|
-
"""A JSONPath selector that visits all nodes recursively.
|
|
454
|
-
|
|
455
|
-
NOTE: Strictly this is a "segment", not a "selector".
|
|
344
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
345
|
+
if isinstance(node.obj, Mapping):
|
|
346
|
+
for key, val in node.obj.items():
|
|
347
|
+
match = node.new_child(val, key)
|
|
348
|
+
node.add_child(match)
|
|
349
|
+
yield match
|
|
350
|
+
|
|
351
|
+
elif isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
352
|
+
for i, val in enumerate(node.obj):
|
|
353
|
+
match = node.new_child(val, i)
|
|
354
|
+
node.add_child(match)
|
|
355
|
+
yield match
|
|
356
|
+
|
|
357
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
358
|
+
if isinstance(node.obj, Mapping):
|
|
359
|
+
for key, val in node.obj.items():
|
|
360
|
+
match = node.new_child(val, key)
|
|
361
|
+
node.add_child(match)
|
|
362
|
+
yield match
|
|
363
|
+
|
|
364
|
+
elif isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
365
|
+
for i, val in enumerate(node.obj):
|
|
366
|
+
match = node.new_child(val, i)
|
|
367
|
+
node.add_child(match)
|
|
368
|
+
yield match
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class SingularQuerySelector(JSONPathSelector):
|
|
372
|
+
"""An embedded absolute query.
|
|
373
|
+
|
|
374
|
+
The result of the embedded query is used as an object member name or array element
|
|
375
|
+
index.
|
|
376
|
+
|
|
377
|
+
NOTE: This is a non-standard selector.
|
|
456
378
|
"""
|
|
457
379
|
|
|
380
|
+
__slots__ = ("query",)
|
|
381
|
+
|
|
382
|
+
def __init__(
|
|
383
|
+
self, *, env: JSONPathEnvironment, token: Token, query: JSONPath
|
|
384
|
+
) -> None:
|
|
385
|
+
super().__init__(env=env, token=token)
|
|
386
|
+
self.query = query
|
|
387
|
+
|
|
388
|
+
if env.strict:
|
|
389
|
+
raise JSONPathSyntaxError("unexpected query selector", token=token)
|
|
390
|
+
|
|
458
391
|
def __str__(self) -> str:
|
|
459
|
-
return
|
|
392
|
+
return str(self.query)
|
|
460
393
|
|
|
461
394
|
def __eq__(self, __value: object) -> bool:
|
|
462
395
|
return (
|
|
463
|
-
isinstance(__value,
|
|
396
|
+
isinstance(__value, SingularQuerySelector)
|
|
397
|
+
and self.query == __value.query
|
|
464
398
|
and self.token == __value.token
|
|
465
399
|
)
|
|
466
400
|
|
|
467
401
|
def __hash__(self) -> int:
|
|
468
|
-
return hash(self.token)
|
|
402
|
+
return hash((self.query, self.token))
|
|
469
403
|
|
|
470
|
-
def
|
|
471
|
-
if isinstance(
|
|
472
|
-
|
|
473
|
-
if isinstance(val, str):
|
|
474
|
-
pass
|
|
475
|
-
elif isinstance(val, (Mapping, Sequence)):
|
|
476
|
-
_match = self.env.match_class(
|
|
477
|
-
filter_context=match.filter_context(),
|
|
478
|
-
obj=val,
|
|
479
|
-
parent=match,
|
|
480
|
-
parts=match.parts + (key,),
|
|
481
|
-
path=match.path + f"[{canonical_string(key)}]",
|
|
482
|
-
root=match.root,
|
|
483
|
-
)
|
|
484
|
-
match.add_child(_match)
|
|
485
|
-
yield _match
|
|
486
|
-
yield from self._expand(_match)
|
|
487
|
-
elif isinstance(match.obj, Sequence) and not isinstance(match.obj, str):
|
|
488
|
-
for i, val in enumerate(match.obj):
|
|
489
|
-
if isinstance(val, str):
|
|
490
|
-
pass
|
|
491
|
-
elif isinstance(val, (Mapping, Sequence)):
|
|
492
|
-
_match = self.env.match_class(
|
|
493
|
-
filter_context=match.filter_context(),
|
|
494
|
-
obj=val,
|
|
495
|
-
parent=match,
|
|
496
|
-
parts=match.parts + (i,),
|
|
497
|
-
path=f"{match.path}[{i}]",
|
|
498
|
-
root=match.root,
|
|
499
|
-
)
|
|
500
|
-
match.add_child(_match)
|
|
501
|
-
yield _match
|
|
502
|
-
yield from self._expand(_match)
|
|
503
|
-
|
|
504
|
-
def resolve(self, matches: Iterable[JSONPathMatch]) -> Iterable[JSONPathMatch]:
|
|
505
|
-
for match in matches:
|
|
506
|
-
yield match
|
|
507
|
-
yield from self._expand(match)
|
|
404
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
405
|
+
if isinstance(node.obj, Mapping):
|
|
406
|
+
nodes = NodeList(self.query.finditer(node.root))
|
|
508
407
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
) -> AsyncIterable[JSONPathMatch]:
|
|
512
|
-
async for match in matches:
|
|
513
|
-
yield match
|
|
514
|
-
for _match in self._expand(match):
|
|
515
|
-
yield _match
|
|
408
|
+
if nodes.empty():
|
|
409
|
+
return
|
|
516
410
|
|
|
411
|
+
value = nodes[0].value
|
|
517
412
|
|
|
518
|
-
|
|
413
|
+
if not isinstance(value, str):
|
|
414
|
+
return
|
|
519
415
|
|
|
416
|
+
with suppress(KeyError):
|
|
417
|
+
match = node.new_child(self.env.getitem(node.obj, value), value)
|
|
418
|
+
node.add_child(match)
|
|
419
|
+
yield match
|
|
520
420
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
yield item
|
|
421
|
+
if isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
422
|
+
nodes = NodeList(self.query.finditer(node.root))
|
|
524
423
|
|
|
424
|
+
if nodes.empty():
|
|
425
|
+
return
|
|
525
426
|
|
|
526
|
-
|
|
527
|
-
"""A bracketed list of selectors, the results of which are concatenated together.
|
|
427
|
+
value = nodes[0].value
|
|
528
428
|
|
|
529
|
-
|
|
530
|
-
|
|
429
|
+
if not isinstance(value, int):
|
|
430
|
+
return
|
|
531
431
|
|
|
532
|
-
|
|
432
|
+
index = self._normalized_index(node.obj, value)
|
|
533
433
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
token: Token,
|
|
539
|
-
items: List[
|
|
540
|
-
Union[
|
|
541
|
-
SliceSelector,
|
|
542
|
-
KeysSelector,
|
|
543
|
-
IndexSelector,
|
|
544
|
-
PropertySelector,
|
|
545
|
-
WildSelector,
|
|
546
|
-
Filter,
|
|
547
|
-
]
|
|
548
|
-
],
|
|
549
|
-
) -> None:
|
|
550
|
-
super().__init__(env=env, token=token)
|
|
551
|
-
self.items = tuple(items)
|
|
434
|
+
with suppress(IndexError):
|
|
435
|
+
match = node.new_child(self.env.getitem(node.obj, index), index)
|
|
436
|
+
node.add_child(match)
|
|
437
|
+
yield match
|
|
552
438
|
|
|
553
|
-
def
|
|
554
|
-
|
|
439
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
440
|
+
if isinstance(node.obj, Mapping):
|
|
441
|
+
nodes = NodeList(
|
|
442
|
+
[match async for match in await self.query.finditer_async(node.root)]
|
|
443
|
+
)
|
|
555
444
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
isinstance(__value, ListSelector)
|
|
559
|
-
and self.items == __value.items
|
|
560
|
-
and self.token == __value.token
|
|
561
|
-
)
|
|
445
|
+
if nodes.empty():
|
|
446
|
+
return
|
|
562
447
|
|
|
563
|
-
|
|
564
|
-
|
|
448
|
+
value = nodes[0].value
|
|
449
|
+
|
|
450
|
+
if not isinstance(value, str):
|
|
451
|
+
return
|
|
452
|
+
|
|
453
|
+
with suppress(KeyError):
|
|
454
|
+
match = node.new_child(
|
|
455
|
+
await self.env.getitem_async(node.obj, value), value
|
|
456
|
+
)
|
|
457
|
+
node.add_child(match)
|
|
458
|
+
yield match
|
|
459
|
+
|
|
460
|
+
if isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
461
|
+
nodes = NodeList(
|
|
462
|
+
[match async for match in await self.query.finditer_async(node.root)]
|
|
463
|
+
)
|
|
565
464
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
for item in self.items:
|
|
569
|
-
yield from item.resolve([match_])
|
|
465
|
+
if nodes.empty():
|
|
466
|
+
return
|
|
570
467
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
468
|
+
value = nodes[0].value
|
|
469
|
+
|
|
470
|
+
if not isinstance(value, int):
|
|
471
|
+
return
|
|
472
|
+
|
|
473
|
+
index = self._normalized_index(node.obj, value)
|
|
474
|
+
|
|
475
|
+
with suppress(IndexError):
|
|
476
|
+
match = node.new_child(
|
|
477
|
+
await self.env.getitem_async(node.obj, index), index
|
|
478
|
+
)
|
|
479
|
+
node.add_child(match)
|
|
480
|
+
yield match
|
|
481
|
+
|
|
482
|
+
def _normalized_index(self, obj: Sequence[object], index: int) -> int:
|
|
483
|
+
if index < 0 and len(obj) >= abs(index):
|
|
484
|
+
return len(obj) + index
|
|
485
|
+
return index
|
|
578
486
|
|
|
579
487
|
|
|
580
488
|
class Filter(JSONPathSelector):
|
|
581
|
-
"""
|
|
489
|
+
"""Select array elements or object values according to a filter expression."""
|
|
582
490
|
|
|
583
491
|
__slots__ = ("expression", "cacheable_nodes")
|
|
584
492
|
|
|
@@ -587,7 +495,7 @@ class Filter(JSONPathSelector):
|
|
|
587
495
|
*,
|
|
588
496
|
env: JSONPathEnvironment,
|
|
589
497
|
token: Token,
|
|
590
|
-
expression:
|
|
498
|
+
expression: FilterExpression,
|
|
591
499
|
) -> None:
|
|
592
500
|
super().__init__(env=env, token=token)
|
|
593
501
|
self.expression = expression
|
|
@@ -607,132 +515,190 @@ class Filter(JSONPathSelector):
|
|
|
607
515
|
def __hash__(self) -> int:
|
|
608
516
|
return hash((str(self.expression), self.token))
|
|
609
517
|
|
|
610
|
-
def resolve(
|
|
611
|
-
self, matches: Iterable[JSONPathMatch]
|
|
612
|
-
) -> Iterable[JSONPathMatch]:
|
|
518
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
613
519
|
if self.cacheable_nodes and self.env.filter_caching:
|
|
614
520
|
expr = self.expression.cache_tree()
|
|
615
521
|
else:
|
|
616
522
|
expr = self.expression
|
|
617
523
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
filter_context=match.filter_context(),
|
|
658
|
-
obj=obj,
|
|
659
|
-
parent=match,
|
|
660
|
-
parts=match.parts + (i,),
|
|
661
|
-
path=f"{match.path}[{i}]",
|
|
662
|
-
root=match.root,
|
|
663
|
-
)
|
|
664
|
-
match.add_child(_match)
|
|
665
|
-
yield _match
|
|
666
|
-
except JSONPathTypeError as err:
|
|
667
|
-
if not err.token:
|
|
668
|
-
err.token = self.token
|
|
669
|
-
raise
|
|
670
|
-
|
|
671
|
-
async def resolve_async( # noqa: PLR0912
|
|
672
|
-
self, matches: AsyncIterable[JSONPathMatch]
|
|
673
|
-
) -> AsyncIterable[JSONPathMatch]:
|
|
524
|
+
if isinstance(node.obj, Mapping):
|
|
525
|
+
for key, val in node.obj.items():
|
|
526
|
+
context = FilterContext(
|
|
527
|
+
env=self.env,
|
|
528
|
+
current=val,
|
|
529
|
+
root=node.root,
|
|
530
|
+
extra_context=node.filter_context(),
|
|
531
|
+
current_key=key,
|
|
532
|
+
)
|
|
533
|
+
try:
|
|
534
|
+
if expr.evaluate(context):
|
|
535
|
+
match = node.new_child(val, key)
|
|
536
|
+
node.add_child(match)
|
|
537
|
+
yield match
|
|
538
|
+
except JSONPathTypeError as err:
|
|
539
|
+
if not err.token:
|
|
540
|
+
err.token = self.token
|
|
541
|
+
raise
|
|
542
|
+
|
|
543
|
+
elif isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
544
|
+
for i, obj in enumerate(node.obj):
|
|
545
|
+
context = FilterContext(
|
|
546
|
+
env=self.env,
|
|
547
|
+
current=obj,
|
|
548
|
+
root=node.root,
|
|
549
|
+
extra_context=node.filter_context(),
|
|
550
|
+
current_key=i,
|
|
551
|
+
)
|
|
552
|
+
try:
|
|
553
|
+
if expr.evaluate(context):
|
|
554
|
+
match = node.new_child(obj, i)
|
|
555
|
+
node.add_child(match)
|
|
556
|
+
yield match
|
|
557
|
+
except JSONPathTypeError as err:
|
|
558
|
+
if not err.token:
|
|
559
|
+
err.token = self.token
|
|
560
|
+
raise
|
|
561
|
+
|
|
562
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
674
563
|
if self.cacheable_nodes and self.env.filter_caching:
|
|
675
564
|
expr = self.expression.cache_tree()
|
|
676
565
|
else:
|
|
677
566
|
expr = self.expression
|
|
678
567
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
568
|
+
if isinstance(node.obj, Mapping):
|
|
569
|
+
for key, val in node.obj.items():
|
|
570
|
+
context = FilterContext(
|
|
571
|
+
env=self.env,
|
|
572
|
+
current=val,
|
|
573
|
+
root=node.root,
|
|
574
|
+
extra_context=node.filter_context(),
|
|
575
|
+
current_key=key,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
try:
|
|
579
|
+
result = await expr.evaluate_async(context)
|
|
580
|
+
except JSONPathTypeError as err:
|
|
581
|
+
if not err.token:
|
|
582
|
+
err.token = self.token
|
|
583
|
+
raise
|
|
584
|
+
|
|
585
|
+
if result:
|
|
586
|
+
match = node.new_child(val, key)
|
|
587
|
+
node.add_child(match)
|
|
588
|
+
yield match
|
|
589
|
+
|
|
590
|
+
elif isinstance(node.obj, Sequence) and not isinstance(node.obj, str):
|
|
591
|
+
for i, obj in enumerate(node.obj):
|
|
592
|
+
context = FilterContext(
|
|
593
|
+
env=self.env,
|
|
594
|
+
current=obj,
|
|
595
|
+
root=node.root,
|
|
596
|
+
extra_context=node.filter_context(),
|
|
597
|
+
current_key=i,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
try:
|
|
601
|
+
result = await expr.evaluate_async(context)
|
|
602
|
+
except JSONPathTypeError as err:
|
|
603
|
+
if not err.token:
|
|
604
|
+
err.token = self.token
|
|
605
|
+
raise
|
|
606
|
+
if result:
|
|
607
|
+
match = node.new_child(obj, i)
|
|
608
|
+
node.add_child(match)
|
|
609
|
+
yield match
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
class KeysFilter(JSONPathSelector):
|
|
613
|
+
"""Selects names from an object's name/value members.
|
|
614
|
+
|
|
615
|
+
NOTE: This is a non-standard selector.
|
|
616
|
+
|
|
617
|
+
See https://jg-rp.github.io/json-p3/guides/jsonpath-extra#keys-filter-selector
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
__slots__ = ("expression",)
|
|
621
|
+
|
|
622
|
+
def __init__(
|
|
623
|
+
self,
|
|
624
|
+
*,
|
|
625
|
+
env: JSONPathEnvironment,
|
|
626
|
+
token: Token,
|
|
627
|
+
expression: FilterExpression,
|
|
628
|
+
) -> None:
|
|
629
|
+
super().__init__(env=env, token=token)
|
|
630
|
+
self.expression = expression
|
|
631
|
+
|
|
632
|
+
def __str__(self) -> str:
|
|
633
|
+
return f"~?{self.expression}"
|
|
634
|
+
|
|
635
|
+
def __eq__(self, __value: object) -> bool:
|
|
636
|
+
return (
|
|
637
|
+
isinstance(__value, Filter)
|
|
638
|
+
and self.expression == __value.expression
|
|
639
|
+
and self.token == __value.token
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
def __hash__(self) -> int:
|
|
643
|
+
return hash(("~", str(self.expression), self.token))
|
|
644
|
+
|
|
645
|
+
def resolve(self, node: JSONPathMatch) -> Iterable[JSONPathMatch]:
|
|
646
|
+
if isinstance(node.value, Mapping):
|
|
647
|
+
for key, val in node.value.items():
|
|
648
|
+
context = FilterContext(
|
|
649
|
+
env=self.env,
|
|
650
|
+
current=val,
|
|
651
|
+
root=node.root,
|
|
652
|
+
extra_context=node.filter_context(),
|
|
653
|
+
current_key=key,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
try:
|
|
657
|
+
if self.expression.evaluate(context):
|
|
658
|
+
match = node.__class__(
|
|
659
|
+
filter_context=node.filter_context(),
|
|
660
|
+
obj=key,
|
|
661
|
+
parent=node,
|
|
662
|
+
parts=node.parts
|
|
663
|
+
+ (f"{self.env.keys_selector_token}{key}",),
|
|
664
|
+
path=f"{node.path}[{self.env.keys_selector_token}{canonical_string(key)}]",
|
|
665
|
+
root=node.root,
|
|
705
666
|
)
|
|
706
|
-
|
|
707
|
-
yield
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if
|
|
726
|
-
|
|
727
|
-
filter_context=
|
|
728
|
-
obj=
|
|
729
|
-
parent=
|
|
730
|
-
parts=
|
|
731
|
-
|
|
732
|
-
|
|
667
|
+
node.add_child(match)
|
|
668
|
+
yield match
|
|
669
|
+
except JSONPathTypeError as err:
|
|
670
|
+
if not err.token:
|
|
671
|
+
err.token = self.token
|
|
672
|
+
raise
|
|
673
|
+
|
|
674
|
+
async def resolve_async(self, node: JSONPathMatch) -> AsyncIterable[JSONPathMatch]:
|
|
675
|
+
if isinstance(node.value, Mapping):
|
|
676
|
+
for key, val in node.value.items():
|
|
677
|
+
context = FilterContext(
|
|
678
|
+
env=self.env,
|
|
679
|
+
current=val,
|
|
680
|
+
root=node.root,
|
|
681
|
+
extra_context=node.filter_context(),
|
|
682
|
+
current_key=key,
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
try:
|
|
686
|
+
if await self.expression.evaluate_async(context):
|
|
687
|
+
match = node.__class__(
|
|
688
|
+
filter_context=node.filter_context(),
|
|
689
|
+
obj=key,
|
|
690
|
+
parent=node,
|
|
691
|
+
parts=node.parts
|
|
692
|
+
+ (f"{self.env.keys_selector_token}{key}",),
|
|
693
|
+
path=f"{node.path}[{self.env.keys_selector_token}{canonical_string(key)}]",
|
|
694
|
+
root=node.root,
|
|
733
695
|
)
|
|
734
|
-
|
|
735
|
-
yield
|
|
696
|
+
node.add_child(match)
|
|
697
|
+
yield match
|
|
698
|
+
except JSONPathTypeError as err:
|
|
699
|
+
if not err.token:
|
|
700
|
+
err.token = self.token
|
|
701
|
+
raise
|
|
736
702
|
|
|
737
703
|
|
|
738
704
|
class FilterContext:
|