json_schema.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. """Functions that help us generate and use info.json files.
  2. """
  3. import json
  4. from collections.abc import Mapping
  5. from functools import lru_cache
  6. from pathlib import Path
  7. import hjson
  8. import jsonschema
  9. from milc import cli
  10. @lru_cache(maxsize=0)
  11. def json_load(json_file):
  12. """Load a json file from disk.
  13. Note: file must be a Path object.
  14. """
  15. try:
  16. return hjson.load(json_file.open(encoding='utf-8'))
  17. except (json.decoder.JSONDecodeError, hjson.HjsonDecodeError) as e:
  18. cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
  19. exit(1)
  20. except Exception as e:
  21. cli.log.error('Unknown error attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
  22. if cli.args.verbose:
  23. cli.log.exception(e)
  24. exit(1)
  25. @lru_cache(maxsize=0)
  26. def load_jsonschema(schema_name):
  27. """Read a jsonschema file from disk.
  28. """
  29. if Path(schema_name).exists():
  30. return json_load(schema_name)
  31. schema_path = Path(f'data/schemas/{schema_name}.jsonschema')
  32. if not schema_path.exists():
  33. schema_path = Path('data/schemas/false.jsonschema')
  34. return json_load(schema_path)
  35. @lru_cache(maxsize=0)
  36. def compile_schema_store():
  37. """Compile all our schemas into a schema store.
  38. """
  39. schema_store = {}
  40. for schema_file in Path('data/schemas').glob('*.jsonschema'):
  41. schema_data = load_jsonschema(schema_file)
  42. if not isinstance(schema_data, dict):
  43. cli.log.debug('Skipping schema file %s', schema_file)
  44. continue
  45. schema_store[schema_data['$id']] = schema_data
  46. return schema_store
  47. @lru_cache(maxsize=0)
  48. def create_validator(schema):
  49. """Creates a validator for the given schema id.
  50. """
  51. schema_store = compile_schema_store()
  52. resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
  53. return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate
  54. def validate(data, schema):
  55. """Validates data against a schema.
  56. """
  57. validator = create_validator(schema)
  58. return validator(data)
  59. def deep_update(origdict, newdict):
  60. """Update a dictionary in place, recursing to do a depth-first deep copy.
  61. """
  62. for key, value in newdict.items():
  63. if isinstance(value, Mapping):
  64. origdict[key] = deep_update(origdict.get(key, {}), value)
  65. else:
  66. origdict[key] = value
  67. return origdict