From 8276135cd3e6357f213c33b05a8fe70d4d854bbe Mon Sep 17 00:00:00 2001 From: James Prior Date: Sun, 22 Jun 2025 08:41:54 +0100 Subject: [PATCH 1/2] Fix lint checks and patch targets that look like ints --- jsonpath/filter.py | 2 +- jsonpath/function_extensions/arguments.py | 3 ++- jsonpath/patch.py | 12 ++++++------ jsonpath/pointer.py | 3 +++ tests/test_json_patch.py | 5 +++++ 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/jsonpath/filter.py b/jsonpath/filter.py index 0f33cff..00cbca4 100644 --- a/jsonpath/filter.py +++ b/jsonpath/filter.py @@ -30,7 +30,7 @@ from .path import JSONPath from .selectors import FilterContext -# ruff: noqa: D102 +# ruff: noqa: D102, PLW1641 class FilterExpression(ABC): diff --git a/jsonpath/function_extensions/arguments.py b/jsonpath/function_extensions/arguments.py index b12111f..e89a58c 100644 --- a/jsonpath/function_extensions/arguments.py +++ b/jsonpath/function_extensions/arguments.py @@ -1,4 +1,5 @@ """Class-based function extension base.""" + import inspect from typing import TYPE_CHECKING from typing import Any @@ -26,7 +27,7 @@ def validate( params = list(inspect.signature(func).parameters.values()) # Keyword only params are not supported - if len([p for p in params if p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD)]): + if [p for p in params if p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD)]: raise JSONPathTypeError( f"function {token.value!r} requires keyword arguments", token=token, diff --git a/jsonpath/patch.py b/jsonpath/patch.py index 913987c..736b55f 100644 --- a/jsonpath/patch.py +++ b/jsonpath/patch.py @@ -74,7 +74,7 @@ def apply( else: parent.insert(int(target), self.value) elif isinstance(parent, MutableMapping): - parent[target] = self.value + parent[str(target)] = self.value else: raise JSONPatchError( f"unexpected operation on {parent.__class__.__name__!r}" @@ -183,7 +183,7 @@ def apply( elif isinstance(parent, MutableMapping): if obj is UNDEFINED: raise JSONPatchError("can't remove nonexistent property") - del parent[self.path.parts[-1]] + del parent[str(self.path.parts[-1])] else: raise JSONPatchError( f"unexpected operation on {parent.__class__.__name__!r}" @@ -221,7 +221,7 @@ def apply( elif isinstance(parent, MutableMapping): if obj is UNDEFINED: raise JSONPatchError("can't replace nonexistent property") - parent[self.path.parts[-1]] = self.value + parent[str(self.path.parts[-1])] = self.value else: raise JSONPatchError( f"unexpected operation on {parent.__class__.__name__!r}" @@ -259,7 +259,7 @@ def apply( if isinstance(source_parent, MutableSequence): del source_parent[int(self.source.parts[-1])] if isinstance(source_parent, MutableMapping): - del source_parent[self.source.parts[-1]] + del source_parent[str(self.source.parts[-1])] dest_parent, _ = self.dest.resolve_parent(data) @@ -270,7 +270,7 @@ def apply( if isinstance(dest_parent, MutableSequence): dest_parent.insert(int(self.dest.parts[-1]), source_obj) elif isinstance(dest_parent, MutableMapping): - dest_parent[self.dest.parts[-1]] = source_obj + dest_parent[str(self.dest.parts[-1])] = source_obj else: raise JSONPatchError( f"unexpected operation on {dest_parent.__class__.__name__!r}" @@ -312,7 +312,7 @@ def apply( if isinstance(dest_parent, MutableSequence): dest_parent.insert(int(self.dest.parts[-1]), copy.deepcopy(source_obj)) elif isinstance(dest_parent, MutableMapping): - dest_parent[self.dest.parts[-1]] = copy.deepcopy(source_obj) + dest_parent[str(self.dest.parts[-1])] = copy.deepcopy(source_obj) else: raise JSONPatchError( f"unexpected operation on {dest_parent.__class__.__name__!r}" diff --git a/jsonpath/pointer.py b/jsonpath/pointer.py index 3628347..778426c 100644 --- a/jsonpath/pointer.py +++ b/jsonpath/pointer.py @@ -485,6 +485,9 @@ def __str__(self) -> str: def __eq__(self, __value: object) -> bool: return isinstance(__value, RelativeJSONPointer) and str(self) == str(__value) + def __hash__(self) -> int: + return hash((self.origin, self.index, self.pointer)) + def _parse( self, rel: str, diff --git a/tests/test_json_patch.py b/tests/test_json_patch.py index 64fafaf..a362858 100644 --- a/tests/test_json_patch.py +++ b/tests/test_json_patch.py @@ -268,3 +268,8 @@ def test_non_standard_addap_op() -> None: # Index 7 is out of range and would raises a JSONPatchError with the `add` op. patch = JSONPatch().addap(path="/foo/7", value=99) assert patch.apply({"foo": [1, 2, 3]}) == {"foo": [1, 2, 3, 99]} + + +def test_add_to_mapping_with_int_key() -> None: + patch = JSONPatch().add(path="/1", value=99) + assert patch.apply({"foo": 1}) == {"foo": 1, "1": 99} From 1fe8aa8b863a3d28d63dfb463c06a4fdc787a5c1 Mon Sep 17 00:00:00 2001 From: James Prior Date: Sun, 22 Jun 2025 08:57:13 +0100 Subject: [PATCH 2/2] Update change log --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8cc4a..8e15371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Python JSONPath Change Log +## Version 1.3.1 (unreleased) + +**Fixes** + +- Fixed the non-standard JSON Patch operation, `addap`. Previously it was behaving like `addne`. See [#81](https://github.com/jg-rp/python-jsonpath/pull/81). +- Fixed JSON Patch ops that operate on mappings and have a target that looks like an int. We now ensure the target is a string. See [#82](https://github.com/jg-rp/python-jsonpath/pull/82). + ## Version 1.3.0 **Fixes**