diff options
Diffstat (limited to 'lib/python')
-rw-r--r-- | lib/python/milc.py | 88 | ||||
-rw-r--r-- | lib/python/qmk/cli/__init__.py | 7 | ||||
-rw-r--r-- | lib/python/qmk/cli/cformat.py | 7 | ||||
-rw-r--r-- | lib/python/qmk/cli/docs.py | 2 | ||||
-rwxr-xr-x | lib/python/qmk/cli/doctor.py | 13 | ||||
-rwxr-xr-x | lib/python/qmk/cli/hello.py | 2 | ||||
-rwxr-xr-x | lib/python/qmk/cli/json2c.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/kle2json.py | 10 | ||||
-rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 2 | ||||
-rwxr-xr-x | lib/python/qmk/cli/pyformat.py | 2 | ||||
-rw-r--r-- | lib/python/qmk/cli/pytest.py | 2 | ||||
-rw-r--r-- | lib/python/qmk/commands.py | 4 | ||||
-rw-r--r-- | lib/python/qmk/keymap.py | 19 |
13 files changed, 97 insertions, 69 deletions
diff --git a/lib/python/milc.py b/lib/python/milc.py index 83edfc7f51..eb18984eb3 100644 --- a/lib/python/milc.py +++ b/lib/python/milc.py @@ -242,15 +242,24 @@ class SubparserWrapper(object): This also stores the default for the argument in `self.cli.default_arguments`. """ - if 'action' in kwargs and kwargs['action'] == 'store_boolean': + if kwargs.get('action') == 'store_boolean': # Store boolean will call us again with the enable/disable flag arguments return handle_store_boolean(self, *args, **kwargs) self.cli.acquire_lock() + argument_name = self.cli.get_argument_name(*args, **kwargs) + self.subparser.add_argument(*args, **kwargs) + + if kwargs.get('action') == 'store_false': + self.cli._config_store_false.append(argument_name) + + if kwargs.get('action') == 'store_true': + self.cli._config_store_true.append(argument_name) + if self.submodule not in self.cli.default_arguments: self.cli.default_arguments[self.submodule] = {} - self.cli.default_arguments[self.submodule][self.cli.get_argument_name(*args, **kwargs)] = kwargs.get('default') + self.cli.default_arguments[self.submodule][argument_name] = kwargs.get('default') self.cli.release_lock() @@ -268,11 +277,13 @@ class MILC(object): # Define some basic info self.acquire_lock() + self._config_store_true = [] + self._config_store_false = [] self._description = None self._entrypoint = None self._inside_context_manager = False self.ansi = ansi_colors - self.arg_only = [] + self.arg_only = {} self.config = self.config_source = None self.config_file = None self.default_arguments = {} @@ -377,7 +388,7 @@ class MILC(object): self.add_argument('--log-file', help='File to write log messages to') self.add_argument('--color', action='store_boolean', default=True, help='color in output') self.add_argument('--config-file', help='The location for the configuration file') - self.arg_only.append('config_file') + self.arg_only['config_file'] = ['general'] def add_subparsers(self, title='Sub-commands', **kwargs): if self._inside_context_manager: @@ -427,17 +438,20 @@ class MILC(object): raise RuntimeError('You must run this before the with statement!') def argument_function(handler): - if 'arg_only' in kwargs and kwargs['arg_only']: + subcommand_name = handler.__name__.replace("_", "-") + + if kwargs.get('arg_only'): arg_name = self.get_argument_name(*args, **kwargs) - self.arg_only.append(arg_name) + if arg_name not in self.arg_only: + self.arg_only[arg_name] = [] + self.arg_only[arg_name].append(subcommand_name) del kwargs['arg_only'] - name = handler.__name__.replace("_", "-") if handler is self._entrypoint: self.add_argument(*args, **kwargs) - elif name in self.subcommands: - self.subcommands[name].add_argument(*args, **kwargs) + elif subcommand_name in self.subcommands: + self.subcommands[subcommand_name].add_argument(*args, **kwargs) else: raise RuntimeError('Decorated function is not entrypoint or subcommand!') @@ -511,35 +525,37 @@ class MILC(object): if argument in ('subparsers', 'entrypoint'): continue - if argument not in self.arg_only: - # Find the argument's section - # Underscores in command's names are converted to dashes during initialization. - # TODO(Erovia) Find a better solution - entrypoint_name = self._entrypoint.__name__.replace("_", "-") - if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]: - argument_found = True - section = self._entrypoint.__name__ - if argument in self.default_arguments['general']: - argument_found = True - section = 'general' - - if not argument_found: - raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!') - exit(1) + # Find the argument's section + # Underscores in command's names are converted to dashes during initialization. + # TODO(Erovia) Find a better solution + entrypoint_name = self._entrypoint.__name__.replace("_", "-") + if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]: + argument_found = True + section = self._entrypoint.__name__ + if argument in self.default_arguments['general']: + argument_found = True + section = 'general' + + if not argument_found: + raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!') + exit(1) + + if argument not in self.arg_only or section not in self.arg_only[argument]: + # Determine the arg value and source + arg_value = getattr(self.args, argument) + if argument in self._config_store_true and arg_value: + passed_on_cmdline = True + elif argument in self._config_store_false and not arg_value: + passed_on_cmdline = True + elif arg_value is not None: + passed_on_cmdline = True + else: + passed_on_cmdline = False # Merge this argument into self.config - if argument in self.default_arguments['general'] or argument in self.default_arguments[entrypoint_name]: - arg_value = getattr(self.args, argument) - if arg_value is not None: - self.config[section][argument] = arg_value - self.config_source[section][argument] = 'argument' - else: - if argument not in self.config[entrypoint_name]: - # Check if the argument exist for this section - arg = getattr(self.args, argument) - if arg is not None: - self.config[section][argument] = arg - self.config_source[section][argument] = 'argument' + if passed_on_cmdline and (argument in self.default_arguments['general'] or argument in self.default_arguments[entrypoint_name] or argument not in self.config[entrypoint_name]): + self.config[section][argument] = arg_value + self.config_source[section][argument] = 'argument' self.release_lock() diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index eb524217cd..394a1353bc 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -2,6 +2,8 @@ We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. """ +import sys + from milc import cli from . import cformat @@ -19,5 +21,6 @@ from . import new from . import pyformat from . import pytest -if not hasattr(cli, 'config_source'): - cli.log.warning("Your QMK CLI is out of date. Please upgrade with `pip3 install --upgrade qmk` or by using your package manager.") +if sys.version_info[0] != 3 or sys.version_info[1] < 6: + cli.log.error('Your Python is too old! Please upgrade to Python 3.6 or later.') + exit(127) diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py index 7e3a91dcf0..0cd8b6192a 100644 --- a/lib/python/qmk/cli/cformat.py +++ b/lib/python/qmk/cli/cformat.py @@ -22,9 +22,8 @@ def cformat_run(files, all_files): cli.log.warn('No changes detected. Use "qmk cformat -a" to format all files') return False if files and all_files: - cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(cli.args.files)) - # 3.6+: Can remove the str casting, python will cast implicitly - subprocess.run(clang_format + [str(file) for file in files], check=True) + cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(files)) + subprocess.run(clang_format + [file for file in files], check=True) cli.log.info('Successfully formatted the C code.') except subprocess.CalledProcessError: @@ -35,7 +34,7 @@ def cformat_run(files, all_files): @cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all core files.') @cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.') @cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.') -@cli.subcommand("Format C code according to QMK's style.") +@cli.subcommand("Format C code according to QMK's style.", hidden=False if cli.config.user.developer else True) def cformat(cli): """Format C code according to QMK's style. """ diff --git a/lib/python/qmk/cli/docs.py b/lib/python/qmk/cli/docs.py index 163c8b8015..5816106504 100644 --- a/lib/python/qmk/cli/docs.py +++ b/lib/python/qmk/cli/docs.py @@ -7,7 +7,7 @@ from milc import cli @cli.argument('-p', '--port', default=8936, type=int, help='Port number to use.') -@cli.subcommand('Run a local webserver for QMK documentation.') +@cli.subcommand('Run a local webserver for QMK documentation.', hidden=False if cli.config.user.developer else True) def docs(cli): """Spin up a local HTTPServer instance for the QMK docs. """ diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py index 3e6f6fe54e..3c46248372 100755 --- a/lib/python/qmk/cli/doctor.py +++ b/lib/python/qmk/cli/doctor.py @@ -135,16 +135,15 @@ def check_udev_rules(): } if udev_dir.exists(): - udev_rules = [str(rule_file) for rule_file in udev_dir.glob('*.rules')] + udev_rules = [rule_file for rule_file in udev_dir.glob('*.rules')] current_rules = set() # Collect all rules from the config files for rule_file in udev_rules: - with open(rule_file, "r") as fd: - for line in fd.readlines(): - line = line.strip() - if not line.startswith("#") and len(line): - current_rules.add(line) + for line in rule_file.read_text().split('\n'): + line = line.strip() + if not line.startswith("#") and len(line): + current_rules.add(line) # Check if the desired rules are among the currently present rules for bootloader, rules in desired_rules.items(): @@ -191,7 +190,7 @@ def is_executable(command): cli.log.debug('Found {fg_cyan}%s', command) return True - cli.log.error("{fg_red}Can't run `%s %s`", (command, version_arg)) + cli.log.error("{fg_red}Can't run `%s %s`", command, version_arg) return False diff --git a/lib/python/qmk/cli/hello.py b/lib/python/qmk/cli/hello.py index bee28c3013..5119188a07 100755 --- a/lib/python/qmk/cli/hello.py +++ b/lib/python/qmk/cli/hello.py @@ -6,7 +6,7 @@ from milc import cli @cli.argument('-n', '--name', default='World', help='Name to greet.') -@cli.subcommand('QMK Hello World.') +@cli.subcommand('QMK Hello World.', hidden=False if cli.config.user.developer else True) def hello(cli): """Log a friendly greeting. """ diff --git a/lib/python/qmk/cli/json2c.py b/lib/python/qmk/cli/json2c.py index 46c4d04bb7..5218405070 100755 --- a/lib/python/qmk/cli/json2c.py +++ b/lib/python/qmk/cli/json2c.py @@ -10,29 +10,27 @@ import qmk.path @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to') @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") -@cli.argument('filename', arg_only=True, help='Configurator JSON file') +@cli.argument('filename', type=qmk.path.normpath, arg_only=True, help='Configurator JSON file') @cli.subcommand('Creates a keymap.c from a QMK Configurator export.') def json2c(cli): """Generate a keymap.c from a configurator export. 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. """ - cli.args.filename = qmk.path.normpath(cli.args.filename) - # Error checking if not cli.args.filename.exists(): cli.log.error('JSON file does not exist!') cli.print_usage() exit(1) - if str(cli.args.filename) == '-': + if cli.args.filename.name == '-': # TODO(skullydazed/anyone): Read file contents from STDIN cli.log.error('Reading from STDIN is not (yet) supported.') cli.print_usage() exit(1) # Environment processing - if cli.args.output == ('-'): + if cli.args.output.name == ('-'): cli.args.output = None # Parse the configurator json diff --git a/lib/python/qmk/cli/kle2json.py b/lib/python/qmk/cli/kle2json.py index 00f63d3622..5268462f92 100755 --- a/lib/python/qmk/cli/kle2json.py +++ b/lib/python/qmk/cli/kle2json.py @@ -26,7 +26,7 @@ class CustomJSONEncoder(json.JSONEncoder): @cli.argument('filename', help='The KLE raw txt to convert') @cli.argument('-f', '--force', action='store_true', help='Flag to overwrite current info.json') -@cli.subcommand('Convert a KLE layout to a Configurator JSON') +@cli.subcommand('Convert a KLE layout to a Configurator JSON', hidden=False if cli.config.user.developer else True) def kle2json(cli): """Convert a KLE layout to QMK's layout format. """ # If filename is a path @@ -37,12 +37,12 @@ def kle2json(cli): file_path = Path(os.environ['ORIG_CWD'], cli.args.filename) # Check for valid file_path for more graceful failure if not file_path.exists(): - return cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', str(file_path)) + return cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', file_path) out_path = file_path.parent raw_code = file_path.open().read() # Check if info.json exists, allow overwrite with force if Path(out_path, "info.json").exists() and not cli.args.force: - cli.log.error('File {fg_cyan}%s/info.json{style_reset_all} already exists, use -f or --force to overwrite.', str(out_path)) + cli.log.error('File {fg_cyan}%s/info.json{style_reset_all} already exists, use -f or --force to overwrite.', out_path) return False try: # Convert KLE raw to x/y coordinates (using kle2xy package from skullydazed) @@ -69,7 +69,7 @@ def kle2json(cli): # Replace layout in keyboard json keyboard = keyboard.replace('"LAYOUT_JSON_HERE"', layout) # Write our info.json - file = open(str(out_path) + "/info.json", "w") + file = open(out_path + "/info.json", "w") file.write(keyboard) file.close() - cli.log.info('Wrote out {fg_cyan}%s/info.json', str(out_path)) + cli.log.info('Wrote out {fg_cyan}%s/info.json', out_path) diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py index 5ae2628565..474fe7974f 100755 --- a/lib/python/qmk/cli/new/keymap.py +++ b/lib/python/qmk/cli/new/keymap.py @@ -40,7 +40,7 @@ def new_keymap(cli): exit(1) # create user directory with default keymap files - shutil.copytree(str(keymap_path_default), str(keymap_path_new), symlinks=True) + shutil.copytree(keymap_path_default, keymap_path_new, symlinks=True) # end message to user cli.log.info("%s keymap directory created in: %s", keymap, keymap_path_new) diff --git a/lib/python/qmk/cli/pyformat.py b/lib/python/qmk/cli/pyformat.py index a53ba40c0a..1464443804 100755 --- a/lib/python/qmk/cli/pyformat.py +++ b/lib/python/qmk/cli/pyformat.py @@ -5,7 +5,7 @@ from milc import cli import subprocess -@cli.subcommand("Format python code according to QMK's style.") +@cli.subcommand("Format python code according to QMK's style.", hidden=False if cli.config.user.developer else True) def pyformat(cli): """Format python code according to QMK's style. """ diff --git a/lib/python/qmk/cli/pytest.py b/lib/python/qmk/cli/pytest.py index 09611d750f..5417a9cb34 100644 --- a/lib/python/qmk/cli/pytest.py +++ b/lib/python/qmk/cli/pytest.py @@ -7,7 +7,7 @@ import subprocess from milc import cli -@cli.subcommand('QMK Python Unit Tests') +@cli.subcommand('QMK Python Unit Tests', hidden=False if cli.config.user.developer else True) def pytest(cli): """Run several linting/testing commands. """ diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 3424cdf085..5d2a03c9a8 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -5,6 +5,7 @@ import os import platform import subprocess import shlex +import shutil import qmk.keymap @@ -28,11 +29,12 @@ def create_make_command(keyboard, keymap, target=None): A command that can be run to make the specified keyboard and keymap """ make_args = [keyboard, keymap] + make_cmd = 'gmake' if shutil.which('gmake') else 'make' if target: make_args.append(target) - return ['make', ':'.join(make_args)] + return [make_cmd, ':'.join(make_args)] def compile_configurator_json(user_keymap, bootloader=None): diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index b91ba89bed..69cdc8d5b5 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py @@ -1,6 +1,5 @@ """Functions that help you work with QMK keymaps. """ -import os from pathlib import Path import qmk.path @@ -11,7 +10,7 @@ DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H /* THIS FILE WAS GENERATED! * - * This file was generated by qmk-compile-json. You may or may not want to + * This file was generated by qmk json2c. You may or may not want to * edit it directly. */ @@ -39,6 +38,15 @@ def template(keyboard): return DEFAULT_KEYMAP_C +def _strip_any(keycode): + """Remove ANY() from a keycode. + """ + if keycode.startswith('ANY(') and keycode.endswith(')'): + keycode = keycode[4:-1] + + return keycode + + def generate(keyboard, layout, layers): """Returns a keymap.c for the specified keyboard, layout, and layers. @@ -53,9 +61,12 @@ def generate(keyboard, layout, layers): An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. """ layer_txt = [] + for layer_num, layer in enumerate(layers): if layer_num != 0: layer_txt[-1] = layer_txt[-1] + ',' + + layer = map(_strip_any, layer) layer_keys = ', '.join(layer) layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys)) @@ -115,7 +126,7 @@ def list_keymaps(keyboard_name): while kb_path != keyboards_dir: keymaps_dir = kb_path / "keymaps" if keymaps_dir.exists(): - names = names.union([keymap for keymap in os.listdir(str(keymaps_dir)) if (keymaps_dir / keymap / "keymap.c").is_file()]) + names = names.union([keymap for keymap in keymaps_dir.iterdir() if (keymaps_dir / keymap / "keymap.c").is_file()]) kb_path = kb_path.parent # if community layouts are supported, get them @@ -123,6 +134,6 @@ def list_keymaps(keyboard_name): for layout in rules_mk["LAYOUTS"].split(): cl_path = Path.cwd() / "layouts" / "community" / layout if cl_path.exists(): - names = names.union([keymap for keymap in os.listdir(str(cl_path)) if (cl_path / keymap / "keymap.c").is_file()]) + names = names.union([keymap for keymap in cl_path.iterdir() if (cl_path / keymap / "keymap.c").is_file()]) return sorted(names) |