Răsfoiți Sursa

Merge remote-tracking branch 'origin/master' into develop

QMK Bot 3 ani în urmă
părinte
comite
36e47cb3aa

+ 6 - 1
data/schemas/keymap.jsonschema

@@ -53,5 +53,10 @@
             "type": "string",
             "description": "asdf"
         }
-    }
+    },
+    "required": [
+        "keyboard",
+        "layout",
+        "layers"
+    ]
 }

+ 3 - 10
lib/python/qmk/cli/json2c.py

@@ -1,12 +1,11 @@
 """Generate a keymap.c from a configurator export.
 """
-import json
-
 from argcomplete.completers import FilesCompleter
 from milc import cli
 
 import qmk.keymap
 import qmk.path
+from qmk.commands import parse_configurator_json
 
 
 @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@@ -19,14 +18,8 @@ def json2c(cli):
     This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided.
     """
 
-    try:
-        # Parse the configurator from json file (or stdin)
-        user_keymap = json.load(cli.args.filename)
-
-    except json.decoder.JSONDecodeError as ex:
-        cli.log.error('The JSON input does not appear to be valid.')
-        cli.log.error(ex)
-        return False
+    # Parse the configurator from json file (or stdin)
+    user_keymap = parse_configurator_json(cli.args.filename)
 
     # Environment processing
     if cli.args.output and cli.args.output.name == '-':

+ 15 - 4
lib/python/qmk/commands.py

@@ -1,6 +1,5 @@
 """Helper functions for commands.
 """
-import json
 import os
 import sys
 import shutil
@@ -9,10 +8,11 @@ from subprocess import DEVNULL
 from time import strftime
 
 from milc import cli
+import jsonschema
 
 import qmk.keymap
 from qmk.constants import QMK_FIRMWARE, KEYBOARD_OUTPUT_PREFIX
-from qmk.json_schema import json_load
+from qmk.json_schema import json_load, validate
 
 time_fmt = '%Y-%m-%d-%H:%M:%S'
 
@@ -185,6 +185,10 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
 
         A command to run to compile and flash the C file.
     """
+    # In case the user passes a keymap.json from a keymap directory directly to the CLI.
+    # e.g.: qmk compile - < keyboards/clueboard/california/keymaps/default/keymap.json
+    user_keymap["keymap"] = user_keymap.get("keymap", "default_json")
+
     # Write the keymap.c file
     keyboard_filesafe = user_keymap['keyboard'].replace('/', '_')
     target = f'{keyboard_filesafe}_{user_keymap["keymap"]}'
@@ -248,8 +252,15 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
 def parse_configurator_json(configurator_file):
     """Open and parse a configurator json export
     """
-    # FIXME(skullydazed/anyone): Add validation here
-    user_keymap = json.load(configurator_file)
+    user_keymap = json_load(configurator_file)
+    # Validate against the jsonschema
+    try:
+        validate(user_keymap, 'qmk.keymap.v1')
+
+    except jsonschema.ValidationError as e:
+        cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}')
+        exit(1)
+
     orig_keyboard = user_keymap['keyboard']
     aliases = json_load(Path('data/mappings/keyboard_aliases.json'))
 

+ 6 - 2
lib/python/qmk/json_schema.py

@@ -16,7 +16,11 @@ def json_load(json_file):
     Note: file must be a Path object.
     """
     try:
-        return hjson.load(json_file.open(encoding='utf-8'))
+        # Get the IO Stream for Path objects
+        # Not necessary if the data is provided via stdin
+        if isinstance(json_file, Path):
+            json_file = json_file.open(encoding='utf-8')
+        return hjson.load(json_file)
 
     except (json.decoder.JSONDecodeError, hjson.HjsonDecodeError) as e:
         cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
@@ -62,7 +66,7 @@ def create_validator(schema):
     """Creates a validator for the given schema id.
     """
     schema_store = compile_schema_store()
-    resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
+    resolver = jsonschema.RefResolver.from_schema(schema_store[schema], store=schema_store)
 
     return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate
 

+ 5 - 1
lib/python/qmk/path.py

@@ -70,9 +70,13 @@ def normpath(path):
 
 
 class FileType(argparse.FileType):
+    def __init__(self, encoding='UTF-8'):
+        # Use UTF8 by default for stdin
+        return super().__init__(encoding=encoding)
+
     def __call__(self, string):
         """normalize and check exists
             otherwise magic strings like '-' for stdin resolve to bad paths
         """
         norm = normpath(string)
-        return super().__call__(norm if norm.exists() else string)
+        return norm if norm.exists() else super().__call__(string)

+ 12 - 0
lib/python/qmk/tests/test_cli_commands.py

@@ -156,6 +156,18 @@ def test_json2c_stdin():
     assert result.stdout == '#include QMK_KEYBOARD_H\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\t[0] = LAYOUT_ortho_1x1(KC_A)};\n\n'
 
 
+def test_json2c_wrong_json():
+    result = check_subcommand('json2c', 'keyboards/handwired/pytest/info.json')
+    check_returncode(result, [1])
+    assert 'Invalid JSON keymap' in result.stdout
+
+
+def test_json2c_no_json():
+    result = check_subcommand('json2c', 'keyboards/handwired/pytest/pytest.h')
+    check_returncode(result, [1])
+    assert 'Invalid JSON encountered' in result.stdout
+
+
 def test_info():
     result = check_subcommand('info', '-kb', 'handwired/pytest/basic')
     check_returncode(result)