From a2255a0e593acffb7e9c56f70f424b2396c1f411 Mon Sep 17 00:00:00 2001 From: James Prior Date: Thu, 11 Sep 2025 15:18:31 +0100 Subject: [PATCH 1/2] Fix `JSONPatch.add` --- CHANGELOG.md | 3 ++- jsonpath/patch.py | 11 ++++++++--- tests/test_issues.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a37a1f..bd455fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ **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 using negative indices. Previously we would resolve negative indices against array-like values, but 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 [#116](https://github.com/jg-rp/python-jsonpath/pull/116). 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 the JSON Patch `add` operation. Previously we would raise a `JSONPatchError` when adding to an array value with an index equal to the array's length. Now we append to array values when pointing to one past the last index of the array. See [#117](https://github.com/jg-rp/python-jsonpath/issues/117). ## Version 2.0.0 diff --git a/jsonpath/patch.py b/jsonpath/patch.py index 736b55f..54480e7 100644 --- a/jsonpath/patch.py +++ b/jsonpath/patch.py @@ -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 @@ -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): @@ -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_. @@ -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, diff --git a/tests/test_issues.py b/tests/test_issues.py index 27bb615..c88f7f4 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -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 @@ -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) From 76103c2910e1791722d431039fce1f479577333c Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 12 Sep 2025 08:15:32 +0100 Subject: [PATCH 2/2] Improve change log entries --- CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd455fb..37fc14f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ **Fixes** -- Fixed JSON pointers using negative indices. Previously we would resolve negative indices against array-like values, but 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 [#116](https://github.com/jg-rp/python-jsonpath/pull/116). 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 the JSON Patch `add` operation. Previously we would raise a `JSONPatchError` when adding to an array value with an index equal to the array's length. Now we append to array values when pointing to one past the last index of the array. See [#117](https://github.com/jg-rp/python-jsonpath/issues/117). +- 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