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) |