[CLI] Don't exit() when certain exceptions occur. (#23442)

This commit is contained in:
Nick Brassel 2024-06-15 19:37:47 +10:00 committed by GitHub
parent d4654ab893
commit 0262161914
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 55 additions and 12 deletions

View File

@ -2,6 +2,7 @@
""" """
from milc import cli from milc import cli
from qmk.search import filter_help, search_keymap_targets from qmk.search import filter_help, search_keymap_targets
from qmk.util import maybe_exit_config
@cli.argument( @cli.argument(
@ -19,6 +20,8 @@ from qmk.search import filter_help, search_keymap_targets
def find(cli): def find(cli):
"""Search through all keyboards and keymaps for a given search criteria. """Search through all keyboards and keymaps for a given search criteria.
""" """
maybe_exit_config(should_exit=False, should_reraise=True)
targets = search_keymap_targets([('all', cli.config.find.keymap)], cli.args.filter) targets = search_keymap_targets([('all', cli.config.find.keymap)], cli.args.filter)
for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)): for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)):
print(f'{target}') print(f'{target}')

View File

@ -27,7 +27,6 @@ Example:
For full documentation, see QMK Docs For full documentation, see QMK Docs
""" """
import sys
import textwrap import textwrap
from typing import Any, Dict, Iterator, List, Tuple from typing import Any, Dict, Iterator, List, Tuple
@ -38,6 +37,7 @@ from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer, locate_keymap from qmk.keymap import keymap_completer, locate_keymap
from qmk.path import normpath from qmk.path import normpath
from qmk.util import maybe_exit
KC_A = 4 KC_A = 4
KC_SPC = 0x2c KC_SPC = 0x2c
@ -88,16 +88,16 @@ def parse_file(file_name: str) -> List[Tuple[str, str]]:
# Check that `typo` is valid. # Check that `typo` is valid.
if not (all([c in TYPO_CHARS for c in typo])): if not (all([c in TYPO_CHARS for c in typo])):
cli.log.error('{fg_red}Error:%d:{fg_reset} Typo "{fg_cyan}%s{fg_reset}" has characters other than a-z, \' and :.', line_number, typo) cli.log.error('{fg_red}Error:%d:{fg_reset} Typo "{fg_cyan}%s{fg_reset}" has characters other than a-z, \' and :.', line_number, typo)
sys.exit(1) maybe_exit(1)
for other_typo in typos: for other_typo in typos:
if typo in other_typo or other_typo in typo: if typo in other_typo or other_typo in typo:
cli.log.error('{fg_red}Error:%d:{fg_reset} Typos may not be substrings of one another, otherwise the longer typo would never trigger: "{fg_cyan}%s{fg_reset}" vs. "{fg_cyan}%s{fg_reset}".', line_number, typo, other_typo) cli.log.error('{fg_red}Error:%d:{fg_reset} Typos may not be substrings of one another, otherwise the longer typo would never trigger: "{fg_cyan}%s{fg_reset}" vs. "{fg_cyan}%s{fg_reset}".', line_number, typo, other_typo)
sys.exit(1) maybe_exit(1)
if len(typo) < 5: if len(typo) < 5:
cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} It is suggested that typos are at least 5 characters long to avoid false triggers: "{fg_cyan}%s{fg_reset}"', line_number, typo) cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} It is suggested that typos are at least 5 characters long to avoid false triggers: "{fg_cyan}%s{fg_reset}"', line_number, typo)
if len(typo) > 127: if len(typo) > 127:
cli.log.error('{fg_red}Error:%d:{fg_reset} Typo exceeds 127 chars: "{fg_cyan}%s{fg_reset}"', line_number, typo) cli.log.error('{fg_red}Error:%d:{fg_reset} Typo exceeds 127 chars: "{fg_cyan}%s{fg_reset}"', line_number, typo)
sys.exit(1) maybe_exit(1)
check_typo_against_dictionary(typo, line_number, correct_words) check_typo_against_dictionary(typo, line_number, correct_words)
@ -136,7 +136,7 @@ def parse_file_lines(file_name: str) -> Iterator[Tuple[int, str, str]]:
tokens = [token.strip() for token in line.split('->', 1)] tokens = [token.strip() for token in line.split('->', 1)]
if len(tokens) != 2 or not tokens[0]: if len(tokens) != 2 or not tokens[0]:
print(f'Error:{line_number}: Invalid syntax: "{line}"') print(f'Error:{line_number}: Invalid syntax: "{line}"')
sys.exit(1) maybe_exit(1)
typo, correction = tokens typo, correction = tokens
typo = typo.lower() # Force typos to lowercase. typo = typo.lower() # Force typos to lowercase.
@ -237,7 +237,7 @@ def encode_link(link: Dict[str, Any]) -> List[int]:
byte_offset = link['byte_offset'] byte_offset = link['byte_offset']
if not (0 <= byte_offset <= 0xffff): if not (0 <= byte_offset <= 0xffff):
cli.log.error('{fg_red}Error:{fg_reset} The autocorrection table is too large, a node link exceeds 64KB limit. Try reducing the autocorrection dict to fewer entries.') cli.log.error('{fg_red}Error:{fg_reset} The autocorrection table is too large, a node link exceeds 64KB limit. Try reducing the autocorrection dict to fewer entries.')
sys.exit(1) maybe_exit(1)
return [byte_offset & 255, byte_offset >> 8] return [byte_offset & 255, byte_offset >> 8]

