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