Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for draft 2020-12 #817

Merged
merged 27 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
173582e
Julian/jsonschema#782: Enable draft2020-12 test suite
nezhar Jun 14, 2021
e877698
Julian/jsonschema#782: Split format and regular test cases on draft20…
nezhar Jun 28, 2021
491168d
Julian/jsonschema#782: Add dependentRequired and dependentSchemas val…
nezhar Jun 15, 2021
edc5eb0
Julian/jsonschema#782: Extend format check for draft2020-12, add dura…
nezhar Jun 14, 2021
0e4eaeb
Julian/jsonschema#782: Add checks for prefixItems, basic check for un…
nezhar Jun 16, 2021
753c415
Julian/jsonschema#782: Adapt items to work with prefixItems
nezhar Jun 16, 2021
96b7fe8
Julian/jsonschema#782: Extend contains with minContains and maxContai…
nezhar Jun 17, 2021
bb8b7ee
Julian/jsonschema#782: Implements unevaluatedItems validations
nezhar Jun 18, 2021
45ee736
Julian/jsonschema#782: Implements unevaluatedProperties validations
nezhar Jun 21, 2021
52d3134
Julian/jsonschema#782: Extend implementation of ref
nezhar Jun 21, 2021
3d81c57
Julian/jsonschema#782: Fixes ref validation priority
nezhar Jun 22, 2021
eccb7ec
Julian/jsonschema#782: Fixes ref resolver for folders
nezhar Jun 22, 2021
11b77ba
Julian/jsonschema#782: Extend resolver for anchor
nezhar Jun 22, 2021
00614cc
Julian/jsonschema#782: Implements defs validations
nezhar Jun 23, 2021
73ec5a4
Julian/jsonschema#782: Add validation for uuid format
nezhar Jun 24, 2021
f8555cd
Julian/jsonschema#782: Implements dynamicRef validations
nezhar Jun 23, 2021
df1953f
Julian/jsonschema#782: Load dependencies from legacy validators
nezhar Jun 25, 2021
16d00de
Julian/jsonschema#782: Fixes relative json pointer format validation …
nezhar Jun 25, 2021
98b49be
Julian/jsonschema#782: Adapt validator test for draft2020-12, fixes c…
nezhar Jun 25, 2021
95742b4
Julian/jsonschema#782: Fixes failing styles
nezhar Jun 26, 2021
769478d
Julian/jsonschema#782: Update validation message for unevaluatedPrope…
nezhar Jun 30, 2021
c380b09
Julian/jsonschema#782: Refactor items behavior with prefixItems
nezhar Jul 1, 2021
bede403
Julian/jsonschema#782: Resolve meta schema vocabularies from local cache
nezhar Jun 28, 2021
1caee54
Julian/jsonschema#782: Extend format tests
nezhar Jul 14, 2021
841a0a2
Julian/jsonschema#782: Code clenaup, fixes validation messages
nezhar Jul 14, 2021
c6b0fc4
Julian/jsonschema#782: Add compatibility to draft7 and older
nezhar Jun 24, 2021
4547b2a
Julian/jsonschema#782: Remove ecmascript validation, extend dynamicRe…
nezhar Jul 21, 2021
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
2 changes: 2 additions & 0 deletions jsonschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
draft4_format_checker,
draft6_format_checker,
draft7_format_checker,
draft202012_format_checker,
)
from jsonschema._types import TypeChecker
from jsonschema.exceptions import (
Expand All @@ -28,6 +29,7 @@
Draft4Validator,
Draft6Validator,
Draft7Validator,
Draft202012Validator,
RefResolver,
validate,
)
Expand Down
84 changes: 75 additions & 9 deletions jsonschema/_format.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from uuid import UUID
import datetime
import ipaddress
import re
Expand Down Expand Up @@ -131,13 +132,14 @@ def conforms(self, instance, format):
draft4_format_checker = FormatChecker()
draft6_format_checker = FormatChecker()
draft7_format_checker = FormatChecker()

draft202012_format_checker = FormatChecker()

_draft_checkers = dict(
draft3=draft3_format_checker,
draft4=draft4_format_checker,
draft6=draft6_format_checker,
draft7=draft7_format_checker,
draft202012=draft202012_format_checker,
)


