From f04e58dad6f56cdbd5d369c9e00405dcdb47c8ea Mon Sep 17 00:00:00 2001 From: Dan McClain Date: Mon, 7 Oct 2019 14:32:30 -0400 Subject: [CLI] Add `qmk list_keyboards` (#6927) `list_keyboards` replicates the `make list-keyboards` by globbing for all paths that include `rules.mk` and then removing the paths that include `keymaps`. This basis of this cli command could be reused in the future as a util, but is not done so here since this would be the only place that would use it currently Resolves #6911 --- lib/python/qmk/cli/list/__init__.py | 1 + lib/python/qmk/cli/list/keyboards.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 lib/python/qmk/cli/list/__init__.py create mode 100644 lib/python/qmk/cli/list/keyboards.py (limited to 'lib/python/qmk/cli/list') diff --git a/lib/python/qmk/cli/list/__init__.py b/lib/python/qmk/cli/list/__init__.py new file mode 100644 index 000000000..c36ba6954 --- /dev/null +++ b/lib/python/qmk/cli/list/__init__.py @@ -0,0 +1 @@ +from . import keyboards diff --git a/lib/python/qmk/cli/list/keyboards.py b/lib/python/qmk/cli/list/keyboards.py new file mode 100644 index 000000000..53a7af75c --- /dev/null +++ b/lib/python/qmk/cli/list/keyboards.py @@ -0,0 +1,26 @@ +"""List the keyboards currently defined within QMK +""" +import os +import re +import glob + +from milc import cli + +@cli.subcommand("List the keyboards currently defined within QMK") +def list_keyboards(cli): + """List the keyboards currently defined within QMK + """ + + base_path = os.path.join(os.getcwd(), "keyboards") + os.path.sep + kb_path_wildcard = os.path.join(base_path, "**", "rules.mk") + + # find everywhere we have rules.mk where keymaps isn't in the path + paths = [path for path in glob.iglob(kb_path_wildcard, recursive=True) if 'keymaps' not in path] + + # strip the keyboard directory path prefix and rules.mk suffix and alphabetize + find_name = lambda path: path.replace(base_path, "").replace(os.path.sep + "rules.mk", "") + names = sorted(map(find_name, paths)) + + for name in names: + # We echo instead of cli.log.info to allow easier piping of this output + cli.echo(name) -- cgit v1.2.3-70-g09d2 From 79edb7c5942108774e667c172550a1519c5543ac Mon Sep 17 00:00:00 2001 From: skullY Date: Tue, 12 Nov 2019 17:27:08 -0800 Subject: Small CLI cleanups * yapf changes * Fix the cformat test * Make the normpath test work when run from / * `qmk config`: Mark `--read-only` as arg_only --- lib/python/qmk/cli/config.py | 2 +- lib/python/qmk/cli/list/keyboards.py | 1 + lib/python/qmk/errors.py | 1 - lib/python/qmk/tests/attrdict.py | 1 - lib/python/qmk/tests/test_cli_commands.py | 2 +- lib/python/qmk/tests/test_qmk_path.py | 2 +- 6 files changed, 4 insertions(+), 5 deletions(-) (limited to 'lib/python/qmk/cli/list') diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py index d6c774e65..c4ee20cba 100644 --- a/lib/python/qmk/cli/config.py +++ b/lib/python/qmk/cli/config.py @@ -12,7 +12,7 @@ def print_config(section, key): cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key]) -@cli.argument('-ro', '--read-only', action='store_true', help='Operate in read-only mode.') +@cli.argument('-ro', '--read-only', arg_only=True, action='store_true', help='Operate in read-only mode.') @cli.argument('configs', nargs='*', arg_only=True, help='Configuration options to read or write.') @cli.subcommand("Read and write configuration settings.") def config(cli): diff --git a/lib/python/qmk/cli/list/keyboards.py b/lib/python/qmk/cli/list/keyboards.py index 53a7af75c..2a29ccb14 100644 --- a/lib/python/qmk/cli/list/keyboards.py +++ b/lib/python/qmk/cli/list/keyboards.py @@ -6,6 +6,7 @@ import glob from milc import cli + @cli.subcommand("List the keyboards currently defined within QMK") def list_keyboards(cli): """List the keyboards currently defined within QMK diff --git a/lib/python/qmk/errors.py b/lib/python/qmk/errors.py index f9bf5b9af..4a8a91556 100644 --- a/lib/python/qmk/errors.py +++ b/lib/python/qmk/errors.py @@ -1,6 +1,5 @@ class NoSuchKeyboardError(Exception): """Raised when we can't find a keyboard/keymap directory. """ - def __init__(self, message): self.message = message diff --git a/lib/python/qmk/tests/attrdict.py b/lib/python/qmk/tests/attrdict.py index 391c75c4e..a2584b923 100644 --- a/lib/python/qmk/tests/attrdict.py +++ b/lib/python/qmk/tests/attrdict.py @@ -3,7 +3,6 @@ class AttrDict(dict): This should only be used to mock objects for unit testing. Please do not use this outside of qmk.tests. """ - def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index 9a9dc4b95..55b8d253f 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py @@ -7,7 +7,7 @@ def check_subcommand(command, *args): def test_cformat(): - assert check_subcommand('cformat', 'tmk_core/common/backlight.c').returncode == 0 + assert check_subcommand('cformat', 'tmk_core/common/keyboard.c').returncode == 0 def test_compile(): diff --git a/lib/python/qmk/tests/test_qmk_path.py b/lib/python/qmk/tests/test_qmk_path.py index 94dbf3a6a..d6961a0f6 100644 --- a/lib/python/qmk/tests/test_qmk_path.py +++ b/lib/python/qmk/tests/test_qmk_path.py @@ -10,4 +10,4 @@ def test_keymap_onekey_pytest(): def test_normpath(): path = qmk.path.normpath('lib/python') - assert path == os.environ['ORIG_CWD'] + '/lib/python' + assert path == os.path.join(os.environ['ORIG_CWD'], 'lib/python') -- cgit v1.2.3-70-g09d2 From f7bdc54c697ff24bec1bd0781666ac05401bafb2 Mon Sep 17 00:00:00 2001 From: skullydazed Date: Wed, 20 Nov 2019 14:54:18 -0800 Subject: Add flake8 to our test suite and fix all errors (#7379) * Add flake8 to our test suite and fix all errors * Add some documentation --- bin/qmk | 4 +- docs/cli_development.md | 32 ++++++++++++ lib/python/kle2xy.py | 2 +- lib/python/qmk/cli/compile.py | 3 -- lib/python/qmk/cli/config.py | 94 ++++++++++++++++++++++-------------- lib/python/qmk/cli/doctor.py | 2 - lib/python/qmk/cli/flash.py | 10 +--- lib/python/qmk/cli/json/keymap.py | 1 - lib/python/qmk/cli/kle2json.py | 4 +- lib/python/qmk/cli/list/keyboards.py | 26 +++++----- lib/python/qmk/cli/pytest.py | 16 +++--- lib/python/qmk/keymap.py | 4 -- lib/python/qmk/path.py | 1 - requirements.txt | 2 + setup.cfg | 9 ++++ 15 files changed, 125 insertions(+), 85 deletions(-) (limited to 'lib/python/qmk/cli/list') diff --git a/bin/qmk b/bin/qmk index 5da8673ba..4d5b3d884 100755 --- a/bin/qmk +++ b/bin/qmk @@ -41,7 +41,7 @@ else: os.environ['QMK_VERSION'] = 'nogit-' + strftime('%Y-%m-%d-%H:%M:%S') + '-dirty' # Setup the CLI -import milc +import milc # noqa milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}' @@ -61,7 +61,7 @@ def main(): os.chdir(qmk_dir) # Import the subcommands - import qmk.cli + import qmk.cli # noqa # Execute return_code = milc.cli() diff --git a/docs/cli_development.md b/docs/cli_development.md index f5c7ad139..cc8c59d06 100644 --- a/docs/cli_development.md +++ b/docs/cli_development.md @@ -173,3 +173,35 @@ You will only be able to access these arguments using `cli.args`. For example: ``` cli.log.info('Reading from %s and writing to %s', cli.args.filename, cli.args.output) ``` + +# Testing, and Linting, and Formatting (oh my!) + +We use nose2, flake8, and yapf to test, lint, and format code. You can use the `pytest` and `pyformat` subcommands to run these tests: + +### Testing and Linting + + qmk pytest + +### Formatting + + qmk pyformat + +## Formatting Details + +We use [yapf](https://github.com/google/yapf) to automatically format code. Our configuration is in the `[yapf]` section of `setup.cfg`. + +?> Tip- Many editors can use yapf as a plugin to automatically format code as you type. + +## Testing Details + +Our tests can be found in `lib/python/qmk/tests/`. You will find both unit and integration tests in this directory. We hope you will write both unit and integration tests for your code, but if you do not please favor integration tests. + +If your PR does not include a comprehensive set of tests please add comments like this to your code so that other people know where they can help: + + # TODO(unassigned/): Write tests + +We use [nose2](https://nose2.readthedocs.io/en/latest/getting_started.html) to run our tests. You can refer to the nose2 documentation for more details on what you can do in your test functions. + +## Linting Details + +We use flake8 to lint our code. Your code should pass flake8 before you open a PR. This will be checked when you run `qmk pytest` and by CI when you submit a PR. diff --git a/lib/python/kle2xy.py b/lib/python/kle2xy.py index 929144319..bff1d025b 100644 --- a/lib/python/kle2xy.py +++ b/lib/python/kle2xy.py @@ -46,7 +46,7 @@ class KLE2xy(list): if 'name' in properties: self.name = properties['name'] - def parse_layout(self, layout): + def parse_layout(self, layout): # noqa FIXME(skullydazed): flake8 says this has a complexity of 25, it should be refactored. # Wrap this in a dictionary so hjson will parse KLE raw data layout = '{"layout": [' + layout + ']}' layout = hjson.loads(layout)['layout'] diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py index 5c2980009..234ffb12c 100755 --- a/lib/python/qmk/cli/compile.py +++ b/lib/python/qmk/cli/compile.py @@ -2,9 +2,6 @@ You can compile a keymap already in the repo or using a QMK Configurator export. """ -import json -import os -import sys import subprocess from argparse import FileType diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py index c4ee20cba..e17d8bb9b 100644 --- a/lib/python/qmk/cli/config.py +++ b/lib/python/qmk/cli/config.py @@ -1,8 +1,5 @@ """Read and write configuration settings """ -import os -import subprocess - from milc import cli @@ -12,6 +9,54 @@ def print_config(section, key): cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key]) +def show_config(): + """Print the current configuration to stdout. + """ + for section in cli.config: + for key in cli.config[section]: + print_config(section, key) + + +def parse_config_token(config_token): + """Split a user-supplied configuration-token into its components. + """ + section = option = value = None + + if '=' in config_token and '.' not in config_token: + cli.log.error('Invalid configuration token, the key must be of the form
.