diff options
| author | LongerHV <46924944+LongerHV@users.noreply.github.com> | 2020-12-29 20:34:48 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-29 11:34:48 -0800 |
| commit | 221d8fd8669ff528bfedd01f41486f5298d960e1 (patch) | |
| tree | c82fe3f0e032ce2057039b4baadbb1f91c0608ed | |
| parent | 3300164065949e6bc9423632ccbcd0022be8074d (diff) | |
| download | qmk_firmware-221d8fd8669ff528bfedd01f41486f5298d960e1.tar.gz qmk_firmware-221d8fd8669ff528bfedd01f41486f5298d960e1.zip | |
[CLI] Add stdin support for json2c command (#11289)
* Implement stdin for json2c command
* Refactor
* Handle json decode error
* Add stdin support for c2json cli command
* Refactor to prevent code duplication
* Change exit(1) to return False in c2json command
* Remove unused import
| -rw-r--r-- | lib/python/qmk/cli/c2json.py | 22 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/json2c.py | 32 | ||||
| -rw-r--r-- | lib/python/qmk/keymap.py | 22 | ||||
| -rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 29 |
4 files changed, 70 insertions, 35 deletions
diff --git a/lib/python/qmk/cli/c2json.py b/lib/python/qmk/cli/c2json.py index 8c8bd1f57..2b3bb774f 100644 --- a/lib/python/qmk/cli/c2json.py +++ b/lib/python/qmk/cli/c2json.py | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | """Generate a keymap.json from a keymap.c file. | 1 | """Generate a keymap.json from a keymap.c file. |
| 2 | """ | 2 | """ |
| 3 | import json | 3 | import json |
| 4 | import sys | ||
| 5 | 4 | ||
| 6 | from milc import cli | 5 | from milc import cli |
| 7 | 6 | ||
| @@ -21,19 +20,14 @@ def c2json(cli): | |||
| 21 | 20 | ||
| 22 | This command uses the `qmk.keymap` module to generate a keymap.json from a keymap.c file. The generated keymap is written to stdout, or to a file if -o is provided. | 21 | This command uses the `qmk.keymap` module to generate a keymap.json from a keymap.c file. The generated keymap is written to stdout, or to a file if -o is provided. |
| 23 | """ | 22 | """ |
| 24 | cli.args.filename = qmk.path.normpath(cli.args.filename) | 23 | if cli.args.filename != '-': |
| 24 | cli.args.filename = qmk.path.normpath(cli.args.filename) | ||
| 25 | 25 | ||
| 26 | # Error checking | 26 | # Error checking |
| 27 | if not cli.args.filename.exists(): | 27 | if not cli.args.filename.exists(): |
| 28 | cli.log.error('C file does not exist!') | 28 | cli.log.error('C file does not exist!') |
| 29 | cli.print_usage() | 29 | cli.print_usage() |
| 30 | exit(1) | 30 | return False |
| 31 | |||
| 32 | if str(cli.args.filename) == '-': | ||
| 33 | # TODO(skullydazed/anyone): Read file contents from STDIN | ||
| 34 | cli.log.error('Reading from STDIN is not (yet) supported.') | ||
| 35 | cli.print_usage() | ||
| 36 | exit(1) | ||
| 37 | 31 | ||
| 38 | # Environment processing | 32 | # Environment processing |
| 39 | if cli.args.output == ('-'): | 33 | if cli.args.output == ('-'): |
| @@ -47,7 +41,7 @@ def c2json(cli): | |||
| 47 | keymap_json = qmk.keymap.generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers']) | 41 | keymap_json = qmk.keymap.generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers']) |
| 48 | except KeyError: | 42 | except KeyError: |
| 49 | cli.log.error('Something went wrong. Try to use --no-cpp.') | 43 | cli.log.error('Something went wrong. Try to use --no-cpp.') |
| 50 | sys.exit(1) | 44 | return False |
| 51 | 45 | ||
| 52 | if cli.args.output: | 46 | if cli.args.output: |
| 53 | cli.args.output.parent.mkdir(parents=True, exist_ok=True) | 47 | cli.args.output.parent.mkdir(parents=True, exist_ok=True) |
diff --git a/lib/python/qmk/cli/json2c.py b/lib/python/qmk/cli/json2c.py index 426078063..97d8fb0c3 100755 --- a/lib/python/qmk/cli/json2c.py +++ b/lib/python/qmk/cli/json2c.py | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | """Generate a keymap.c from a configurator export. | 1 | """Generate a keymap.c from a configurator export. |
| 2 | """ | 2 | """ |
| 3 | import json | 3 | import json |
| 4 | import sys | ||
| 4 | 5 | ||
| 5 | from milc import cli | 6 | from milc import cli |
| 6 | 7 | ||
| @@ -17,26 +18,31 @@ def json2c(cli): | |||
| 17 | 18 | ||
| 18 | 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. | 19 | 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. |
| 19 | """ | 20 | """ |
| 20 | # Error checking | ||
| 21 | if cli.args.filename and cli.args.filename.name == '-': | ||
| 22 | # TODO(skullydazed/anyone): Read file contents from STDIN | ||
| 23 | cli.log.error('Reading from STDIN is not (yet) supported.') | ||
| 24 | cli.print_usage() | ||
| 25 | return False | ||
| 26 | 21 | ||
| 27 | if not cli.args.filename.exists(): | 22 | try: |
| 28 | cli.log.error('JSON file does not exist!') | 23 | # Parse the configurator from stdin |
| 29 | cli.print_usage() | 24 | if cli.args.filename and cli.args.filename.name == '-': |
| 25 | user_keymap = json.load(sys.stdin) | ||
| 26 | |||
| 27 | else: | ||
| 28 | # Error checking | ||
| 29 | if not cli.args.filename.exists(): | ||
| 30 | cli.log.error('JSON file does not exist!') | ||
| 31 | return False | ||
| 32 | |||
| 33 | # Parse the configurator json file | ||
| 34 | else: | ||
| 35 | user_keymap = json.loads(cli.args.filename.read_text()) | ||
| 36 | |||
| 37 | except json.decoder.JSONDecodeError as ex: | ||
| 38 | cli.log.error('The JSON input does not appear to be valid.') | ||
| 39 | cli.log.error(ex) | ||
| 30 | return False | 40 | return False |
| 31 | 41 | ||
| 32 | # Environment processing | 42 | # Environment processing |
| 33 | if cli.args.output and cli.args.output.name == '-': | 43 | if cli.args.output and cli.args.output.name == '-': |
| 34 | cli.args.output = None | 44 | cli.args.output = None |
| 35 | 45 | ||
| 36 | # Parse the configurator json | ||
| 37 | with cli.args.filename.open('r') as fd: | ||
| 38 | user_keymap = json.load(fd) | ||
| 39 | |||
| 40 | # Generate the keymap | 46 | # Generate the keymap |
| 41 | keymap_c = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) | 47 | keymap_c = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) |
| 42 | 48 | ||
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index 31c61ae6a..266532f50 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | from pathlib import Path | 3 | from pathlib import Path |
| 4 | import json | 4 | import json |
| 5 | import subprocess | 5 | import subprocess |
| 6 | import sys | ||
| 6 | 7 | ||
| 7 | from pygments.lexers.c_cpp import CLexer | 8 | from pygments.lexers.c_cpp import CLexer |
| 8 | from pygments.token import Token | 9 | from pygments.token import Token |
| @@ -312,16 +313,17 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa | |||
| 312 | return sorted(names) | 313 | return sorted(names) |
| 313 | 314 | ||
| 314 | 315 | ||
| 315 | def _c_preprocess(path): | 316 | def _c_preprocess(path, stdin=None): |
| 316 | """ Run a file through the C pre-processor | 317 | """ Run a file through the C pre-processor |
| 317 | 318 | ||
| 318 | Args: | 319 | Args: |
| 319 | path: path of the keymap.c file | 320 | path: path of the keymap.c file (set None to use stdin) |
| 321 | stdin: stdin pipe (e.g. sys.stdin) | ||
| 320 | 322 | ||
| 321 | Returns: | 323 | Returns: |
| 322 | the stdout of the pre-processor | 324 | the stdout of the pre-processor |
| 323 | """ | 325 | """ |
| 324 | pre_processed_keymap = qmk.commands.run(['cpp', path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) | 326 | pre_processed_keymap = qmk.commands.run(['cpp', path] if path else ['cpp'], stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) |
| 325 | return pre_processed_keymap.stdout | 327 | return pre_processed_keymap.stdout |
| 326 | 328 | ||
| 327 | 329 | ||
| @@ -451,17 +453,23 @@ def parse_keymap_c(keymap_file, use_cpp=True): | |||
| 451 | Currently only cares about the keymaps array. | 453 | Currently only cares about the keymaps array. |
| 452 | 454 | ||
| 453 | Args: | 455 | Args: |
| 454 | keymap_file: path of the keymap.c file | 456 | keymap_file: path of the keymap.c file (or '-' to use stdin) |
| 455 | 457 | ||
| 456 | use_cpp: if True, pre-process the file with the C pre-processor | 458 | use_cpp: if True, pre-process the file with the C pre-processor |
| 457 | 459 | ||
| 458 | Returns: | 460 | Returns: |
| 459 | a dictionary containing the parsed keymap | 461 | a dictionary containing the parsed keymap |
| 460 | """ | 462 | """ |
| 461 | if use_cpp: | 463 | if keymap_file == '-': |
| 462 | keymap_file = _c_preprocess(keymap_file) | 464 | if use_cpp: |
| 465 | keymap_file = _c_preprocess(None, sys.stdin) | ||
| 466 | else: | ||
| 467 | keymap_file = sys.stdin.read() | ||
| 463 | else: | 468 | else: |
| 464 | keymap_file = keymap_file.read_text() | 469 | if use_cpp: |
| 470 | keymap_file = _c_preprocess(keymap_file) | ||
| 471 | else: | ||
| 472 | keymap_file = keymap_file.read_text() | ||
| 465 | 473 | ||
| 466 | keymap = dict() | 474 | keymap = dict() |
| 467 | keymap['layers'] = _get_layers(keymap_file) | 475 | keymap['layers'] = _get_layers(keymap_file) |
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index 08e80f2c9..efd9f6cf6 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
| @@ -8,11 +8,20 @@ is_windows = 'windows' in platform.platform().lower() | |||
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | def check_subcommand(command, *args): | 10 | def check_subcommand(command, *args): |
| 11 | cmd = ['bin/qmk', command] + list(args) | 11 | cmd = ['bin/qmk', command, *args] |
| 12 | result = run(cmd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) | 12 | result = run(cmd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) |
| 13 | return result | 13 | return result |
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | def check_subcommand_stdin(file_to_read, command, *args): | ||
| 17 | """Pipe content of a file to a command and return output. | ||
| 18 | """ | ||
| 19 | with open(file_to_read) as my_file: | ||
| 20 | cmd = ['bin/qmk', command, *args] | ||
| 21 | result = run(cmd, stdin=my_file, stdout=PIPE, stderr=STDOUT, universal_newlines=True) | ||
| 22 | return result | ||
| 23 | |||
| 24 | |||
| 16 | def check_returncode(result, expected=[0]): | 25 | def check_returncode(result, expected=[0]): |
| 17 | """Print stdout if `result.returncode` does not match `expected`. | 26 | """Print stdout if `result.returncode` does not match `expected`. |
| 18 | """ | 27 | """ |
| @@ -129,6 +138,12 @@ def test_json2c(): | |||
| 129 | 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' | 138 | 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' |
| 130 | 139 | ||
| 131 | 140 | ||
| 141 | def test_json2c_stdin(): | ||
| 142 | result = check_subcommand_stdin('keyboards/handwired/onekey/keymaps/default_json/keymap.json', 'json2c', '-') | ||
| 143 | check_returncode(result) | ||
| 144 | 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' | ||
| 145 | |||
| 146 | |||
| 132 | def test_info(): | 147 | def test_info(): |
| 133 | result = check_subcommand('info', '-kb', 'handwired/onekey/pytest') | 148 | result = check_subcommand('info', '-kb', 'handwired/onekey/pytest') |
| 134 | check_returncode(result) | 149 | check_returncode(result) |
| @@ -186,6 +201,18 @@ def test_c2json_nocpp(): | |||
| 186 | assert result.stdout.strip() == '{"keyboard": "handwired/onekey/pytest", "documentation": "This file is a keymap.json file for handwired/onekey/pytest", "keymap": "default", "layout": "LAYOUT", "layers": [["KC_ENTER"]]}' | 201 | assert result.stdout.strip() == '{"keyboard": "handwired/onekey/pytest", "documentation": "This file is a keymap.json file for handwired/onekey/pytest", "keymap": "default", "layout": "LAYOUT", "layers": [["KC_ENTER"]]}' |
| 187 | 202 | ||
| 188 | 203 | ||
| 204 | def test_c2json_stdin(): | ||
| 205 | result = check_subcommand_stdin("keyboards/handwired/onekey/keymaps/default/keymap.c", "c2json", "-kb", "handwired/onekey/pytest", "-km", "default", "-") | ||
| 206 | check_returncode(result) | ||
| 207 | assert result.stdout.strip() == '{"keyboard": "handwired/onekey/pytest", "documentation": "This file is a keymap.json file for handwired/onekey/pytest", "keymap": "default", "layout": "LAYOUT_ortho_1x1", "layers": [["KC_A"]]}' | ||
| 208 | |||
| 209 | |||
| 210 | def test_c2json_nocpp_stdin(): | ||
| 211 | result = check_subcommand_stdin("keyboards/handwired/onekey/keymaps/pytest_nocpp/keymap.c", "c2json", "--no-cpp", "-kb", "handwired/onekey/pytest", "-km", "default", "-") | ||
| 212 | check_returncode(result) | ||
| 213 | assert result.stdout.strip() == '{"keyboard": "handwired/onekey/pytest", "documentation": "This file is a keymap.json file for handwired/onekey/pytest", "keymap": "default", "layout": "LAYOUT", "layers": [["KC_ENTER"]]}' | ||
| 214 | |||
| 215 | |||
| 189 | def test_clean(): | 216 | def test_clean(): |
| 190 | result = check_subcommand('clean', '-a') | 217 | result = check_subcommand('clean', '-a') |
| 191 | check_returncode(result) | 218 | check_returncode(result) |
