python-jsonpath 2.0.0__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2023-present James Prior <jamesgr.prior@gmail.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "2.0.0"
4
+ __version__ = "2.0.1"
jsonpath/exceptions.py CHANGED
@@ -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):
jsonpath/patch.py CHANGED
@@ -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
- raise JSONPatchError("index out of range")
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[object], MutableMapping[str, object]],
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[object], MutableMapping[str, object]],
684
+ data: Union[str, IOBase, MutableSequence[Any], MutableMapping[str, Any]],
680
685
  *,
681
686
  unicode_escape: bool = True,
682
687
  uri_decode: bool = False,
jsonpath/pointer.py CHANGED
@@ -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 `-(2**53) + 1`.
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 = -(2**53) + 1
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
- self.parts = parts or self._parse(
79
- pointer,
80
- unicode_escape=unicode_escape,
81
- uri_decode=uri_decode,
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[Union[int, str], ...]:
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, s: str) -> Union[str, int]:
111
- # Reject non-zero ints that start with a zero.
112
- if len(s) > 1 and s.startswith("0"):
113
- return s
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(s)
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 s
125
+ return None
122
126
 
123
- def _getitem(self, obj: Any, key: Any) -> Any:
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
- # Note that `obj` does not have to be a Mapping or Sequence here. Any object
129
- # implementing `__getitem__` will do.
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
- raise JSONPointerIndexError(f"index out of range: {key}") from err
137
-
138
- def _handle_key_error(self, obj: Any, key: Any, err: Exception) -> object:
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(key, str)
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: Any, err: Exception) -> object:
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, JSONPointer) and self.parts == other.parts
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"JSONPointer({self._s!r})" # pragma: no cover
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 JSONPointer(
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 JSONPointer(other, unicode_escape=False, uri_decode=False)
416
+ return self.__class__(other, unicode_escape=False, uri_decode=False)
419
417
 
420
418
  parts = self.parts + tuple(
421
- self._index(p.replace("~1", "/").replace("~0", "~"))
422
- for p in other.split("/")
419
+ p.replace("~1", "/").replace("~0", "~") for p in other.split("/")
423
420
  )
424
421
 
425
- return JSONPointer(
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):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jsonpath
3
- Version: 2.0.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 JSONPath that follows RFC 9535 much more strictly. If you require maximum interoperability with JSONPath implemented in other languages - at the expense of extra features - choose [jsonpath-rfc9535](https://pypi.org/project/jsonpath-rfc9535/) over [python-jsonpath](https://pypi.org/project/python-jsonpath/).
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 matches RFC 9535's JSONPath model internally and is careful to use the spec's terminology. It 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.
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
 
@@ -1,20 +1,20 @@
1
- jsonpath/__about__.py,sha256=u_kCi-RjsCWgIkLeYKup0DniWqgwFRL5nmU2FGajbqg,132
1
+ jsonpath/__about__.py,sha256=-7JogzyaEMlAT9mZnAfz6WvYW2e1688bbokpApou5SM,132
2
2
  jsonpath/__init__.py,sha256=-a4CE0KwMtXNtOvTIriRZ37EQezZ85EpLz7sj2XpXeQ,11101
3
3
  jsonpath/__main__.py,sha256=6Y5wOE7U-MHymopXOsxofaY30tVZYPGTJO0L4vytoUw,61
4
4
  jsonpath/_data.py,sha256=JEpu5Kg0_kgxYKUilBcHVdTmPxf3-Vc0NgaW6olsqyY,577
5
5
  jsonpath/_types.py,sha256=mxwypalUAI64XdDwXCwIM_1MXCUOTM13y826j-UD8LY,818
6
6
  jsonpath/cli.py,sha256=QNDOzRvGWW9jDNhPpsE9tL1jyS9LS5kSQeMh3q86C6E,10217
7
7
  jsonpath/env.py,sha256=G0UK9NZvxaLEicdD10hxrd3C_pp-o9FxekkzpZb_O4I,24540
8
- jsonpath/exceptions.py,sha256=A9wPF-bUHnOBk5g-dqdZELJ3NIes9kCUh_OUtDrwUNQ,6495
8
+ jsonpath/exceptions.py,sha256=V7A4A76NjFffa11ePEYgYcV6gG8sdmL9fh3KGEGqPu8,6498
9
9
  jsonpath/filter.py,sha256=W4TyM8um0AQzA_m4Iy-dr1Jd5341YmTP6UWmXfSpia4,22617
10
10
  jsonpath/fluent_api.py,sha256=mfAA2t-xUGOmGVr_1h9lxo3y4FsAvaxOpvOH8jZ4DHU,9117
11
11
  jsonpath/lex.py,sha256=_64KcaE5Mi3-E5DdYMyX1tHRHtokQWXGKT-ToMb_dfs,12019
12
12
  jsonpath/lru_cache.py,sha256=ohd_ssx2UnBWlLyt16bh2Vg6a_SDA1xHyI_wyOkHLvY,4047
13
13
  jsonpath/match.py,sha256=hPz0OEVJOYJ3MzenJuUzxxHSC0-NNPrLyHXHBRYzOZE,4167
14
14
  jsonpath/parse.py,sha256=QplaB5u2QRl_MyVUDkJ39cPKI6s2oX-ztZ10KUcxNQM,29973
15
- jsonpath/patch.py,sha256=T6CP0GDJg6drD9qWs6ueRFdQRhXj_xPGEtW5x01iJb8,25388
15
+ jsonpath/patch.py,sha256=pwlAPFlwHZ-gW8COy60Axk9e1yIVNnQ4ctD0IEILaA4,25593
16
16
  jsonpath/path.py,sha256=k4LEmfMhNcJ0ZnEIqOX--G1SoCWhtZZ2S1OzC6kHotA,15661
17
- jsonpath/pointer.py,sha256=UjqqWQWL8D-chjSEQ2al_Ev5xcHaKxFFfQxPfuBSwO0,23100
17
+ jsonpath/pointer.py,sha256=VEd_cX2_5eR-z755yRatmIS-OLAl_e-9wZhF-t4F1YM,23037
18
18
  jsonpath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  jsonpath/segments.py,sha256=a3gTT6capn2VTjMLbVUeYgkzNhEMCjcopZh7WkdrGbs,4458
20
20
  jsonpath/selectors.py,sha256=ozxKBwd81E3Fd0F-E-9nrPZYqmNLfdrWYjliGfE8jGU,24645
@@ -35,8 +35,8 @@ jsonpath/function_extensions/search.py,sha256=H2Si0js_UG6LACxHfZDPhKK8qqmVtUUhr3
35
35
  jsonpath/function_extensions/starts_with.py,sha256=z-9hkNjQQj_p579CnM6W-E6nc1ucooHHwKwpefO_EBA,676
36
36
  jsonpath/function_extensions/typeof.py,sha256=yCAj9zOqSnam1mfHCGolNHWDmsBOvU3rAhbZDYycx50,1780
37
37
  jsonpath/function_extensions/value.py,sha256=fQMbPUV87Jn1nOwAlBpTeLmLIG5ejH0XQBOM_SR-Us4,721
38
- python_jsonpath-2.0.0.dist-info/METADATA,sha256=WUKT1yMdGHTDI7ONfeg89UQRkOURdjGbj2uZXFcH3t0,6678
39
- python_jsonpath-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
- python_jsonpath-2.0.0.dist-info/entry_points.txt,sha256=xvbWnAebJyOMI_9ugK0xrpFRlwmEsAJD2kNHU0Dvscc,43
41
- python_jsonpath-2.0.0.dist-info/licenses/LICENSE.txt,sha256=u7PksAQGI1QYWcERHeauMseZ4XAzDKUrKW8Z4wbeU1k,1101
42
- python_jsonpath-2.0.0.dist-info/RECORD,,
38
+ python_jsonpath-2.0.1.dist-info/METADATA,sha256=fyv3QwOwtVsP_9Nkqv-1Cn8Natr-WHF1jd0n9gYFAts,6529
39
+ python_jsonpath-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
+ python_jsonpath-2.0.1.dist-info/entry_points.txt,sha256=xvbWnAebJyOMI_9ugK0xrpFRlwmEsAJD2kNHU0Dvscc,43
41
+ python_jsonpath-2.0.1.dist-info/licenses/LICENSE.txt,sha256=u7PksAQGI1QYWcERHeauMseZ4XAzDKUrKW8Z4wbeU1k,1101
42
+ python_jsonpath-2.0.1.dist-info/RECORD,,