View File

@ -12,6 +12,7 @@ from qmk.constants import QMK_FIRMWARE
from qmk.commands import find_make, get_make_parallel_args, build_environment from qmk.commands import find_make, get_make_parallel_args, build_environment
from qmk.search import search_keymap_targets, search_make_targets from qmk.search import search_keymap_targets, search_make_targets
from qmk.build_targets import BuildTarget, JsonKeymapBuildTarget from qmk.build_targets import BuildTarget, JsonKeymapBuildTarget
from qmk.util import maybe_exit_config
def mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool, no_temp: bool, parallel: int, **env): def mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool, no_temp: bool, parallel: int, **env):
@ -100,6 +101,8 @@ all: {keyboard_safe}_{keymap_name}_binary
def mass_compile(cli): def mass_compile(cli):
"""Compile QMK Firmware against all keyboards. """Compile QMK Firmware against all keyboards.
""" """
maybe_exit_config(should_exit=False, should_reraise=True)
if len(cli.args.builds) > 0: if len(cli.args.builds) > 0:
json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)]) json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)])
make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds)) make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds))

View File

@ -9,6 +9,7 @@ from qmk.userspace import UserspaceDefs
from qmk.build_targets import JsonKeymapBuildTarget from qmk.build_targets import JsonKeymapBuildTarget
from qmk.search import search_keymap_targets from qmk.search import search_keymap_targets
from qmk.cli.mass_compile import mass_compile_targets from qmk.cli.mass_compile import mass_compile_targets
from qmk.util import maybe_exit_config
@cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help="Remove temporary files during build.") @cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help="Remove temporary files during build.")
@ -22,6 +23,8 @@ def userspace_compile(cli):
cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')
return False return False
maybe_exit_config(should_exit=False, should_reraise=True)
userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')
build_targets = [] build_targets = []

View File

@ -10,6 +10,7 @@ from qmk.build_targets import BuildTarget
from qmk.keyboard import is_all_keyboards, keyboard_folder from qmk.keyboard import is_all_keyboards, keyboard_folder
from qmk.keymap import is_keymap_target from qmk.keymap import is_keymap_target
from qmk.search import search_keymap_targets from qmk.search import search_keymap_targets
from qmk.util import maybe_exit_config
@cli.argument('-e', '--expand', arg_only=True, action='store_true', help="Expands any use of `all` for either keyboard or keymap.") @cli.argument('-e', '--expand', arg_only=True, action='store_true', help="Expands any use of `all` for either keyboard or keymap.")
@ -19,6 +20,8 @@ def userspace_list(cli):
cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')
return False return False
maybe_exit_config(should_exit=False, should_reraise=True)
userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')
if cli.args.expand: if cli.args.expand:

View File

