Source code for metatrain.utils.jsonschema
import difflib
from typing import Any, Dict, Optional
import jsonschema
from jsonschema.exceptions import ValidationError
from jsonschema.protocols import Validator
[docs]
def validate(
instance: Dict,
schema: Any,
cls: Optional[Validator] = None,
*args: Any,
**kwargs: Any,
) -> None:
r"""Validate an instance under the given schema.
Function similar to :py:class:`jsonschema.validate` but displaying only the human
readable error message without showing the reference schema and path if the instance
is invalid. In addition, if the error is caused by unallowed
``additionalProperties`` the closest matching properties will be suggested.
:param instance: Instance to validate
:param schema: Schema to validate with
:param cls: Validator class to use.
:param \*args: Additional positional arguments to pass to the validator
:param \*\*kwargs: Additional keyword arguments to pass to the validator
:raises ValueError: If the instance is invalid
:raises jsonschema.exceptions.SchemaError: If the schema itself is invalid
"""
try:
jsonschema.validate(instance, schema, cls=cls, *args, **kwargs) # noqa: B026
except ValidationError as error:
message = error.message
if error.validator == "additionalProperties":
# Change error message to be clearer for users
message = message.replace(
"Additional properties are not allowed", "Unrecognized options"
)
known_properties = error.schema["properties"].keys()
unknown_properties = error.instance.keys() - known_properties
closest_matches = []
for name in unknown_properties:
closest_match = difflib.get_close_matches(
word=name, possibilities=known_properties
)
if closest_match:
closest_matches.append(f"'{closest_match[0]}'")
if closest_matches:
message += f". Did you mean {', '.join(closest_matches)}?"
message = f"Invalid option for {error.json_path[2:]}: {message}"
message += f"\nExpected {error.schema}"
raise ValueError(message) from error