Skip to content
Open
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
24 changes: 23 additions & 1 deletion jsonschema/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ def is_json_pointer(instance: object) -> bool:
@_checks_drafts(
draft7="relative-json-pointer",
draft201909="relative-json-pointer",
draft202012="relative-json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_relative_json_pointer(instance: object) -> bool:
Expand All @@ -496,6 +495,29 @@ def is_relative_json_pointer(instance: object) -> bool:
return False
return (rest == "#") or bool(jsonpointer.JsonPointer(rest))

@_checks_drafts(
draft202012="relative-json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_relative_json_pointer_202012(instance: object) -> bool:
# Definition taken from:
# https://json-schema.org/draft/2020-12/relative-json-pointer
if not isinstance(instance, str):
return True
if not instance:
return False

prefix = re.match(
r"(?:0|[1-9][0-9]*)(?:[+-](?:0|[1-9][0-9]*))?",
instance,
re.ASCII,
)
if prefix is None:
return False

rest = instance[prefix.end():]
return (rest == "#") or bool(jsonpointer.JsonPointer(rest))


with suppress(ImportError):
import uri_template
Expand Down
44 changes: 43 additions & 1 deletion jsonschema/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@

from jsonschema import FormatChecker, ValidationError
from jsonschema.exceptions import FormatError
from jsonschema.validators import Draft4Validator
from jsonschema.validators import (
Draft4Validator,
Draft7Validator,
Draft201909Validator,
Draft202012Validator,
)

BOOM = ValueError("Boom!")
BANG = ZeroDivisionError("Bang!")
Expand Down Expand Up @@ -89,3 +94,40 @@ def test_repr(self):
repr(checker),
"<FormatChecker checkers=['bar', 'baz', 'foo']>",
)


class TestRelativeJSONPointer(TestCase):
def _checker(self):
checker = Draft202012Validator.FORMAT_CHECKER
if "relative-json-pointer" not in checker.checkers:
self.skipTest("requires the format extra")
return checker

def test_draft202012_allows_index_manipulation(self):
checker = self._checker()

for instance in ("0+1", "0+1/foo", "1-2/bar", "0-0", "0-1#"):
with self.subTest(instance=instance):
checker.check(instance, "relative-json-pointer")

def test_older_drafts_still_reject_index_manipulation(self):
self._checker()
for validator in (Draft7Validator, Draft201909Validator):
with (
self.subTest(validator=validator.__name__),
self.assertRaises(FormatError),
):
validator.FORMAT_CHECKER.check(
"0+1/foo",
"relative-json-pointer",
)

def test_draft202012_rejects_malformed_index_manipulation(self):
checker = self._checker()

for instance in ("0+", "0-01", "0++1", "0+1not-a-pointer"):
with (
self.subTest(instance=instance),
self.assertRaises(FormatError),
):
checker.check(instance, "relative-json-pointer")
Loading