diff options
Diffstat (limited to 'lib/python/qmk')
| -rw-r--r-- | lib/python/qmk/cli/__init__.py | 13 | ||||
| -rw-r--r-- | lib/python/qmk/cli/cformat.py | 6 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/compile.py | 10 | ||||
| -rw-r--r-- | lib/python/qmk/cli/config.py | 96 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/doctor.py | 5 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/hello.py | 6 | ||||
| -rw-r--r-- | lib/python/qmk/cli/json/__init__.py | 5 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/json/keymap.py | 20 | ||||
| -rw-r--r-- | lib/python/qmk/cli/new/__init__.py | 1 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 17 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/pyformat.py | 5 | ||||
| -rw-r--r-- | lib/python/qmk/cli/pytest.py (renamed from lib/python/qmk/cli/nose2.py) | 8 | ||||
| -rw-r--r-- | lib/python/qmk/path.py | 1 | ||||
| -rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 39 | 
14 files changed, 195 insertions, 37 deletions
| diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index e69de29bb2..fb4e0ecb46 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -0,0 +1,13 @@ +"""QMK CLI Subcommands + +We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. +""" +from . import cformat +from . import compile +from . import config +from . import doctor +from . import hello +from . import json +from . import new +from . import pyformat +from . import pytest diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py index 91e650368b..d2382bdbde 100644 --- a/lib/python/qmk/cli/cformat.py +++ b/lib/python/qmk/cli/cformat.py @@ -6,9 +6,9 @@ import subprocess  from milc import cli [email protected]('files', nargs='*', help='Filename(s) to format.') [email protected]("Format C code according to QMK's style.") -def main(cli): [email protected]('files', nargs='*', arg_only=True, help='Filename(s) to format.') [email protected]("Format C code according to QMK's style.") +def cformat(cli):      """Format C code according to QMK's style.      """      clang_format = ['clang-format', '-i'] diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py index 7e14ad8fbf..6646891b30 100755 --- a/lib/python/qmk/cli/compile.py +++ b/lib/python/qmk/cli/compile.py @@ -14,11 +14,11 @@ import qmk.keymap  import qmk.path [email protected]('filename', nargs='?', type=FileType('r'), help='The configurator export to compile') [email protected]('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile')  @cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')  @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') [email protected]('Compile a QMK Firmware.') -def main(cli): [email protected]('Compile a QMK Firmware.') +def compile(cli):      """Compile a QMK Firmware.      If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists. @@ -41,9 +41,9 @@ def main(cli):          # Compile the keymap          command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))] -    elif cli.config.general.keyboard and cli.config.general.keymap: +    elif cli.config.compile.keyboard and cli.config.compile.keymap:          # Generate the make command for a specific keyboard/keymap. -        command = ['make', ':'.join((cli.config.general.keyboard, cli.config.general.keymap))] +        command = ['make', ':'.join((cli.config.compile.keyboard, cli.config.compile.keymap))]      else:          cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.') diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py new file mode 100644 index 0000000000..d6c774e651 --- /dev/null +++ b/lib/python/qmk/cli/config.py @@ -0,0 +1,96 @@ +"""Read and write configuration settings +""" +import os +import subprocess + +from milc import cli + + +def print_config(section, key): +    """Print a single config setting to stdout. +    """ +    cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key]) + + [email protected]('-ro', '--read-only', action='store_true', help='Operate in read-only mode.') [email protected]('configs', nargs='*', arg_only=True, help='Configuration options to read or write.') [email protected]("Read and write configuration settings.") +def config(cli): +    """Read and write config settings. + +    This script iterates over the config_tokens supplied as argument. Each config_token has the following form: + +        section[.key][=value] + +    If only a section (EG 'compile') is supplied all keys for that section will be displayed. + +    If section.key is supplied the value for that single key will be displayed. + +    If section.key=value is supplied the value for that single key will be set. + +    If section.key=None is supplied the key will be deleted. + +    No validation is done to ensure that the supplied section.key is actually used by qmk scripts. +    """ +    if not cli.args.configs: +        # Walk the config tree +        for section in cli.config: +            for key in cli.config[section]: +                print_config(section, key) + +        return True + +    # Process config_tokens +    save_config = False + +    for argument in cli.args.configs: +        # Split on space in case they quoted multiple config tokens +        for config_token in argument.split(' '): +            # Extract the section, config_key, and value to write from the supplied config_token. +            if '=' in config_token: +                key, value = config_token.split('=') +            else: +                key = config_token +                value = None + +            if '.' in key: +                section, config_key = key.split('.', 1) +            else: +                section = key +                config_key = None + +            # Validation +            if config_key and '.' in config_key: +                cli.log.error('Config keys may not have more than one period! "%s" is not valid.', key) +                return False + +            # Do what the user wants +            if section and config_key and value: +                # Write a config key +                log_string = '%s.%s{fg_cyan}:{fg_reset} %s {fg_cyan}->{fg_reset} %s' +                if cli.args.read_only: +                    log_string += ' {fg_red}(change not written)' + +                cli.echo(log_string, section, config_key, cli.config[section][config_key], value) + +                if not cli.args.read_only: +                    if value == 'None': +                        del cli.config[section][config_key] +                    else: +                        cli.config[section][config_key] = value +                    save_config = True + +            elif section and config_key: +                # Display a single key +                print_config(section, config_key) + +            elif section: +                # Display an entire section +                for key in cli.config[section]: +                    print_config(section, key) + +    # Ending actions +    if save_config: +        cli.save_config() + +    return True diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py index 5a713b20f5..3474422a89 100755 --- a/lib/python/qmk/cli/doctor.py +++ b/lib/python/qmk/cli/doctor.py @@ -11,8 +11,8 @@ from glob import glob  from milc import cli [email protected]('Basic QMK environment checks') -def main(cli): [email protected]('Basic QMK environment checks') +def doctor(cli):      """Basic QMK environment checks.      This is currently very simple, it just checks that all the expected binaries are on your system. @@ -36,6 +36,7 @@ def main(cli):          else:              try:                  subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True) +                cli.log.info('Found {fg_cyan}%s', binary)              except subprocess.CalledProcessError:                  cli.log.error("{fg_red}Can't run `%s --version`", binary)                  ok = False diff --git a/lib/python/qmk/cli/hello.py b/lib/python/qmk/cli/hello.py index bc0cb6de18..bee28c3013 100755 --- a/lib/python/qmk/cli/hello.py +++ b/lib/python/qmk/cli/hello.py @@ -6,8 +6,8 @@ from milc import cli  @cli.argument('-n', '--name', default='World', help='Name to greet.') [email protected]('QMK Hello World.') -def main(cli): [email protected]('QMK Hello World.') +def hello(cli):      """Log a friendly greeting.      """ -    cli.log.info('Hello, %s!', cli.config.general.name) +    cli.log.info('Hello, %s!', cli.config.hello.name) diff --git a/lib/python/qmk/cli/json/__init__.py b/lib/python/qmk/cli/json/__init__.py index e69de29bb2..f4ebfc45b4 100644 --- a/lib/python/qmk/cli/json/__init__.py +++ b/lib/python/qmk/cli/json/__init__.py @@ -0,0 +1,5 @@ +"""QMK CLI JSON Subcommands + +We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. +""" +from . import keymap diff --git a/lib/python/qmk/cli/json/keymap.py b/lib/python/qmk/cli/json/keymap.py index e2d0b58093..a65acd6197 100755 --- a/lib/python/qmk/cli/json/keymap.py +++ b/lib/python/qmk/cli/json/keymap.py @@ -9,10 +9,10 @@ from milc import cli  import qmk.keymap [email protected]('-o', '--output', help='File to write to') [email protected]('filename', help='Configurator JSON file') [email protected]('Create a keymap.c from a QMK Configurator export.') -def main(cli): [email protected]('-o', '--output', arg_only=True, help='File to write to') [email protected]('filename', arg_only=True, help='Configurator JSON file') [email protected]('Create a keymap.c from a QMK Configurator export.') +def json_keymap(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. @@ -28,8 +28,8 @@ def main(cli):          exit(1)      # Environment processing -    if cli.config.general.output == ('-'): -        cli.config.general.output = None +    if cli.args.output == ('-'): +        cli.args.output = None      # Parse the configurator json      with open(qmk.path.normpath(cli.args.filename), 'r') as fd: @@ -38,17 +38,17 @@ def main(cli):      # Generate the keymap      keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) -    if cli.config.general.output: -        output_dir = os.path.dirname(cli.config.general.output) +    if cli.args.output: +        output_dir = os.path.dirname(cli.args.output)          if not os.path.exists(output_dir):              os.makedirs(output_dir) -        output_file = qmk.path.normpath(cli.config.general.output) +        output_file = qmk.path.normpath(cli.args.output)          with open(output_file, 'w') as keymap_fd:              keymap_fd.write(keymap_c) -        cli.log.info('Wrote keymap to %s.', cli.config.general.output) +        cli.log.info('Wrote keymap to %s.', cli.args.output)      else:          print(keymap_c) diff --git a/lib/python/qmk/cli/new/__init__.py b/lib/python/qmk/cli/new/__init__.py index e69de29bb2..c6a26939b8 100644 --- a/lib/python/qmk/cli/new/__init__.py +++ b/lib/python/qmk/cli/new/__init__.py @@ -0,0 +1 @@ +from . import keymap diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py index b378e5ab43..5efb81c93f 100755 --- a/lib/python/qmk/cli/new/keymap.py +++ b/lib/python/qmk/cli/new/keymap.py @@ -6,15 +6,15 @@ import shutil  from milc import cli [email protected]('-k', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') [email protected]('-u', '--username', help='Specify any name for the new keymap directory') [email protected]('Creates a new keymap for the keyboard of your choosing') -def main(cli): [email protected]('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') [email protected]('-km', '--keymap', help='Specify the name for the new keymap directory') [email protected]('Creates a new keymap for the keyboard of your choosing') +def new_keymap(cli):      """Creates a new keymap for the keyboard of your choosing.      """      # ask for user input if keyboard or username was not provided in the command line -    keyboard = cli.config.general.keyboard if cli.config.general.keyboard else input("Keyboard Name: ") -    username = cli.config.general.username if cli.config.general.username else input("Username: ") +    keyboard = cli.config.new_keymap.keyboard if cli.config.new_keymap.keyboard else input("Keyboard Name: ") +    keymap = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else input("Keymap Name: ")      # generate keymap paths      kb_path = os.path.join(os.getcwd(), "keyboards", keyboard) @@ -36,6 +36,5 @@ def main(cli):      shutil.copytree(keymap_path_default, keymap_path, symlinks=True)      # end message to user -    cli.log.info("%s keymap directory created in: %s\n" + -                 "Compile a firmware file with your new keymap by typing: \n" + -                 "qmk compile -kb %s -km %s", username, keymap_path, keyboard, username) +    cli.log.info("%s keymap directory created in: %s", username, keymap_path) +    cli.log.info("Compile a firmware with your new keymap by typing: \n" + "qmk compile -kb %s -km %s", keyboard, username) diff --git a/lib/python/qmk/cli/pyformat.py b/lib/python/qmk/cli/pyformat.py index b1f8c02b28..a53ba40c0a 100755 --- a/lib/python/qmk/cli/pyformat.py +++ b/lib/python/qmk/cli/pyformat.py @@ -5,12 +5,13 @@ from milc import cli  import subprocess [email protected]("Format python code according to QMK's style.") -def main(cli): [email protected]("Format python code according to QMK's style.") +def pyformat(cli):      """Format python code according to QMK's style.      """      try:          subprocess.run(['yapf', '-vv', '-ri', 'bin/qmk', 'lib/python'], check=True)          cli.log.info('Successfully formatted the python code in `bin/qmk` and `lib/python`.') +      except subprocess.CalledProcessError:          cli.log.error('Error formatting python code!') diff --git a/lib/python/qmk/cli/nose2.py b/lib/python/qmk/cli/pytest.py index c6c9c67b30..14613e1d96 100644 --- a/lib/python/qmk/cli/nose2.py +++ b/lib/python/qmk/cli/pytest.py @@ -2,17 +2,19 @@  QMK script to run unit and integration tests against our python code.  """ +import sys  from milc import cli [email protected]('QMK Python Unit Tests') -def main(cli): [email protected]('QMK Python Unit Tests') +def pytest(cli):      """Use nose2 to run unittests      """      try:          import nose2 +      except ImportError:          cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2')          return False -    nose2.discover() +    nose2.discover(argv=['nose2', '-v']) diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py index cf087265fb..2149625cc6 100644 --- a/lib/python/qmk/path.py +++ b/lib/python/qmk/path.py @@ -2,6 +2,7 @@  """  import logging  import os +from pkgutil import walk_packages  from qmk.errors import NoSuchKeyboardError diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py new file mode 100644 index 0000000000..2fc6e0f723 --- /dev/null +++ b/lib/python/qmk/tests/test_cli_commands.py @@ -0,0 +1,39 @@ +import subprocess + + +def check_subcommand(command, *args): +    cmd = ['bin/qmk', command] + list(args) +    return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + + +def test_cformat(): +    assert check_subcommand('cformat', 'tmk_core/common/backlight.c').returncode == 0 + + +def test_compile(): +    assert check_subcommand('compile', '-kb', 'handwired/onekey/pytest', '-km', 'default').returncode == 0 + + +def test_config(): +    result = check_subcommand('config') +    assert result.returncode == 0 +    assert 'general.color' in result.stdout + + +def test_doctor(): +    result = check_subcommand('doctor') +    assert result.returncode == 0 +    assert 'QMK Doctor is checking your environment.' in result.stderr +    assert 'QMK is ready to go' in result.stderr + + +def test_hello(): +    result = check_subcommand('hello') +    assert result.returncode == 0 +    assert 'Hello,' in result.stderr + + +def test_pyformat(): +    result = check_subcommand('pyformat') +    assert result.returncode == 0 +    assert 'Successfully formatted the python code' in result.stderr | 
