diff options
author | Zach White <skullydazed@gmail.com> | 2021-06-24 20:48:53 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-24 20:48:53 -0700 |
commit | b908275354ba6cd9dd4d393dbbedfbd2ad0f316d (patch) | |
tree | 7efbb70c51fb917d203b629feb9eadf169f71c91 /lib/python | |
parent | 9d4412cb8bf9512952118b190fedea72e33d28cb (diff) | |
download | qmk_firmware-b908275354ba6cd9dd4d393dbbedfbd2ad0f316d.tar.gz qmk_firmware-b908275354ba6cd9dd4d393dbbedfbd2ad0f316d.zip |
Optimize our jsonschema by using refs (#13271)
* fix some broken info.json files
* optimize our jsonschema using refs
* fix formatting after vscode broke it
* make flake8 happy
* cleanup
* make our schema validation more compact and flexible
Diffstat (limited to 'lib/python')
-rwxr-xr-x | lib/python/qmk/cli/format/json.py | 5 | ||||
-rw-r--r-- | lib/python/qmk/info.py | 6 | ||||
-rw-r--r-- | lib/python/qmk/json_schema.py | 34 |
3 files changed, 25 insertions, 20 deletions
diff --git a/lib/python/qmk/cli/format/json.py b/lib/python/qmk/cli/format/json.py index 1358c70e7..19d504491 100755 --- a/lib/python/qmk/cli/format/json.py +++ b/lib/python/qmk/cli/format/json.py | |||
@@ -8,7 +8,7 @@ from jsonschema import ValidationError | |||
8 | from milc import cli | 8 | from milc import cli |
9 | 9 | ||
10 | from qmk.info import info_json | 10 | from qmk.info import info_json |
11 | from qmk.json_schema import json_load, keyboard_validate | 11 | from qmk.json_schema import json_load, validate |
12 | from qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder | 12 | from qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder |
13 | from qmk.path import normpath | 13 | from qmk.path import normpath |
14 | 14 | ||
@@ -23,14 +23,13 @@ def format_json(cli): | |||
23 | 23 | ||
24 | if cli.args.format == 'auto': | 24 | if cli.args.format == 'auto': |
25 | try: | 25 | try: |
26 | keyboard_validate(json_file) | 26 | validate(json_file, 'qmk.keyboard.v1') |
27 | json_encoder = InfoJSONEncoder | 27 | json_encoder = InfoJSONEncoder |
28 | 28 | ||
29 | except ValidationError as e: | 29 | except ValidationError as e: |
30 | cli.log.warning('File %s did not validate as a keyboard:\n\t%s', cli.args.json_file, e) | 30 | cli.log.warning('File %s did not validate as a keyboard:\n\t%s', cli.args.json_file, e) |
31 | cli.log.info('Treating %s as a keymap file.', cli.args.json_file) | 31 | cli.log.info('Treating %s as a keymap file.', cli.args.json_file) |
32 | json_encoder = KeymapJSONEncoder | 32 | json_encoder = KeymapJSONEncoder |
33 | |||
34 | elif cli.args.format == 'keyboard': | 33 | elif cli.args.format == 'keyboard': |
35 | json_encoder = InfoJSONEncoder | 34 | json_encoder = InfoJSONEncoder |
36 | elif cli.args.format == 'keymap': | 35 | elif cli.args.format == 'keymap': |
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index a70bd4c31..5525f0fe6 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py | |||
@@ -9,7 +9,7 @@ from milc import cli | |||
9 | 9 | ||
10 | from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS | 10 | from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS |
11 | from qmk.c_parse import find_layouts | 11 | from qmk.c_parse import find_layouts |
12 | from qmk.json_schema import deep_update, json_load, keyboard_validate, keyboard_api_validate | 12 | from qmk.json_schema import deep_update, json_load, validate |
13 | from qmk.keyboard import config_h, rules_mk | 13 | from qmk.keyboard import config_h, rules_mk |
14 | from qmk.keymap import list_keymaps | 14 | from qmk.keymap import list_keymaps |
15 | from qmk.makefile import parse_rules_mk_file | 15 | from qmk.makefile import parse_rules_mk_file |
@@ -66,7 +66,7 @@ def info_json(keyboard): | |||
66 | 66 | ||
67 | # Validate against the jsonschema | 67 | # Validate against the jsonschema |
68 | try: | 68 | try: |
69 | keyboard_api_validate(info_data) | 69 | validate(info_data, 'qmk.api.keyboard.v1') |
70 | 70 | ||
71 | except jsonschema.ValidationError as e: | 71 | except jsonschema.ValidationError as e: |
72 | json_path = '.'.join([str(p) for p in e.absolute_path]) | 72 | json_path = '.'.join([str(p) for p in e.absolute_path]) |
@@ -490,7 +490,7 @@ def merge_info_jsons(keyboard, info_data): | |||
490 | continue | 490 | continue |
491 | 491 | ||
492 | try: | 492 | try: |
493 | keyboard_validate(new_info_data) | 493 | validate(new_info_data, 'qmk.keyboard.v1') |
494 | except jsonschema.ValidationError as e: | 494 | except jsonschema.ValidationError as e: |
495 | json_path = '.'.join([str(p) for p in e.absolute_path]) | 495 | json_path = '.'.join([str(p) for p in e.absolute_path]) |
496 | cli.log.error('Not including data from file: %s', info_file) | 496 | cli.log.error('Not including data from file: %s', info_file) |
diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py index 077dfcaa9..3e5663a29 100644 --- a/lib/python/qmk/json_schema.py +++ b/lib/python/qmk/json_schema.py | |||
@@ -24,9 +24,10 @@ def json_load(json_file): | |||
24 | 24 | ||
25 | def load_jsonschema(schema_name): | 25 | def load_jsonschema(schema_name): |
26 | """Read a jsonschema file from disk. | 26 | """Read a jsonschema file from disk. |
27 | |||
28 | FIXME(skullydazed/anyone): Refactor to make this a public function. | ||
29 | """ | 27 | """ |
28 | if Path(schema_name).exists(): | ||
29 | return json_load(schema_name) | ||
30 | |||
30 | schema_path = Path(f'data/schemas/{schema_name}.jsonschema') | 31 | schema_path = Path(f'data/schemas/{schema_name}.jsonschema') |
31 | 32 | ||
32 | if not schema_path.exists(): | 33 | if not schema_path.exists(): |
@@ -35,28 +36,33 @@ def load_jsonschema(schema_name): | |||
35 | return json_load(schema_path) | 36 | return json_load(schema_path) |
36 | 37 | ||
37 | 38 | ||
38 | def keyboard_validate(data): | 39 | def create_validator(schema): |
39 | """Validates data against the keyboard jsonschema. | 40 | """Creates a validator for the given schema id. |
40 | """ | 41 | """ |
41 | schema = load_jsonschema('keyboard') | 42 | schema_store = {} |
42 | validator = jsonschema.Draft7Validator(schema).validate | ||
43 | 43 | ||
44 | return validator(data) | 44 | for schema_file in Path('data/schemas').glob('*.jsonschema'): |
45 | schema_data = load_jsonschema(schema_file) | ||
46 | if not isinstance(schema_data, dict): | ||
47 | cli.log.debug('Skipping schema file %s', schema_file) | ||
48 | continue | ||
49 | schema_store[schema_data['$id']] = schema_data | ||
50 | |||
51 | resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store) | ||
52 | |||
53 | return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate | ||
45 | 54 | ||
46 | 55 | ||
47 | def keyboard_api_validate(data): | 56 | def validate(data, schema): |
48 | """Validates data against the api_keyboard jsonschema. | 57 | """Validates data against a schema. |
49 | """ | 58 | """ |
50 | base = load_jsonschema('keyboard') | 59 | validator = create_validator(schema) |
51 | relative = load_jsonschema('api_keyboard') | ||
52 | resolver = jsonschema.RefResolver.from_schema(base) | ||
53 | validator = jsonschema.Draft7Validator(relative, resolver=resolver).validate | ||
54 | 60 | ||
55 | return validator(data) | 61 | return validator(data) |
56 | 62 | ||
57 | 63 | ||
58 | def deep_update(origdict, newdict): | 64 | def deep_update(origdict, newdict): |
59 | """Update a dictionary in place, recursing to do a deep copy. | 65 | """Update a dictionary in place, recursing to do a depth-first deep copy. |
60 | """ | 66 | """ |
61 | for key, value in newdict.items(): | 67 | for key, value in newdict.items(): |
62 | if isinstance(value, Mapping): | 68 | if isinstance(value, Mapping): |