Expand All @@ -147,12 +149,14 @@ def _checks_drafts(
draft4=None,
draft6=None,
draft7=None,
draft202012=None,
raises=(),
):
draft3 = draft3 or name
draft4 = draft4 or name
draft6 = draft6 or name
draft7 = draft7 or name
draft202012 = draft202012 or name

def wrap(func):
if draft3:
Expand All @@ -163,13 +167,17 @@ def wrap(func):
func = _draft_checkers["draft6"].checks(draft6, raises)(func)
if draft7:
func = _draft_checkers["draft7"].checks(draft7, raises)(func)
if draft202012:
func = _draft_checkers["draft202012"].checks(
draft202012, raises
)(func)

# Oy. This is bad global state, but relied upon for now, until
# deprecation. See https://github.com/Julian/jsonschema/issues/519
# and test_format_checkers_come_with_defaults
FormatChecker.cls_checks(draft7 or draft6 or draft4 or draft3, raises)(
func,
)
FormatChecker.cls_checks(
draft202012 or draft7 or draft6 or draft4 or draft3, raises
)(func)
return func
return wrap

Expand All @@ -187,6 +195,7 @@ def is_email(instance):
draft4="ipv4",
draft6="ipv4",
draft7="ipv4",
draft202012="ipv4",
raises=ipaddress.AddressValueError,
)
def is_ipv4(instance):
Expand All @@ -213,6 +222,7 @@ def is_ipv6(instance):
draft4="hostname",
draft6="hostname",
draft7="hostname",
draft202012="hostname",
)
def is_host_name(instance):
if not isinstance(instance, str):
Expand All @@ -228,6 +238,7 @@ def is_host_name(instance):
else:
@_checks_drafts(
draft7="idn-hostname",
draft202012="idn-hostname",
raises=(idna.IDNAError, UnicodeError),
)
def is_idn_host_name(instance):
Expand All @@ -254,6 +265,7 @@ def is_uri(instance):
@_checks_drafts(
draft6="uri-reference",
draft7="uri-reference",
draft202012="uri-reference",
raises=ValueError,
)
def is_uri_reference(instance):
Expand All @@ -262,19 +274,30 @@ def is_uri_reference(instance):
return validate_rfc3986(instance, rule="URI_reference")

else:
@_checks_drafts(draft7="iri", raises=ValueError)
@_checks_drafts(
draft7="iri",
draft202012="iri",
raises=ValueError,
)
def is_iri(instance):
if not isinstance(instance, str):
return True
return rfc3987.parse(instance, rule="IRI")

@_checks_drafts(draft7="iri-reference", raises=ValueError)
@_checks_drafts(
draft7="iri-reference",
draft202012="iri-reference",
raises=ValueError,
)
def is_iri_reference(instance):
if not isinstance(instance, str):
return True
return rfc3987.parse(instance, rule="IRI_reference")

@_checks_drafts(name="uri", raises=ValueError)
@_checks_drafts(
name="uri",
raises=ValueError,
)
def is_uri(instance):
if not isinstance(instance, str):
return True
Expand All @@ -283,6 +306,7 @@ def is_uri(instance):
@_checks_drafts(
draft6="uri-reference",
draft7="uri-reference",
draft202012="uri-reference",
raises=ValueError,
)
def is_uri_reference(instance):
Expand All @@ -306,7 +330,10 @@ def is_datetime(instance):
return True
return validate_rfc3339(instance.upper())

@_checks_drafts(draft7="time")
@_checks_drafts(
draft7="time",
draft202012="time",
)
def is_time(instance):
if not isinstance(instance, str):
return True
Expand All @@ -327,7 +354,12 @@ def _is_date(instance):
return datetime.datetime.strptime(instance, "%Y-%m-%d")


