Skip to content

Commit

Permalink
feat: JSONPointer.join
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Jul 18, 2023
1 parent cf97fec commit 525acfd
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 8 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
**Features**

- Added a command line interface, exposing JSONPath, JSON Pointer and JSON Patch features.
- Added `JSONPointer.parent()`, a method that returns a the parent of the pointer, as a new `JSONPointer`.
- Implemented `JSONPointer.__truediv__()` to allow creation of child pointers from an existing pointer.
- Added `JSONPointer.parent()`, a method that returns the parent of the pointer, as a new `JSONPointer`.
- Implemented `JSONPointer.__truediv__()` to allow creation of child pointers from an existing pointer using the slash (`/`) operator.
- Added `JSONPointer.join()`, a method for creating child pointers. This is equivalent to using the slash (`/`) operator for each argument given to `join()`.
- Added `JSONPointer.exists()`, a method that returns `True` if a the pointer can be resolved against some data, or `False` otherwise.

## Version 0.8.1
Expand Down
32 changes: 27 additions & 5 deletions jsonpath/pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
if TYPE_CHECKING:
from .match import JSONPathMatch

UNDEFINED = object()

class _Undefined:
def __str__(self) -> str:
return "<jsonpath.pointer.UNDEFINED>"


UNDEFINED = _Undefined()


class JSONPointer:
Expand Down Expand Up @@ -195,8 +201,8 @@ def resolve_parent(
Returns:
A `(parent, object)` tuple, where parent will be `None` if this
pointer points to the root node in the document. If the parent
exists but the last object does not, `(parent, None)` will be
returned.
exists but the last object does not, `(parent, UNDEFINED)` will
be returned.
Raises:
JSONPointerIndexError: When attempting to access a sequence by
Expand Down Expand Up @@ -336,6 +342,7 @@ def exists(
_True_ if this pointer can be resolved against _data_, or _False_
otherwise.
**_New in version 0.9.0_**
"""
try:
self.resolve(data)
Expand All @@ -347,6 +354,8 @@ def parent(self) -> JSONPointer:
"""Return this pointer's parent, as a new `JSONPointer`.
If this pointer points to the document root, _self_ is returned.
**_New in version 0.9.0_**
"""
if not self.parts:
return self
Expand All @@ -359,11 +368,11 @@ def parent(self) -> JSONPointer:
)

def __truediv__(self, other: object) -> JSONPointer:
"""Join this path with _other_.
"""Join this pointer with _other_.
_other_ is expected to be a JSON Pointer string, possibly without a
leading slash. If _other_ does have a leading slash, the previous
pointer is ignored and a new JSONPath is returns from _other_.
pointer is ignored and a new JSONPath is returned from _other_.
_other_ should not be a "Relative JSON Pointer".
"""
Expand All @@ -386,6 +395,19 @@ def __truediv__(self, other: object) -> JSONPointer:
self._encode(parts), parts=parts, unicode_escape=False, uri_decode=False
)

def join(self, *parts: str) -> JSONPointer:
"""Join this pointer with _parts_.
Each part is expected to be a JSON Pointer string, possibly without a
leading slash. If a part does have a leading slash, the previous
pointer is ignored and a new `JSONPath` is created, and processing of
remaining parts continues.
"""
pointer = self
for part in parts:
pointer = pointer / part
return pointer


def resolve(
pointer: Union[str, Iterable[Union[str, int]]],
Expand Down
17 changes: 16 additions & 1 deletion tests/test_json_pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def test_pointer_parent() -> None:
assert parent.resolve(data) == {"some": {"thing": [1, 2, 3]}}


def test_join_pointers() -> None:
def test_join_pointers_with_slash() -> None:
"""Test that we can join a pointer to a relative path with the `/` operator."""
pointer = JSONPointer("/foo")
assert str(pointer) == "/foo"
Expand All @@ -258,6 +258,21 @@ def test_join_pointers() -> None:
pointer / 0


def test_join_pointers() -> None:
pointer = JSONPointer("/foo")
assert str(pointer) == "/foo"
assert str(pointer.join("bar")) == "/foo/bar"
assert str(pointer.join("baz")) == "/foo/baz"
assert str(pointer.join("bar/baz")) == "/foo/bar/baz"
assert str(pointer.join("bar", "baz")) == "/foo/bar/baz"
assert str(pointer.join("bar/baz", "0")) == "/foo/bar/baz/0"
assert str(pointer.join("/bar")) == "/bar"
assert str(pointer.join("/bar", "0")) == "/bar/0"

with pytest.raises(TypeError):
pointer.join(0) # type: ignore


def test_pointer_exists() -> None:
data = {"some": {"thing": [1, 2, 3]}, "other": None}
assert JSONPointer("/some/thing").exists(data) is True
Expand Down

0 comments on commit 525acfd

Please sign in to comment.