python-jsonpath 2.0.0__tar.gz → 2.0.1__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-2.0.0 → python_jsonpath-2.0.1}/PKG-INFO +3 -3
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/README.md +2 -2
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/__about__.py +1 -1
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/exceptions.py +3 -3
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/patch.py +8 -3
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/pointer.py +52 -55
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/.gitignore +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/LICENSE.txt +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/__init__.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/__main__.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/_data.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/_types.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/cli.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/env.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/filter.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/fluent_api.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/__init__.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/_pattern.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/arguments.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/count.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/filter_function.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/is_instance.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/keys.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/length.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/match.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/search.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/starts_with.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/typeof.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/value.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/lex.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/lru_cache.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/match.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/parse.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/path.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/py.typed +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/segments.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/selectors.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/serialize.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/stream.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/token.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/unescape.py +0 -0
- {python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-jsonpath
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
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
|
|
@@ -95,9 +95,9 @@ conda install -c conda-forge python-jsonpath
|
|
|
95
95
|
|
|
96
96
|
## Related projects
|
|
97
97
|
|
|
98
|
-
- [JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - A Python implementation of
|
|
98
|
+
- [JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - A minimal, slightly cleanr Python implementation of RFC 9535. If you're not interested JSONPath sytax beyond that defined in RFC 9535, you might choose [jsonpath-rfc9535](https://pypi.org/project/jsonpath-rfc9535/) over [python-jsonpath](https://pypi.org/project/python-jsonpath/).
|
|
99
99
|
|
|
100
|
-
jsonpath-rfc9535
|
|
100
|
+
jsonpath-rfc9535 also includes utilities for verifying and testing the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). Most notably the nondeterministic behavior of some JSONPath selectors.
|
|
101
101
|
|
|
102
102
|
- [JSON P3](https://github.com/jg-rp/json-p3) - RFC 9535 implemented in TypeScript. JSON P3 does not include all the non-standard features of Python JSONPath, but does define some optional [extra syntax](https://jg-rp.github.io/json-p3/guides/jsonpath-extra).
|
|
103
103
|
|
|
@@ -66,9 +66,9 @@ conda install -c conda-forge python-jsonpath
|
|
|
66
66
|
|
|
67
67
|
## Related projects
|
|
68
68
|
|
|
69
|
-
- [JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - A Python implementation of
|
|
69
|
+
- [JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - A minimal, slightly cleanr Python implementation of RFC 9535. If you're not interested JSONPath sytax beyond that defined in RFC 9535, you might choose [jsonpath-rfc9535](https://pypi.org/project/jsonpath-rfc9535/) over [python-jsonpath](https://pypi.org/project/python-jsonpath/).
|
|
70
70
|
|
|
71
|
-
jsonpath-rfc9535
|
|
71
|
+
jsonpath-rfc9535 also includes utilities for verifying and testing the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). Most notably the nondeterministic behavior of some JSONPath selectors.
|
|
72
72
|
|
|
73
73
|
- [JSON P3](https://github.com/jg-rp/json-p3) - RFC 9535 implemented in TypeScript. JSON P3 does not include all the non-standard features of Python JSONPath, but does define some optional [extra syntax](https://jg-rp.github.io/json-p3/guides/jsonpath-extra).
|
|
74
74
|
|
|
@@ -160,21 +160,21 @@ class JSONPointerIndexError(JSONPointerResolutionError, IndexError):
|
|
|
160
160
|
"""An exception raised when an array index is out of range."""
|
|
161
161
|
|
|
162
162
|
def __str__(self) -> str:
|
|
163
|
-
return f"pointer index error {super().__str__()}"
|
|
163
|
+
return f"pointer index error: {super().__str__()}"
|
|
164
164
|
|
|
165
165
|
|
|
166
166
|
class JSONPointerKeyError(JSONPointerResolutionError, KeyError):
|
|
167
167
|
"""An exception raised when a pointer references a mapping with a missing key."""
|
|
168
168
|
|
|
169
169
|
def __str__(self) -> str:
|
|
170
|
-
return f"pointer key error {super().__str__()}"
|
|
170
|
+
return f"pointer key error: {super().__str__()}"
|
|
171
171
|
|
|
172
172
|
|
|
173
173
|
class JSONPointerTypeError(JSONPointerResolutionError, TypeError):
|
|
174
174
|
"""An exception raised when a pointer resolves a string against a sequence."""
|
|
175
175
|
|
|
176
176
|
def __str__(self) -> str:
|
|
177
|
-
return f"pointer type error {super().__str__()}"
|
|
177
|
+
return f"pointer type error: {super().__str__()}"
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
class RelativeJSONPointerError(Exception):
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
from abc import ABC
|
|
8
8
|
from abc import abstractmethod
|
|
9
9
|
from io import IOBase
|
|
10
|
+
from typing import Any
|
|
10
11
|
from typing import Dict
|
|
11
12
|
from typing import Iterable
|
|
12
13
|
from typing import List
|
|
@@ -70,7 +71,11 @@ class OpAdd(Op):
|
|
|
70
71
|
if target == "-":
|
|
71
72
|
parent.append(self.value)
|
|
72
73
|
else:
|
|
73
|
-
|
|
74
|
+
index = self.path._index(target) # noqa: SLF001
|
|
75
|
+
if index == len(parent):
|
|
76
|
+
parent.append(self.value)
|
|
77
|
+
else:
|
|
78
|
+
raise JSONPatchError("index out of range")
|
|
74
79
|
else:
|
|
75
80
|
parent.insert(int(target), self.value)
|
|
76
81
|
elif isinstance(parent, MutableMapping):
|
|
@@ -628,7 +633,7 @@ class JSONPatch:
|
|
|
628
633
|
|
|
629
634
|
def apply(
|
|
630
635
|
self,
|
|
631
|
-
data: Union[str, IOBase, MutableSequence[
|
|
636
|
+
data: Union[str, IOBase, MutableSequence[Any], MutableMapping[str, Any]],
|
|
632
637
|
) -> object:
|
|
633
638
|
"""Apply all operations from this patch to _data_.
|
|
634
639
|
|
|
@@ -676,7 +681,7 @@ class JSONPatch:
|
|
|
676
681
|
|
|
677
682
|
def apply(
|
|
678
683
|
patch: Union[str, IOBase, Iterable[Mapping[str, object]], None],
|
|
679
|
-
data: Union[str, IOBase, MutableSequence[
|
|
684
|
+
data: Union[str, IOBase, MutableSequence[Any], MutableMapping[str, Any]],
|
|
680
685
|
*,
|
|
681
686
|
unicode_escape: bool = True,
|
|
682
687
|
uri_decode: bool = False,
|
|
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
from typing import Iterable
|
|
12
12
|
from typing import Mapping
|
|
13
|
+
from typing import Optional
|
|
13
14
|
from typing import Sequence
|
|
14
15
|
from typing import Tuple
|
|
15
16
|
from typing import Union
|
|
@@ -58,14 +59,14 @@ class JSONPointer:
|
|
|
58
59
|
max_int_index (int): The maximum integer allowed when resolving array
|
|
59
60
|
items by index. Defaults to `(2**53) - 1`.
|
|
60
61
|
min_int_index (int): The minimum integer allowed when resolving array
|
|
61
|
-
items by index. Defaults to
|
|
62
|
+
items by index. Defaults to `0`.
|
|
62
63
|
"""
|
|
63
64
|
|
|
64
65
|
__slots__ = ("_s", "parts")
|
|
65
66
|
|
|
66
67
|
keys_selector = "~"
|
|
67
68
|
max_int_index = (2**53) - 1
|
|
68
|
-
min_int_index =
|
|
69
|
+
min_int_index = 0
|
|
69
70
|
|
|
70
71
|
def __init__(
|
|
71
72
|
self,
|
|
@@ -75,11 +76,15 @@ class JSONPointer:
|
|
|
75
76
|
unicode_escape: bool = True,
|
|
76
77
|
uri_decode: bool = False,
|
|
77
78
|
) -> None:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
if parts:
|
|
80
|
+
self.parts = tuple(str(part) for part in parts)
|
|
81
|
+
else:
|
|
82
|
+
self.parts = self._parse(
|
|
83
|
+
pointer,
|
|
84
|
+
unicode_escape=unicode_escape,
|
|
85
|
+
uri_decode=uri_decode,
|
|
86
|
+
)
|
|
87
|
+
|
|
83
88
|
self._s = self._encode(self.parts)
|
|
84
89
|
|
|
85
90
|
def __str__(self) -> str:
|
|
@@ -91,7 +96,7 @@ class JSONPointer:
|
|
|
91
96
|
*,
|
|
92
97
|
unicode_escape: bool,
|
|
93
98
|
uri_decode: bool,
|
|
94
|
-
) -> Tuple[
|
|
99
|
+
) -> Tuple[str, ...]:
|
|
95
100
|
if uri_decode:
|
|
96
101
|
s = unquote(s)
|
|
97
102
|
if unicode_escape:
|
|
@@ -103,43 +108,49 @@ class JSONPointer:
|
|
|
103
108
|
"pointer must start with a slash or be the empty string"
|
|
104
109
|
)
|
|
105
110
|
|
|
106
|
-
return tuple(
|
|
107
|
-
self._index(p.replace("~1", "/").replace("~0", "~")) for p in s.split("/")
|
|
108
|
-
)[1:]
|
|
111
|
+
return tuple(p.replace("~1", "/").replace("~0", "~") for p in s.split("/"))[1:]
|
|
109
112
|
|
|
110
|
-
def _index(self,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
def _index(self, key: str) -> Optional[int]:
|
|
114
|
+
"""Return an array index for `key`.
|
|
115
|
+
|
|
116
|
+
Return `None` if key can't be converted to an index.
|
|
117
|
+
"""
|
|
118
|
+
# Reject indexes that start with a zero.
|
|
119
|
+
if len(key) > 1 and key.startswith("0"):
|
|
120
|
+
return None
|
|
114
121
|
|
|
115
122
|
try:
|
|
116
|
-
index = int(
|
|
117
|
-
if index < self.min_int_index or index > self.max_int_index:
|
|
118
|
-
raise JSONPointerError("index out of range")
|
|
119
|
-
return index
|
|
123
|
+
index = int(key)
|
|
120
124
|
except ValueError:
|
|
121
|
-
return
|
|
125
|
+
return None
|
|
122
126
|
|
|
123
|
-
|
|
127
|
+
if index < self.min_int_index or index > self.max_int_index:
|
|
128
|
+
raise JSONPointerIndexError(
|
|
129
|
+
f"array indices must be between {self.min_int_index}"
|
|
130
|
+
f" and {self.max_int_index}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return index
|
|
134
|
+
|
|
135
|
+
def _getitem(self, obj: Any, key: str) -> Any:
|
|
124
136
|
try:
|
|
125
137
|
# Handle the most common cases. A mapping with a string key, or a sequence
|
|
126
138
|
# with an integer index.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
139
|
+
if isinstance(obj, Sequence) and not isinstance(obj, str):
|
|
140
|
+
index = self._index(key)
|
|
141
|
+
if isinstance(index, int):
|
|
142
|
+
return getitem(obj, index)
|
|
130
143
|
return getitem(obj, key)
|
|
131
144
|
except KeyError as err:
|
|
132
145
|
return self._handle_key_error(obj, key, err)
|
|
133
146
|
except TypeError as err:
|
|
134
147
|
return self._handle_type_error(obj, key, err)
|
|
135
148
|
except IndexError as err:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if isinstance(key, int):
|
|
140
|
-
# Try a string repr of the index-like item as a mapping key.
|
|
141
|
-
return self._getitem(obj, str(key))
|
|
149
|
+
if not isinstance(err, JSONPointerIndexError):
|
|
150
|
+
raise JSONPointerIndexError(f"index out of range: {key}") from err
|
|
151
|
+
raise
|
|
142
152
|
|
|
153
|
+
def _handle_key_error(self, obj: Any, key: str, err: Exception) -> object:
|
|
143
154
|
# Handle non-standard key/property selector/pointer.
|
|
144
155
|
#
|
|
145
156
|
# For the benefit of `RelativeJSONPointer.to()` and `JSONPathMatch.pointer()`,
|
|
@@ -149,8 +160,7 @@ class JSONPointer:
|
|
|
149
160
|
# Note that if a key with a leading `#`/`~` exists in `obj`, it will have been
|
|
150
161
|
# handled by `_getitem`.
|
|
151
162
|
if (
|
|
152
|
-
isinstance(
|
|
153
|
-
and isinstance(obj, Mapping)
|
|
163
|
+
isinstance(obj, Mapping)
|
|
154
164
|
and key.startswith((self.keys_selector, "#"))
|
|
155
165
|
and key[1:] in obj
|
|
156
166
|
):
|
|
@@ -158,17 +168,10 @@ class JSONPointer:
|
|
|
158
168
|
|
|
159
169
|
raise JSONPointerKeyError(key) from err
|
|
160
170
|
|
|
161
|
-
def _handle_type_error(self, obj: Any, key:
|
|
162
|
-
if (
|
|
163
|
-
isinstance(obj, str)
|
|
164
|
-
or not isinstance(obj, Sequence)
|
|
165
|
-
or not isinstance(key, str)
|
|
166
|
-
):
|
|
171
|
+
def _handle_type_error(self, obj: Any, key: str, err: Exception) -> object:
|
|
172
|
+
if not isinstance(obj, Sequence) or not isinstance(key, str):
|
|
167
173
|
raise JSONPointerTypeError(f"{key}: {err}") from err
|
|
168
174
|
|
|
169
|
-
# `obj` is array-like
|
|
170
|
-
# `key` is a string
|
|
171
|
-
|
|
172
175
|
if key == "-":
|
|
173
176
|
# "-" is a valid index when appending to a JSON array with JSON Patch, but
|
|
174
177
|
# not when resolving a JSON Pointer.
|
|
@@ -185,11 +188,6 @@ class JSONPointer:
|
|
|
185
188
|
raise JSONPointerIndexError(f"index out of range: {_index}") from err
|
|
186
189
|
return _index
|
|
187
190
|
|
|
188
|
-
# Try int index. Reject non-zero ints that start with a zero.
|
|
189
|
-
index = self._index(key)
|
|
190
|
-
if isinstance(index, int):
|
|
191
|
-
return self._getitem(obj, index)
|
|
192
|
-
|
|
193
191
|
raise JSONPointerTypeError(f"{key}: {err}") from err
|
|
194
192
|
|
|
195
193
|
def resolve(
|
|
@@ -349,13 +347,13 @@ class JSONPointer:
|
|
|
349
347
|
)
|
|
350
348
|
|
|
351
349
|
def __eq__(self, other: object) -> bool:
|
|
352
|
-
return isinstance(other,
|
|
350
|
+
return isinstance(other, self.__class__) and self.parts == other.parts
|
|
353
351
|
|
|
354
352
|
def __hash__(self) -> int:
|
|
355
|
-
return hash(self.parts) # pragma: no cover
|
|
353
|
+
return hash((self.__class__, self.parts)) # pragma: no cover
|
|
356
354
|
|
|
357
355
|
def __repr__(self) -> str:
|
|
358
|
-
return f"
|
|
356
|
+
return f"{self.__class__.__name__}({self._s!r})" # pragma: no cover
|
|
359
357
|
|
|
360
358
|
def exists(
|
|
361
359
|
self, data: Union[str, IOBase, Sequence[object], Mapping[str, object]]
|
|
@@ -391,7 +389,7 @@ class JSONPointer:
|
|
|
391
389
|
if not self.parts:
|
|
392
390
|
return self
|
|
393
391
|
parent_parts = self.parts[:-1]
|
|
394
|
-
return
|
|
392
|
+
return self.__class__(
|
|
395
393
|
self._encode(parent_parts),
|
|
396
394
|
parts=parent_parts,
|
|
397
395
|
unicode_escape=False,
|
|
@@ -415,14 +413,13 @@ class JSONPointer:
|
|
|
415
413
|
|
|
416
414
|
other = self._unicode_escape(other.lstrip())
|
|
417
415
|
if other.startswith("/"):
|
|
418
|
-
return
|
|
416
|
+
return self.__class__(other, unicode_escape=False, uri_decode=False)
|
|
419
417
|
|
|
420
418
|
parts = self.parts + tuple(
|
|
421
|
-
|
|
422
|
-
for p in other.split("/")
|
|
419
|
+
p.replace("~1", "/").replace("~0", "~") for p in other.split("/")
|
|
423
420
|
)
|
|
424
421
|
|
|
425
|
-
return
|
|
422
|
+
return self.__class__(
|
|
426
423
|
self._encode(parts), parts=parts, unicode_escape=False, uri_decode=False
|
|
427
424
|
)
|
|
428
425
|
|
|
@@ -612,7 +609,7 @@ class RelativeJSONPointer:
|
|
|
612
609
|
raise RelativeJSONPointerIndexError(
|
|
613
610
|
f"index offset out of range {new_index}"
|
|
614
611
|
)
|
|
615
|
-
parts[-1] = int(parts[-1]) + self.index
|
|
612
|
+
parts[-1] = str(int(parts[-1]) + self.index)
|
|
616
613
|
|
|
617
614
|
# Pointer or index/property
|
|
618
615
|
if isinstance(self.pointer, JSONPointer):
|
|
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
|
{python_jsonpath-2.0.0 → python_jsonpath-2.0.1}/jsonpath/function_extensions/filter_function.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|