Add flake8 to our test suite and fix all errors (#7379)
* Add flake8 to our test suite and fix all errors * Add some documentation
This commit is contained in:
parent
d2115f7d1c
commit
f7bdc54c69
15 changed files with 125 additions and 85 deletions
4
bin/qmk
4
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()
|
||||
|
|
|
@ -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/<yourGithubUsername>): Write <unit|integration> 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.
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 <section>.<option>: %s', config_token)
|
||||
return section, option, value
|
||||
|
||||
# Separate the key (<section>.<option>) from the value
|
||||
if '=' in config_token:
|
||||
key, value = config_token.split('=')
|
||||
else:
|
||||
key = config_token
|
||||
|
||||
# Extract the section and option from the key
|
||||
if '.' in key:
|
||||
section, option = key.split('.', 1)
|
||||
else:
|
||||
section = key
|
||||
|
||||
return section, option, value
|
||||
|
||||
|
||||
def set_config(section, option, value):
|
||||
"""Set a config key in the running config.
|
||||
"""
|
||||
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, option, cli.config[section][option], value)
|
||||
|
||||
if not cli.args.read_only:
|
||||
if value == 'None':
|
||||
del cli.config[section][option]
|
||||
else:
|
||||
cli.config[section][option] = value
|
||||
|
||||
|
||||
@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.")
|
||||
|
@ -33,12 +78,7 @@ def config(cli):
|
|||
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
|
||||
return show_config()
|
||||
|
||||
# Process config_tokens
|
||||
save_config = False
|
||||
|
@ -46,43 +86,23 @@ def config(cli):
|
|||
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
|
||||
section, option, value = parse_config_token(config_token)
|
||||
|
||||
# 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)
|
||||
if option and '.' in option:
|
||||
cli.log.error('Config keys may not have more than one period! "%s" is not valid.', config_token)
|
||||
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 section and option and value:
|
||||
# Write a configuration option
|
||||
set_config(section, option, 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:
|
||||
elif section and option:
|
||||
# Display a single key
|
||||
print_config(section, config_key)
|
||||
print_config(section, option)
|
||||
|
||||
elif section:
|
||||
# Display an entire section
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
Check up for QMK environment.
|
||||
"""
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from glob import glob
|
||||
|
||||
from milc import cli
|
||||
|
||||
|
|
|
@ -3,17 +3,11 @@
|
|||
You can compile a keymap already in the repo or using a QMK Configurator export.
|
||||
A bootloader must be specified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from argparse import FileType
|
||||
|
||||
from milc import cli
|
||||
from qmk.commands import create_make_command
|
||||
from qmk.commands import parse_configurator_json
|
||||
from qmk.commands import compile_configurator_json
|
||||
|
||||
import qmk.path
|
||||
from milc import cli
|
||||
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
|
||||
|
||||
|
||||
def print_bootloader_help():
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from milc import cli
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
"""Convert raw KLE to JSON
|
||||
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from argparse import FileType
|
||||
from decimal import Decimal
|
||||
from collections import OrderedDict
|
||||
|
||||
|
@ -23,7 +21,7 @@ class CustomJSONEncoder(json.JSONEncoder):
|
|||
return float(obj)
|
||||
except TypeError:
|
||||
pass
|
||||
return JSONEncoder.default(self, obj)
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
@cli.argument('filename', help='The KLE raw txt to convert')
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
"""List the keyboards currently defined within QMK
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
|
||||
from milc import cli
|
||||
|
||||
BASE_PATH = os.path.join(os.getcwd(), "keyboards") + os.path.sep
|
||||
KB_WILDCARD = os.path.join(BASE_PATH, "**", "rules.mk")
|
||||
|
||||
|
||||
def find_name(path):
|
||||
"""Determine the keyboard name by stripping off the base_path and rules.mk.
|
||||
"""
|
||||
return path.replace(BASE_PATH, "").replace(os.path.sep + "rules.mk", "")
|
||||
|
||||
|
||||
@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]
|
||||
paths = [path for path in glob.iglob(KB_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)
|
||||
# Extract the keyboard name from the path and print it
|
||||
for keyboard_name in sorted(map(find_name, paths)):
|
||||
print(keyboard_name)
|
||||
|
|
|
@ -2,19 +2,15 @@
|
|||
|
||||
QMK script to run unit and integration tests against our python code.
|
||||
"""
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from milc import cli
|
||||
|
||||
|
||||
@cli.subcommand('QMK Python Unit Tests')
|
||||
def pytest(cli):
|
||||
"""Use nose2 to run unittests
|
||||
"""Run several linting/testing commands.
|
||||
"""
|
||||
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(argv=['nose2', '-v'])
|
||||
flake8 = subprocess.run(['flake8', 'lib/python', 'bin/qmk'])
|
||||
nose2 = subprocess.run(['nose2', '-v'])
|
||||
return flake8.returncode | nose2.returncode
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
"""Functions that help you work with QMK keymaps.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from traceback import format_exc
|
||||
|
||||
import qmk.path
|
||||
from qmk.errors import NoSuchKeyboardError
|
||||
|
||||
# The `keymap.c` template to use when a keyboard doesn't have its own
|
||||
DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"""
|
||||
import logging
|
||||
import os
|
||||
from pkgutil import walk_packages
|
||||
|
||||
from qmk.errors import NoSuchKeyboardError
|
||||
|
||||
|
|
|
@ -4,3 +4,5 @@ appdirs
|
|||
argcomplete
|
||||
colorama
|
||||
hjson
|
||||
nose2
|
||||
flake8
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
# Python settings for QMK
|
||||
[flake8]
|
||||
ignore =
|
||||
# QMK is ok with long lines.
|
||||
E501
|
||||
per_file_ignores =
|
||||
**/__init__.py:F401
|
||||
|
||||
# Let's slowly crank this down
|
||||
max_complexity=16
|
||||
|
||||
[yapf]
|
||||
# Align closing bracket with visual indentation.
|
||||
|
|
Loading…
Reference in a new issue