keyboard stuff
1"""Keyboard information script.
2
3Compile an info.json for a particular keyboard and pretty-print it.
4"""
5import json
6
7from argcomplete.completers import FilesCompleter
8from jsonschema import Draft202012Validator, RefResolver, validators
9from milc import cli
10from pathlib import Path
11
12from qmk.decorators import automagic_keyboard, automagic_keymap
13from qmk.info import info_json
14from qmk.json_encoders import InfoJSONEncoder
15from qmk.json_schema import compile_schema_store
16from qmk.keyboard import keyboard_completer, keyboard_folder
17from qmk.path import is_keyboard, normpath
18
19
20def pruning_validator(validator_class):
21 """Extends Draft202012Validator to remove properties that aren't specified in the schema.
22 """
23 validate_properties = validator_class.VALIDATORS["properties"]
24
25 def remove_additional_properties(validator, properties, instance, schema):
26 for prop in list(instance.keys()):
27 if prop not in properties:
28 del instance[prop]
29
30 for error in validate_properties(validator, properties, instance, schema):
31 yield error
32
33 return validators.extend(validator_class, {"properties": remove_additional_properties})
34
35
36def strip_info_json(kb_info_json):
37 """Remove the API-only properties from the info.json.
38 """
39 schema_store = compile_schema_store()
40 pruning_draft_validator = pruning_validator(Draft202012Validator)
41 schema = schema_store['qmk.keyboard.v1']
42 resolver = RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
43 validator = pruning_draft_validator(schema, resolver=resolver).validate
44
45 return validator(kb_info_json)
46
47
48@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
49@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
50@cli.argument('-o', '--output', arg_only=True, completer=FilesCompleter, help='Write the output the specified file, overwriting if necessary.')
51@cli.argument('-ow', '--overwrite', arg_only=True, action='store_true', help='Overwrite the existing info.json. (Overrides the location of --output)')
52@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
53@automagic_keyboard
54@automagic_keymap
55def generate_info_json(cli):
56 """Generate an info.json file for a keyboard
57 """
58 # Determine our keyboard(s)
59 if not cli.config.generate_info_json.keyboard:
60 cli.log.error('Missing parameter: --keyboard')
61 cli.subcommands['info'].print_help()
62 return False
63
64 if not is_keyboard(cli.config.generate_info_json.keyboard):
65 cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard)
66 return False
67
68 if cli.args.overwrite:
69 output_path = (Path('keyboards') / cli.config.generate_info_json.keyboard / 'info.json').resolve()
70
71 if cli.args.output:
72 cli.log.warning('Overwriting user supplied --output with %s', output_path)
73
74 cli.args.output = output_path
75
76 # Build the info.json file
77 kb_info_json = info_json(cli.config.generate_info_json.keyboard)
78 strip_info_json(kb_info_json)
79 info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder, sort_keys=True)
80
81 if cli.args.output:
82 # Write to a file
83 output_path = normpath(cli.args.output)
84
85 if output_path.exists():
86 cli.log.warning('Overwriting output file %s', output_path)
87
88 output_path.write_text(info_json_text + '\n')
89 cli.log.info('Wrote info.json to %s.', output_path)
90
91 else:
92 # Display the results
93 print(info_json_text)