python-jsonpath 1.2.2__py3-none-any.whl → 1.3.0__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 +2 -0
- jsonpath/filter.py +36 -3
- jsonpath/match.py +5 -0
- jsonpath/selectors.py +22 -23
- jsonpath/serialize.py +13 -0
- {python_jsonpath-1.2.2.dist-info → python_jsonpath-1.3.0.dist-info}/METADATA +4 -3
- {python_jsonpath-1.2.2.dist-info → python_jsonpath-1.3.0.dist-info}/RECORD +11 -10
- {python_jsonpath-1.2.2.dist-info → python_jsonpath-1.3.0.dist-info}/WHEEL +1 -1
- {python_jsonpath-1.2.2.dist-info → python_jsonpath-1.3.0.dist-info}/entry_points.txt +0 -0
- {python_jsonpath-1.2.2.dist-info → python_jsonpath-1.3.0.dist-info}/licenses/LICENSE.txt +0 -0
jsonpath/__about__.py
CHANGED
jsonpath/__init__.py
CHANGED
|
@@ -24,6 +24,7 @@ from .fluent_api import Projection
|
|
|
24
24
|
from .fluent_api import Query
|
|
25
25
|
from .lex import Lexer
|
|
26
26
|
from .match import JSONPathMatch
|
|
27
|
+
from .match import NodeList
|
|
27
28
|
from .parse import Parser
|
|
28
29
|
from .patch import JSONPatch
|
|
29
30
|
from .path import CompoundJSONPath
|
|
@@ -58,6 +59,7 @@ __all__ = (
|
|
|
58
59
|
"JSONPointerResolutionError",
|
|
59
60
|
"JSONPointerTypeError",
|
|
60
61
|
"Lexer",
|
|
62
|
+
"NodeList",
|
|
61
63
|
"match",
|
|
62
64
|
"Parser",
|
|
63
65
|
"Projection",
|
jsonpath/filter.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import copy
|
|
6
|
-
import json
|
|
7
6
|
import re
|
|
8
7
|
from abc import ABC
|
|
9
8
|
from abc import abstractmethod
|
|
@@ -25,6 +24,7 @@ from .function_extensions import FilterFunction
|
|
|
25
24
|
from .match import NodeList
|
|
26
25
|
from .selectors import Filter as FilterSelector
|
|
27
26
|
from .selectors import ListSelector
|
|
27
|
+
from .serialize import canonical_string
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
30
|
from .path import JSONPath
|
|
@@ -208,7 +208,7 @@ class StringLiteral(Literal[str]):
|
|
|
208
208
|
__slots__ = ()
|
|
209
209
|
|
|
210
210
|
def __str__(self) -> str:
|
|
211
|
-
return
|
|
211
|
+
return canonical_string(self.value)
|
|
212
212
|
|
|
213
213
|
|
|
214
214
|
class IntegerLiteral(Literal[int]):
|
|
@@ -375,6 +375,12 @@ class InfixExpression(FilterExpression):
|
|
|
375
375
|
self.right = children[1]
|
|
376
376
|
|
|
377
377
|
|
|
378
|
+
PRECEDENCE_LOWEST = 1
|
|
379
|
+
PRECEDENCE_LOGICAL_OR = 3
|
|
380
|
+
PRECEDENCE_LOGICAL_AND = 4
|
|
381
|
+
PRECEDENCE_PREFIX = 7
|
|
382
|
+
|
|
383
|
+
|
|
378
384
|
class BooleanExpression(FilterExpression):
|
|
379
385
|
"""An expression that always evaluates to `True` or `False`."""
|
|
380
386
|
|
|
@@ -408,13 +414,40 @@ class BooleanExpression(FilterExpression):
|
|
|
408
414
|
)
|
|
409
415
|
|
|
410
416
|
def __str__(self) -> str:
|
|
411
|
-
return
|
|
417
|
+
return self._canonical_string(self.expression, PRECEDENCE_LOWEST)
|
|
412
418
|
|
|
413
419
|
def __eq__(self, other: object) -> bool:
|
|
414
420
|
return (
|
|
415
421
|
isinstance(other, BooleanExpression) and self.expression == other.expression
|
|
416
422
|
)
|
|
417
423
|
|
|
424
|
+
def _canonical_string(
|
|
425
|
+
self, expression: FilterExpression, parent_precedence: int
|
|
426
|
+
) -> str:
|
|
427
|
+
if isinstance(expression, InfixExpression):
|
|
428
|
+
if expression.operator == "&&":
|
|
429
|
+
left = self._canonical_string(expression.left, PRECEDENCE_LOGICAL_AND)
|
|
430
|
+
right = self._canonical_string(expression.right, PRECEDENCE_LOGICAL_AND)
|
|
431
|
+
expr = f"{left} && {right}"
|
|
432
|
+
return (
|
|
433
|
+
f"({expr})" if parent_precedence >= PRECEDENCE_LOGICAL_AND else expr
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
if expression.operator == "||":
|
|
437
|
+
left = self._canonical_string(expression.left, PRECEDENCE_LOGICAL_OR)
|
|
438
|
+
right = self._canonical_string(expression.right, PRECEDENCE_LOGICAL_OR)
|
|
439
|
+
expr = f"{left} || {right}"
|
|
440
|
+
return (
|
|
441
|
+
f"({expr})" if parent_precedence >= PRECEDENCE_LOGICAL_OR else expr
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
if isinstance(expression, PrefixExpression):
|
|
445
|
+
operand = self._canonical_string(expression.right, PRECEDENCE_PREFIX)
|
|
446
|
+
expr = f"!{operand}"
|
|
447
|
+
return f"({expr})" if parent_precedence > PRECEDENCE_PREFIX else expr
|
|
448
|
+
|
|
449
|
+
return str(expression)
|
|
450
|
+
|
|
418
451
|
def evaluate(self, context: FilterContext) -> bool:
|
|
419
452
|
return context.env.is_truthy(self.expression.evaluate(context))
|
|
420
453
|
|
jsonpath/match.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""The JSONPath match object, as returned from `JSONPath.finditer()`."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import Any
|
|
@@ -104,6 +105,10 @@ class NodeList(List[JSONPathMatch]):
|
|
|
104
105
|
return self[0].obj
|
|
105
106
|
return [match.obj for match in self]
|
|
106
107
|
|
|
108
|
+
def paths(self) -> List[str]:
|
|
109
|
+
"""Return a normalized path for each node in this node list."""
|
|
110
|
+
return [match.path for match in self]
|
|
111
|
+
|
|
107
112
|
def empty(self) -> bool:
|
|
108
113
|
"""Return `True` if this node list is empty."""
|
|
109
114
|
return not bool(self)
|
jsonpath/selectors.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""JSONPath segments and selectors, as returned from `Parser.parse`."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from abc import ABC
|
|
@@ -17,6 +18,7 @@ from typing import Union
|
|
|
17
18
|
|
|
18
19
|
from .exceptions import JSONPathIndexError
|
|
19
20
|
from .exceptions import JSONPathTypeError
|
|
21
|
+
from .serialize import canonical_string
|
|
20
22
|
|
|
21
23
|
if TYPE_CHECKING:
|
|
22
24
|
from .env import JSONPathEnvironment
|
|
@@ -75,7 +77,11 @@ class PropertySelector(JSONPathSelector):
|
|
|
75
77
|
self.shorthand = shorthand
|
|
76
78
|
|
|
77
79
|
def __str__(self) -> str:
|
|
78
|
-
return
|
|
80
|
+
return (
|
|
81
|
+
f"[{canonical_string(self.name)}]"
|
|
82
|
+
if self.shorthand
|
|
83
|
+
else f"{canonical_string(self.name)}"
|
|
84
|
+
)
|
|
79
85
|
|
|
80
86
|
def __eq__(self, __value: object) -> bool:
|
|
81
87
|
return (
|
|
@@ -98,7 +104,7 @@ class PropertySelector(JSONPathSelector):
|
|
|
98
104
|
obj=self.env.getitem(match.obj, self.name),
|
|
99
105
|
parent=match,
|
|
100
106
|
parts=match.parts + (self.name,),
|
|
101
|
-
path=match.path + f"[
|
|
107
|
+
path=match.path + f"[{canonical_string(self.name)}]",
|
|
102
108
|
root=match.root,
|
|
103
109
|
)
|
|
104
110
|
match.add_child(_match)
|
|
@@ -117,7 +123,7 @@ class PropertySelector(JSONPathSelector):
|
|
|
117
123
|
obj=await self.env.getitem_async(match.obj, self.name),
|
|
118
124
|
parent=match,
|
|
119
125
|
parts=match.parts + (self.name,),
|
|
120
|
-
path=match.path + f"[
|
|
126
|
+
path=match.path + f"[{canonical_string(self.name)}]",
|
|
121
127
|
root=match.root,
|
|
122
128
|
)
|
|
123
129
|
match.add_child(_match)
|
|
@@ -321,20 +327,15 @@ class SliceSelector(JSONPathSelector):
|
|
|
321
327
|
):
|
|
322
328
|
raise JSONPathIndexError("index out of range", token=self.token)
|
|
323
329
|
|
|
324
|
-
def _normalized_index(self, obj: Sequence[object], index: int) -> int:
|
|
325
|
-
if index < 0 and len(obj) >= abs(index):
|
|
326
|
-
return len(obj) + index
|
|
327
|
-
return index
|
|
328
|
-
|
|
329
330
|
def resolve(self, matches: Iterable[JSONPathMatch]) -> Iterable[JSONPathMatch]:
|
|
330
331
|
for match in matches:
|
|
331
332
|
if not isinstance(match.obj, Sequence) or self.slice.step == 0:
|
|
332
333
|
continue
|
|
333
334
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
for norm_index, obj in zip( # noqa: B905
|
|
336
|
+
range(*self.slice.indices(len(match.obj))),
|
|
337
|
+
self.env.getitem(match.obj, self.slice),
|
|
338
|
+
):
|
|
338
339
|
_match = self.env.match_class(
|
|
339
340
|
filter_context=match.filter_context(),
|
|
340
341
|
obj=obj,
|
|
@@ -345,7 +346,6 @@ class SliceSelector(JSONPathSelector):
|
|
|
345
346
|
)
|
|
346
347
|
match.add_child(_match)
|
|
347
348
|
yield _match
|
|
348
|
-
idx += step
|
|
349
349
|
|
|
350
350
|
async def resolve_async(
|
|
351
351
|
self, matches: AsyncIterable[JSONPathMatch]
|
|
@@ -354,10 +354,10 @@ class SliceSelector(JSONPathSelector):
|
|
|
354
354
|
if not isinstance(match.obj, Sequence) or self.slice.step == 0:
|
|
355
355
|
continue
|
|
356
356
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
361
|
_match = self.env.match_class(
|
|
362
362
|
filter_context=match.filter_context(),
|
|
363
363
|
obj=obj,
|
|
@@ -368,7 +368,6 @@ class SliceSelector(JSONPathSelector):
|
|
|
368
368
|
)
|
|
369
369
|
match.add_child(_match)
|
|
370
370
|
yield _match
|
|
371
|
-
idx += step
|
|
372
371
|
|
|
373
372
|
|
|
374
373
|
class WildSelector(JSONPathSelector):
|
|
@@ -402,7 +401,7 @@ class WildSelector(JSONPathSelector):
|
|
|
402
401
|
obj=val,
|
|
403
402
|
parent=match,
|
|
404
403
|
parts=match.parts + (key,),
|
|
405
|
-
path=match.path + f"[
|
|
404
|
+
path=match.path + f"[{canonical_string(key)}]",
|
|
406
405
|
root=match.root,
|
|
407
406
|
)
|
|
408
407
|
match.add_child(_match)
|
|
@@ -431,7 +430,7 @@ class WildSelector(JSONPathSelector):
|
|
|
431
430
|
obj=val,
|
|
432
431
|
parent=match,
|
|
433
432
|
parts=match.parts + (key,),
|
|
434
|
-
path=match.path + f"[
|
|
433
|
+
path=match.path + f"[{canonical_string(key)}]",
|
|
435
434
|
root=match.root,
|
|
436
435
|
)
|
|
437
436
|
match.add_child(_match)
|
|
@@ -479,7 +478,7 @@ class RecursiveDescentSelector(JSONPathSelector):
|
|
|
479
478
|
obj=val,
|
|
480
479
|
parent=match,
|
|
481
480
|
parts=match.parts + (key,),
|
|
482
|
-
path=match.path + f"[
|
|
481
|
+
path=match.path + f"[{canonical_string(key)}]",
|
|
483
482
|
root=match.root,
|
|
484
483
|
)
|
|
485
484
|
match.add_child(_match)
|
|
@@ -633,7 +632,7 @@ class Filter(JSONPathSelector):
|
|
|
633
632
|
obj=val,
|
|
634
633
|
parent=match,
|
|
635
634
|
parts=match.parts + (key,),
|
|
636
|
-
path=match.path + f"[
|
|
635
|
+
path=match.path + f"[{canonical_string(key)}]",
|
|
637
636
|
root=match.root,
|
|
638
637
|
)
|
|
639
638
|
match.add_child(_match)
|
|
@@ -701,7 +700,7 @@ class Filter(JSONPathSelector):
|
|
|
701
700
|
obj=val,
|
|
702
701
|
parent=match,
|
|
703
702
|
parts=match.parts + (key,),
|
|
704
|
-
path=match.path + f"[
|
|
703
|
+
path=match.path + f"[{canonical_string(key)}]",
|
|
705
704
|
root=match.root,
|
|
706
705
|
)
|
|
707
706
|
match.add_child(_match)
|
jsonpath/serialize.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Helper functions for serializing compiled JSONPath queries."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def canonical_string(value: str) -> str:
|
|
7
|
+
"""Return _value_ as a canonically formatted string literal."""
|
|
8
|
+
single_quoted = (
|
|
9
|
+
json.dumps(value, ensure_ascii=False)[1:-1]
|
|
10
|
+
.replace('\\"', '"')
|
|
11
|
+
.replace("'", "\\'")
|
|
12
|
+
)
|
|
13
|
+
return f"'{single_quoted}'"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-jsonpath
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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
|
|
7
7
|
Project-URL: Source, https://github.com/jg-rp/python-jsonpath
|
|
8
8
|
Author-email: James Prior <jamesgr.prior@gmail.com>
|
|
9
|
-
License: MIT
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.txt
|
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
jsonpath/__about__.py,sha256=
|
|
2
|
-
jsonpath/__init__.py,sha256=
|
|
1
|
+
jsonpath/__about__.py,sha256=xH26iMzGwmzBudJ98FMRtlmv2skwqYMrJnX4zFOO-64,132
|
|
2
|
+
jsonpath/__init__.py,sha256=RTvhFGRb7yLLPUe80a6qD2WAv9gXRe5J69kx0-K6HXc,2412
|
|
3
3
|
jsonpath/__main__.py,sha256=6Y5wOE7U-MHymopXOsxofaY30tVZYPGTJO0L4vytoUw,61
|
|
4
4
|
jsonpath/_data.py,sha256=JEpu5Kg0_kgxYKUilBcHVdTmPxf3-Vc0NgaW6olsqyY,577
|
|
5
5
|
jsonpath/cli.py,sha256=scpWfJXl1jLQ80ZyXqMCu8JtRgZUXluC11x6OkC4oeA,10026
|
|
6
6
|
jsonpath/env.py,sha256=EqlLT6_WE7WbChNs_xDipF7V8uGG3EnyKabJ_bstpGs,22815
|
|
7
7
|
jsonpath/exceptions.py,sha256=5n-9vaKTu5asWllHT0IN-824ewcNCyQIyKvQ58TERVk,4351
|
|
8
|
-
jsonpath/filter.py,sha256=
|
|
8
|
+
jsonpath/filter.py,sha256=kxRX1V4QNYi-PLg0hqOdrpHl8M53M1d585p_beYesps,22042
|
|
9
9
|
jsonpath/fluent_api.py,sha256=mfAA2t-xUGOmGVr_1h9lxo3y4FsAvaxOpvOH8jZ4DHU,9117
|
|
10
10
|
jsonpath/lex.py,sha256=8yrSLd3Q3A9Y4GeacOjboUp40XsR_BkRDQ3CC3UVcP8,10296
|
|
11
|
-
jsonpath/match.py,sha256=
|
|
11
|
+
jsonpath/match.py,sha256=qJ8IUn8RsuC-iL6RKr2vTn0l1f7bcx1m9cnZiAEqYH0,3657
|
|
12
12
|
jsonpath/parse.py,sha256=s7BuFsSgX2GuDubrFr-SDcCk2ZInGzPV04FNFcUwxPM,25214
|
|
13
13
|
jsonpath/patch.py,sha256=tSr8-cGOY4lgnSX1PTYotObIZfakbTRaktrO4M4dqgQ,25321
|
|
14
14
|
jsonpath/path.py,sha256=RUeP7k_WcvP5ZnYblcRDd_-wgo9VnMKyIevTcRImYE4,16421
|
|
15
15
|
jsonpath/pointer.py,sha256=v8k16orIFFOHY1q_QRa5dj4gtPKA--YLA9srOLZpK_E,22299
|
|
16
16
|
jsonpath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
jsonpath/selectors.py,sha256=
|
|
17
|
+
jsonpath/selectors.py,sha256=oesy964W8Qd1qSSb0qtWJY63A2D0lZc0ppF-pGEYmgU,27313
|
|
18
|
+
jsonpath/serialize.py,sha256=viMcWY52wkUseTNfPKillVWswHePJ2L_B3PV3bDVvmU,360
|
|
18
19
|
jsonpath/stream.py,sha256=S1xxgOsJlivu6Y8-kejMoYm7MgSlhjnoX_H5cem5Yhk,2815
|
|
19
20
|
jsonpath/token.py,sha256=2xD2Wrxj7TgyREUhQxzcBcOWLhjz8demYBJ7BqaNJMA,3938
|
|
20
21
|
jsonpath/function_extensions/__init__.py,sha256=wrUayhwlm6iNLnSIx_ER6EeGiqR91T_SIjNBUr42nX0,548
|
|
@@ -28,8 +29,8 @@ jsonpath/function_extensions/match.py,sha256=KjsH33fCFGonp2RV__FuaeIOTwLLcvgaaCi
|
|
|
28
29
|
jsonpath/function_extensions/search.py,sha256=O11fnkHlbvf0QPrLISYfhlPXBvVPBr-U8V0dGbd614Y,710
|
|
29
30
|
jsonpath/function_extensions/typeof.py,sha256=yCAj9zOqSnam1mfHCGolNHWDmsBOvU3rAhbZDYycx50,1780
|
|
30
31
|
jsonpath/function_extensions/value.py,sha256=fQMbPUV87Jn1nOwAlBpTeLmLIG5ejH0XQBOM_SR-Us4,721
|
|
31
|
-
python_jsonpath-1.
|
|
32
|
-
python_jsonpath-1.
|
|
33
|
-
python_jsonpath-1.
|
|
34
|
-
python_jsonpath-1.
|
|
35
|
-
python_jsonpath-1.
|
|
32
|
+
python_jsonpath-1.3.0.dist-info/METADATA,sha256=Q_8xmqUi-_9ZaJiP8CgeREcsAHzkKkrMk7HyQliKAKQ,6381
|
|
33
|
+
python_jsonpath-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
34
|
+
python_jsonpath-1.3.0.dist-info/entry_points.txt,sha256=xvbWnAebJyOMI_9ugK0xrpFRlwmEsAJD2kNHU0Dvscc,43
|
|
35
|
+
python_jsonpath-1.3.0.dist-info/licenses/LICENSE.txt,sha256=u7PksAQGI1QYWcERHeauMseZ4XAzDKUrKW8Z4wbeU1k,1101
|
|
36
|
+
python_jsonpath-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|