#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019 Chris Sewell
#
# This file is part of aiida-crystal17.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms and conditions
# of version 3 of the GNU Lesser General Public License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
"""Utility functions for validating JSON objects against schemas."""
import io
import json
import os
import importlib_resources
import jsonschema
from aiida_crystal17.validation import schemas
[docs]def load_schema(name):
"""Read and return a JSON schema.
If the name is an absolute path, it will be used as is, otherwise
it will be loaded as resource from the internal json schema module.
Parameters
----------
name: str
Returns
-------
dict
"""
if os.path.isabs(name):
with io.open(name) as jfile:
schema = json.load(jfile)
else:
schema = json.loads(importlib_resources.read_text(schemas, name))
return schema
[docs]def load_validator(schema):
"""Create a validator for a schema.
Parameters
----------
schema : str or dict
schema or path to schema
Returns
-------
jsonschema.IValidator
the validator to use
"""
if isinstance(schema, str):
schema = load_schema(schema)
validator_cls = jsonschema.validators.validator_for(schema)
validator_cls.check_schema(schema)
# by default, only validates lists
def is_array(checker, instance):
return isinstance(instance, (tuple, list))
type_checker = validator_cls.TYPE_CHECKER.redefine("array", is_array)
validator_cls = jsonschema.validators.extend(
validator_cls, type_checker=type_checker
)
validator = validator_cls(schema=schema)
return validator
[docs]def validate_against_schema(data, schema):
"""Validate json-type data against a schema.
Parameters
----------
data: dict
schema: dict or str
schema, name of schema resource, or absolute path to a schema
Raises
------
jsonschema.exceptions.SchemaError
if the schema is invalid
jsonschema.exceptions.ValidationError
if the instance is invalid
Returns
-------
bool
return True if validated
"""
validator = load_validator(schema)
# validator.validate(data)
errors = sorted(validator.iter_errors(data), key=lambda e: e.path)
if errors:
raise jsonschema.ValidationError(
"\n".join(
[
"- {} [key path: '{}']".format(
error.message, "/".join([str(p) for p in error.path])
)
for error in errors
]
)
)
return True