Compare commits

...

3 Commits

Author SHA1 Message Date
Kévin Commaille f4e7b2aa97
Fix property type resolution in render-object-table (#1789)
The split was not clear between property-type and type-or-title,
so it was not obvious which partial should be called for recursion.
That resulted in an error where type-or-title was only called for objects and array items, even if it also resolves
arrays of types.

This makes the split clearer. property-type must be called for any schema,
and object-type-or-title is only called for object schemas.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
1 month ago
Kévin Commaille 521e555cf6
Bump minimum Hugo version in README (#1788)
To match the one in config.toml

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
1 month ago
Kévin Commaille a81b720151
Upgrade CI scripts dependencies (#1786)
Will allow us to benefit from future fixes in JSON Schema validation.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
1 month ago

@ -61,7 +61,7 @@ place after an MSC has been accepted, not as part of a proposal itself.
1. Install the extended version (often the OS default) of Hugo:
<https://gohugo.io/getting-started/installing>. Note that at least Hugo
v0.110.0 is required.
v0.113.0 is required.
Alternatively, use the Docker image at
https://hub.docker.com/r/klakegg/hugo/. (The "extended edition" is required

@ -0,0 +1 @@
Upgrade jsonschema and python-jsonpath CI scripts dependencies.

@ -0,0 +1 @@
Fix property type resolution in `render-object-table` partial.

@ -78,20 +78,20 @@
{{ end }}
{{/*
Computes the type to display for a property, given:
Computes the type to display for a property's schema, given:
* `type`: string or array of strings for the type(s) of the property
* `type`: optional string or array of strings for the type(s) of the property
* `title`: optional string for the title of the property
* `oneOf`: optional array of dictionaries describing the different formats
that the property can have
* `additionalProperties`: optional dictionary for properties with undefined
names
* `additionalProperties`: if the type is an object, optional dictionary for
properties with undefined names
* `patternProperties`: optional dictionary for properties with names
adhering to a regex pattern
* `patternProperties`: if the type is an object, optional dictionary for
properties with names adhering to a regex pattern
* `items`: if the type is an array, array of dictionaries describing the
format of the array's items
@ -100,37 +100,59 @@
*/}}
{{ define "partials/property-type" }}
{{ $type := .type }}
{{ if or (eq .type "object") (and .oneOf (reflect.IsSlice .oneOf)) }}
{{ $type = partial "type-or-title" . }}
{{ end }}
{{ $type := "" }}
{{/*
If the property is an array, indicate this with square brackets,
like `[type]`.
*/}}
{{ if eq .type "array"}}
{{ if eq .type "object" }}
{{/* Resolve the type or title of the object */}}
{{ $type = partial "object-type-or-title" . }}
{{ else if eq .type "array"}}
{{/*
If the property is an array, indicate this with square brackets,
like `[type]`.
*/}}
{{ $items := .items }}
{{ if .items }}
{{ $items = partial "json-schema/resolve-allof" .items }}
{{ end }}
{{ $inner_type := partial "type-or-title" $items }}
{{ $inner_type := partial "property-type" $items }}
{{ $type = delimit (slice "[" $inner_type "]") "" }}
{{ else if or (reflect.IsSlice .type) .oneOf }}
{{/*
It's legal to specify an array of types.
There are two ways to do that:
- Use an array of strings.
- Use oneOf, with items having a schema.
Join them together in that case, like `type|other_type`.
*/}}
{{ $types := slice }}
{{ if .oneOf }}
{{ range .oneOf }}
{{ $types = $types | append (partial "property-type" .) }}
{{ end }}
{{ else }}
{{ range .type }}
{{ $types = $types | append . }}
{{ end }}
{{ end }}
{{ $type = delimit $types "|" }}
{{ else }}
{{/* A simple type like string or boolean */}}
{{ $type = .type }}
{{ end }}
{{ return $type }}
{{ end }}
{{/*
Computes the type to display for a property's schema, given:
Computes the type to display for an object property's schema, given:
* `type`: string or array of strings for the type(s) of the property
* `type`: string equal to "object"
* `title`: optional string for the title of the property
* `oneOf`: optional array of dictionaries describing the different formats
that the property can have
* `title`: optional string for the title of the object property
* `additionalProperties`: optional dictionary for properties with undefined
names
@ -142,8 +164,8 @@
The title has a higher priority than anything else.
*/}}
{{ define "partials/type-or-title" }}
{{ $type := "" }}
{{ define "partials/object-type-or-title" }}
{{ $type := "object" }}
{{ if .title }}
{{/*
If the property has a `title`, use that rather than `type`.
@ -176,32 +198,8 @@
{{ end }}
{{ $type = delimit (slice "{string: " (delimit $types "|") "}" ) "" }}
{{ else if reflect.IsSlice .type }}
{{/* It's legal to specify an array of types. Join them together in that case */}}
{{ $types := slice }}
{{ range .type }}
{{ $types = $types | append . }}
{{ end }}
{{ $type = delimit $types "|" }}
{{ else if and .oneOf (reflect.IsSlice .oneOf) }}
{{/*
This is like an array of types except some of the types probably have a schema.
Join them together too.
*/}}
{{ $types := slice }}
{{ range .oneOf }}
{{ $types = $types | append (partial "type-or-title" .) }}
{{ end }}
{{ $type = delimit $types "|" }}
{{ else }}
{{ $type = .type }}
{{ end }}
{{ return $type }}
{{ end }}

@ -42,6 +42,12 @@ except ImportError as e:
import_error("jsonschema", "jsonschema", "jsonschema", e)
raise
try:
import referencing
except ImportError as e:
import_error("referencing", "referencing", "referencing", e)
raise
try:
import yaml
except ImportError as e:
@ -56,13 +62,14 @@ def check_example_file(examplepath, schemapath):
with open(schemapath) as f:
schema = yaml.safe_load(f)
# $id as a URI with scheme is necessary to make registry resolver work.
fileurl = "file://" + os.path.abspath(schemapath)
schema["id"] = fileurl
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri})
schema["$id"] = fileurl
print ("Checking schema for: %r %r" % (examplepath, schemapath))
try:
validator = jsonschema.Draft202012Validator(schema, resolver)
registry = referencing.Registry(retrieve=helpers.load_resource_from_uri)
validator = jsonschema.validators.Draft202012Validator(schema, registry=registry)
validator.validate(example)
except Exception as e:
raise ValueError("Error validating JSON schema for %r %r" % (

@ -42,6 +42,12 @@ except ImportError as e:
import_error("jsonschema", "jsonschema", "jsonschema", e)
raise
try:
import referencing
except ImportError as e:
import_error("referencing", "referencing", "referencing", e)
raise
try:
import yaml
except ImportError as e:
@ -70,10 +76,12 @@ class SchemaDirReport:
self.errors += other_report.errors
def check_example(path, schema, example):
# URI with scheme is necessary to make RefResolver work.
# $id as a URI with scheme is necessary to make registry resolver work.
fileurl = "file://" + os.path.abspath(path)
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri})
validator = jsonschema.Draft202012Validator(schema, resolver)
schema["$id"] = fileurl
registry = referencing.Registry(retrieve=helpers.load_resource_from_uri)
validator = jsonschema.validators.Draft202012Validator(schema, registry=registry)
validator.validate(example)
@ -128,7 +136,7 @@ def check_schema_file(schema_path):
# Check schema is valid.
try:
validator = jsonschema.Draft202012Validator
validator = jsonschema.validators.Draft202012Validator
validator.check_schema(schema)
except Exception as e:
print(f"Failed to validate JSON schema: {e}")

@ -42,6 +42,12 @@ except ImportError as e:
import_error("jsonschema", "jsonschema", "jsonschema", e)
raise
try:
import referencing
except ImportError as e:
import_error("referencing", "referencing", "referencing", e)
raise
try:
import yaml
except ImportError as e:
@ -50,8 +56,11 @@ except ImportError as e:
def check_schema(filepath, example, schema):
resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": helpers.load_file_from_uri})
validator = jsonschema.Draft202012Validator(schema, resolver)
# $id as a URI with scheme is necessary to make registry resolver work.
schema["$id"] = filepath
registry = referencing.Registry(retrieve=helpers.load_resource_from_uri)
validator = jsonschema.validators.Draft202012Validator(schema, registry=registry)
validator.validate(example)

@ -19,6 +19,7 @@
import json
import os
import os.path
import referencing
import urllib.parse
import yaml
@ -84,4 +85,15 @@ def load_file_from_uri(path):
else:
# We have to assume it's YAML because some of the YAML examples
# do not have file extensions.
return yaml.safe_load(f)
return yaml.safe_load(f)
def load_resource_from_uri(path):
"""Load a JSON or YAML JSON Schema, as a `referencing.Resource` object, from
a file:// URI.
"""
contents = load_file_from_uri(path)
resource = referencing.Resource(
contents=contents,
specification=referencing.jsonschema.DRAFT202012
)
return resource

@ -1,10 +1,11 @@
# no doubt older versions would be fine for many of these but these were
# current at the time of writing
# we need at least version 4.0.0 for support of JSON Schema Draft 2020-12.
jsonschema == 4.17.3
# we need at least version 4.18.0 for support of referencing library.
jsonschema >= 4.18.0
referencing >= 0.28.4
python-jsonpath == 0.9.0
python-jsonpath >= 1.0.0
attrs >= 23.1.0
PyYAML >= 3.12
requests >= 2.18.4

Loading…
Cancel
Save