Skip to content

Commit

Permalink
fix: pointers without slashes, closes #27
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Jul 15, 2023
1 parent be219c3 commit b7e4159
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Version 0.9.0 (unreleased)

**Fixes**

- Fixed a bug with the parsing of JSON Pointers. When given an arbitrary string without slashes, `JSONPointer` would resolve to the document root. The empty string is the only valid pointer that should resolve to the document root. See [#27](https://github.com/jg-rp/python-jsonpath/issues/27).

**Features**

- Added a command line interface, exposing JSONPath, JSON Pointer and JSON Patch features.
Expand Down
4 changes: 2 additions & 2 deletions jsonpath/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from jsonpath.exceptions import JSONPathIndexError
from jsonpath.exceptions import JSONPathSyntaxError
from jsonpath.exceptions import JSONPathTypeError
from jsonpath.exceptions import JSONPointerResolutionError
from jsonpath.exceptions import JSONPointerError

INDENT = 2

Expand Down Expand Up @@ -295,7 +295,7 @@ def handle_pointer_command(args: argparse.Namespace) -> None:
raise
sys.stderr.write(f"target document json decode error: {err}\n")
sys.exit(1)
except JSONPointerResolutionError as err:
except JSONPointerError as err:
if args.debug:
raise
sys.stderr.write(str(err) + "\n")
Expand Down
9 changes: 6 additions & 3 deletions jsonpath/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,12 @@ def _op_pointer(
f"({op}:{i})"
)

return JSONPointer(
pointer, unicode_escape=self.unicode_escape, uri_decode=self.uri_decode
)
try:
return JSONPointer(
pointer, unicode_escape=self.unicode_escape, uri_decode=self.uri_decode
)
except JSONPointerError as err:
raise JSONPatchError(f"{err} ({op}:{i})") from err

def _op_value(
self, operation: Mapping[str, object], key: str, op: str, i: int
Expand Down
9 changes: 7 additions & 2 deletions jsonpath/pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import Union
from urllib.parse import unquote

from .exceptions import JSONPointerError
from .exceptions import JSONPointerIndexError
from .exceptions import JSONPointerKeyError
from .exceptions import JSONPointerResolutionError
Expand Down Expand Up @@ -93,8 +94,12 @@ def _parse(
.decode("utf-16")
)

# TODO: lstrip pointer
# TODO: handle pointer without leading slash and not empty string
s = s.lstrip()
if s and not s.startswith("/"):
raise JSONPointerError(
"pointer must start with a slash or be the empty string"
)

return tuple(
self._index(p.replace("~1", "/").replace("~0", "~")) for p in s.split("/")
)[1:]
Expand Down
19 changes: 17 additions & 2 deletions tests/test_json_pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import jsonpath
from jsonpath import JSONPointer
from jsonpath import JSONPointerError
from jsonpath import JSONPointerIndexError
from jsonpath import JSONPointerResolutionError
from jsonpath import JSONPointerTypeError
Expand Down Expand Up @@ -117,9 +118,9 @@ def test_resolve_with_missing_parent() -> None:

def test_resolve_with_missing_target() -> None:
data = {"some": {"thing": [1, 2, 3]}}
pointer = JSONPointer("some/other")
pointer = JSONPointer("/some/other")
parent, rv = pointer.resolve_parent(data)
assert parent == data
assert parent == data["some"]
assert rv == UNDEFINED


Expand Down Expand Up @@ -204,3 +205,17 @@ def test_index_with_leading_zero() -> None:
pointer = JSONPointer("/some/thing/01")
with pytest.raises(JSONPointerTypeError):
pointer.resolve_parent(data)


def test_pointer_without_leading_slash() -> None:
with pytest.raises(JSONPointerError):
JSONPointer("some/thing/01")

with pytest.raises(JSONPointerError):
JSONPointer("nosuchthing")


def test_pointer_with_leading_whitespace() -> None:
data = {"some": {"thing": [1, 2, 3]}}
pointer = JSONPointer(" /some/thing/0")
assert pointer.resolve(data) == 1

0 comments on commit b7e4159

Please sign in to comment.