@_checks_drafts(draft3="date", draft7="date", raises=ValueError)
@_checks_drafts(
draft3="date",
draft7="date",
draft202012="date",
raises=ValueError,
)
def is_date(instance):
if not isinstance(instance, str):
return True
Expand Down Expand Up @@ -377,6 +409,7 @@ def is_css3_color(instance):
@_checks_drafts(
draft6="json-pointer",
draft7="json-pointer",
draft202012="json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_json_pointer(instance):
Expand All @@ -390,6 +423,7 @@ def is_json_pointer(instance):
# into a new external library.
@_checks_drafts(
draft7="relative-json-pointer",
draft202012="relative-json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_relative_json_pointer(instance):
Expand All @@ -400,6 +434,10 @@ def is_relative_json_pointer(instance):
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:
return False

non_negative_integer.append(character)
continue

Expand All @@ -419,8 +457,36 @@ def is_relative_json_pointer(instance):
@_checks_drafts(
draft6="uri-template",
draft7="uri-template",
draft202012="uri-template",
)
def is_uri_template(instance):
if not isinstance(instance, str):
return True
return uri_template.validate(instance)


try:
import isoduration
except ImportError: # pragma: no cover
pass
else:
@_checks_drafts(
draft202012="duration",
raises=isoduration.DurationParsingException,
)
def is_duration(instance):
if not isinstance(instance, str):
return True
return isoduration.parse_duration(instance)


@_checks_drafts(
draft202012="uuid",
raises=ValueError,
)
def is_uuid(instance):
if not isinstance(instance, str):
return True
if "-" not in instance:
raise ValueError("Invalid UUID format")
return UUID(instance)
69 changes: 69 additions & 0 deletions jsonschema/_legacy_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
from jsonschema.exceptions import ValidationError


def ignore_ref_siblings(schema):
"""
Returns a list of validators that should apply for the given schema
Used for draft7 and earlier
"""
ref = schema.get(u"$ref")
if ref is not None:
return [(u"$ref", ref)]
else:
return schema.items()


def dependencies_draft3(validator, dependencies, instance, schema):
if not validator.is_type(instance, "object"):
return
Expand All @@ -27,6 +39,37 @@ def dependencies_draft3(validator, dependencies, instance, schema):
yield ValidationError(message % (each, property))


def dependencies_draft4_draft6_draft7(
validator,
dependencies,
instance,
schema,
):
"""
Support for the ``dependencies`` validator from pre-draft 2019-09.

In later drafts, the validator was split into separate
``dependentRequired`` and ``dependentSchemas`` validators.
"""
if not validator.is_type(instance, "object"):
return

for property, dependency in dependencies.items():
if property not in instance:
continue

if validator.is_type(dependency, "array"):
for each in dependency:
if each not in instance:
message = "%r is a dependency of %r"
yield ValidationError(message % (each, property))
else:
for error in validator.descend(
instance, dependency, schema_path=property,
):
yield error


def disallow_draft3(validator, disallow, instance, schema):
for disallowed in _utils.ensure_list(disallow):
if validator.is_valid(instance, {"type": [disallowed]}):
Expand Down Expand Up @@ -61,6 +104,22 @@ def items_draft3_draft4(validator, items, instance, schema):
yield error


def items_draft6_draft7(validator, items, instance, schema):
if not validator.is_type(instance, "array"):
return

if validator.is_type(items, "array"):
for (index, item), subschema in zip(enumerate(instance), items):
for error in validator.descend(
item, subschema, path=index, schema_path=index,
):
yield error
else:
for index, item in enumerate(instance):
for error in validator.descend(item, items, path=index):
yield error


def minimum_draft3_draft4(validator, minimum, instance, schema):
if not validator.is_type(instance, "number"):
return
Expand Down Expand Up @@ -138,3 +197,13 @@ def type_draft3(validator, types, instance, schema):
yield ValidationError(
_utils.types_msg(instance, types), context=all_errors,
)


def contains_draft6_draft7(validator, contains, instance, schema):
if not validator.is_type(instance, "array"):
return

if not any(validator.is_valid(element, contains) for element in instance):
yield ValidationError(
"None of %r are valid under the given schema" % (instance,)
)
1 change: 1 addition & 0 deletions jsonschema/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,4 @@ def remove(self, *types):
),
)
draft7_type_checker = draft6_type_checker
draft202012_type_checker = draft7_type_checker
Loading