diff --git a/jsonschema/_format.py b/jsonschema/_format.py index 5efda86e6..cd057f0f8 100644 --- a/jsonschema/_format.py +++ b/jsonschema/_format.py @@ -488,8 +488,11 @@ def is_relative_json_pointer(instance: object) -> bool: non_negative_integer, rest = [], "" for i, character in enumerate(instance): if character.isdigit(): - # digits with a leading "0" are not allowed - if i > 0 and int(instance[i - 1]) == 0: + # a leading "0" is not allowed: "0" by itself is valid, but a + # multi-digit integer may not start with "0" (e.g. "01"). Only + # the single accumulated "0" followed by another digit is a + # leading zero -- "100"/"205" are fine. + if non_negative_integer == ["0"]: return False non_negative_integer.append(character) diff --git a/jsonschema/tests/test_format.py b/jsonschema/tests/test_format.py index d829f9848..6d4183006 100644 --- a/jsonschema/tests/test_format.py +++ b/jsonschema/tests/test_format.py @@ -80,6 +80,25 @@ def test_format_checkers_come_with_defaults(self): with self.assertRaises(FormatError): checker.check(instance="not-an-ipv4", format="ipv4") + def test_relative_json_pointer_allows_multidigit_integers(self): + checker = FormatChecker() + if "relative-json-pointer" not in checker.checkers: + self.skipTest("jsonpointer is not available") + + # Only a *leading* zero is disallowed; a multi-digit integer with an + # internal or trailing zero is valid. + valids = ("0", "1", "120", "100", "205", "1000", "100/foo", "100#") + for valid in valids: + self.assertTrue( + checker.conforms(valid, "relative-json-pointer"), + msg=f"{valid!r} should be a valid relative-json-pointer", + ) + for invalid in ("01", "00", "007", "01/a", "01#"): + self.assertFalse( + checker.conforms(invalid, "relative-json-pointer"), + msg=f"{invalid!r} should not be a valid relative-json-pointer", + ) + def test_repr(self): checker = FormatChecker(formats=()) checker.checks("foo")(lambda thing: True) # pragma: no cover