@ -69,7 +69,7 @@ def _via_to_keymap(via_backup, keyboard_data, keymap_layout):
layout_data = keyboard_data['layouts'].get(keymap_layout) layout_data = keyboard_data['layouts'].get(keymap_layout)
if not layout_data: if not layout_data:
cli.log.error(f'LAYOUT macro {keymap_layout} is not a valid one for keyboard {cli.args.keyboard}!') cli.log.error(f'LAYOUT macro {keymap_layout} is not a valid one for keyboard {cli.args.keyboard}!')
exit(1) return None
layout_data = layout_data['layout'] layout_data = layout_data['layout']
sorting_hat = list() sorting_hat = list()
@ -118,7 +118,7 @@ def via2json(cli):
keymap_layout = cli.args.layout if cli.args.layout else _find_via_layout_macro(cli.args.keyboard) keymap_layout = cli.args.layout if cli.args.layout else _find_via_layout_macro(cli.args.keyboard)
if not keymap_layout: if not keymap_layout:
cli.log.error(f"Couldn't find LAYOUT macro for keyboard {cli.args.keyboard}. Please specify it with the '-l' argument.") cli.log.error(f"Couldn't find LAYOUT macro for keyboard {cli.args.keyboard}. Please specify it with the '-l' argument.")
exit(1) return False
# Load the VIA backup json # Load the VIA backup json
with cli.args.filename.open('r') as fd: with cli.args.filename.open('r') as fd:
@ -126,9 +126,15 @@ def via2json(cli):
# Generate keyboard metadata # Generate keyboard metadata
keyboard_data = info_json(cli.args.keyboard) keyboard_data = info_json(cli.args.keyboard)
if not keyboard_data:
cli.log.error(f'LAYOUT macro {keymap_layout} is not a valid one for keyboard {cli.args.keyboard}!')
return False
# Get keycode array # Get keycode array
keymap_data = _via_to_keymap(via_backup, keyboard_data, keymap_layout) keymap_data = _via_to_keymap(via_backup, keyboard_data, keymap_layout)
if not keymap_data:
cli.log.error(f'Could not extract valid keycode data from VIA backup matching keyboard {cli.args.keyboard}!')
return False
# Convert macros # Convert macros
macro_data = list() macro_data = list()

View File

@ -11,6 +11,7 @@ import jsonschema
from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE
from qmk.json_schema import json_load, validate from qmk.json_schema import json_load, validate
from qmk.keyboard import keyboard_alias_definitions from qmk.keyboard import keyboard_alias_definitions
from qmk.util import maybe_exit
def find_make(): def find_make():
@ -52,7 +53,7 @@ def parse_configurator_json(configurator_file):
except jsonschema.ValidationError as e: except jsonschema.ValidationError as e:
cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}') cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}')
exit(1) maybe_exit(1)
keyboard = user_keymap['keyboard'] keyboard = user_keymap['keyboard']
aliases = keyboard_alias_definitions() aliases = keyboard_alias_definitions()

View File

@ -14,6 +14,7 @@ from qmk.keyboard import config_h, rules_mk
from qmk.commands import parse_configurator_json from qmk.commands import parse_configurator_json
from qmk.makefile import parse_rules_mk_file from qmk.makefile import parse_rules_mk_file
from qmk.math import compute from qmk.math import compute
from qmk.util import maybe_exit
true_values = ['1', 'on', 'yes'] true_values = ['1', 'on', 'yes']
false_values = ['0', 'off', 'no'] false_values = ['0', 'off', 'no']
@ -208,7 +209,7 @@ def _validate(keyboard, info_data):
except jsonschema.ValidationError as e: except jsonschema.ValidationError as e:
json_path = '.'.join([str(p) for p in e.absolute_path]) json_path = '.'.join([str(p) for p in e.absolute_path])
cli.log.error('Invalid API data: %s: %s: %s', keyboard, json_path, e.message) cli.log.error('Invalid API data: %s: %s: %s', keyboard, json_path, e.message)
exit(1) maybe_exit(1)
def info_json(keyboard): def info_json(keyboard):

View File

@ -11,6 +11,8 @@ from copy import deepcopy
from milc import cli from milc import cli
from qmk.util import maybe_exit
def _dict_raise_on_duplicates(ordered_pairs): def _dict_raise_on_duplicates(ordered_pairs):
"""Reject duplicate keys.""" """Reject duplicate keys."""
@ -38,10 +40,10 @@ def _json_load_impl(json_file, strict=True):
except (json.decoder.JSONDecodeError, hjson.HjsonDecodeError) as e: 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) cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
exit(1) maybe_exit(1)
except Exception as e: except Exception as e:
cli.log.error('Unknown error attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e) cli.log.error('Unknown error attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
exit(1) maybe_exit(1)
def json_load(json_file, strict=True): def json_load(json_file, strict=True):

View File

@ -2,9 +2,30 @@
""" """
import contextlib import contextlib
import multiprocessing import multiprocessing
import sys
from milc import cli from milc import cli
maybe_exit_should_exit = True
maybe_exit_reraise = False
# Controls whether or not early `exit()` calls should be made
def maybe_exit(rc):
if maybe_exit_should_exit:
sys.exit(rc)
if maybe_exit_reraise:
e = sys.exception()
if e:
raise e
def maybe_exit_config(should_exit: bool = True, should_reraise: bool = False):
global maybe_exit_should_exit
global maybe_exit_reraise
maybe_exit_should_exit = should_exit
maybe_exit_reraise = should_reraise
@contextlib.contextmanager @contextlib.contextmanager
def parallelize(): def parallelize():