json_schema.py 2.0 KB

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