Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@

**Fixes**

- Fixed JSON pointers using negative indices. The JSON Pointer specification (RFC 6901) does not allow negative array indexes. We now raise a `JSONPointerIndexError` if a JSON Pointer attempts to resolve an array item with a negative index. See [#115](https://github.com/jg-rp/python-jsonpath/issues/115). For anyone needing JSON Pointers that support negative indexes, set `JSONPointer.min_int_index` to a suitably negative integer, like `JSONPointer.min_int_index = -(2**53) + 1`.
- Fixed JSON pointers with negative indices.

Previously, negative indices were resolved against array-like values, but the JSON Pointer specification (RFC 6901) does not permit negative array indexes. We now raise a `JSONPointerIndexError` when a JSON Pointer attempts to resolve an array element using a negative index.

For users who require negative indices in JSON Pointers, you can set `JSONPointer.min_int_index` to a suitably negative integer, like `JSONPointer.min_int_index = -(2**53) + 1`.

See [#116](https://github.com/jg-rp/python-jsonpath/pull/116).

- Fixed the JSON Patch `add` operation.

Previously, a `JSONPatchError` was raised when pointing to an array index equal to the array's length. Now we append to arrays in such cases. See [#117](https://github.com/jg-rp/python-jsonpath/issues/117).

## Version 2.0.0

Expand Down
11 changes: 8 additions & 3 deletions jsonpath/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from abc import ABC
from abc import abstractmethod
from io import IOBase
from typing import Any
from typing import Dict
from typing import Iterable
from typing import List
Expand Down Expand Up @@ -70,7 +71,11 @@ def apply(
if target == "-":
parent.append(self.value)
else:
raise JSONPatchError("index out of range")
index = self.path._index(target) # noqa: SLF001
if index == len(parent):
parent.append(self.value)
else:
raise JSONPatchError("index out of range")
else:
parent.insert(int(target), self.value)
elif isinstance(parent, MutableMapping):
Expand Down Expand Up @@ -628,7 +633,7 @@ def test(self: Self, path: Union[str, JSONPointer], value: object) -> Self:

def apply(
self,
data: Union[str, IOBase, MutableSequence[object], MutableMapping[str, object]],
data: Union[str, IOBase, MutableSequence[Any], MutableMapping[str, Any]],
) -> object:
"""Apply all operations from this patch to _data_.

Expand Down Expand Up @@ -676,7 +681,7 @@ def asdicts(self) -> List[Dict[str, object]]:

def apply(
patch: Union[str, IOBase, Iterable[Mapping[str, object]], None],
data: Union[str, IOBase, MutableSequence[object], MutableMapping[str, object]],
data: Union[str, IOBase, MutableSequence[Any], MutableMapping[str, Any]],
*,
unicode_escape: bool = True,
uri_decode: bool = False,
Expand Down
15 changes: 15 additions & 0 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pytest

from jsonpath import JSONPatch
from jsonpath import JSONPatchError
from jsonpath import JSONPointerIndexError
from jsonpath import findall
from jsonpath import pointer
Expand Down Expand Up @@ -100,3 +102,16 @@ def test_issue_115() -> None:
# Negative index
with pytest.raises(JSONPointerIndexError):
pointer.resolve("/users/-1/score", data)


def test_issue_117() -> None:
# When the target value is an array of length 2, /foo/2 is the same as /foo/-
patch = JSONPatch().add(path="/foo/2", value=99)
data = {"foo": ["bar", "baz"]}
assert patch.apply(data) == {"foo": ["bar", "baz", 99]}

# Array length + 1 raises
patch = JSONPatch().add(path="/foo/3", value=99)
data = {"foo": ["bar", "baz"]}
with pytest.raises(JSONPatchError):
patch.apply(data)
Loading