summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/python/milc.py14
-rwxr-xr-xlib/python/qmk/cli/compile.py2
-rw-r--r--lib/python/qmk/cli/docs.py10
-rwxr-xr-xlib/python/qmk/cli/doctor.py67
-rw-r--r--lib/python/qmk/cli/flash.py5
-rw-r--r--lib/python/qmk/commands.py11
-rw-r--r--lib/python/qmk/tests/test_qmk_errors.py2
7 files changed, 68 insertions, 43 deletions
diff --git a/lib/python/milc.py b/lib/python/milc.py
index e8599eff3f..4392c8376a 100644
--- a/lib/python/milc.py
+++ b/lib/python/milc.py
@@ -595,23 +595,25 @@ class MILC(object):
return entrypoint_func
- def add_subcommand(self, handler, description, name=None, **kwargs):
+ def add_subcommand(self, handler, description, name=None, hidden=False, **kwargs):
"""Register a subcommand.
If name is not provided we use `handler.__name__`.
"""
+
if self._inside_context_manager:
raise RuntimeError('You must run this before the with statement!')
if self._subparsers is None:
- self.add_subparsers()
+ self.add_subparsers(metavar="")
if not name:
name = handler.__name__.replace("_", "-")
self.acquire_lock()
-
- kwargs['help'] = description
+ if not hidden:
+ self._subparsers.metavar = "{%s,%s}" % (self._subparsers.metavar[1:-1], name) if self._subparsers.metavar else "{%s%s}" % (self._subparsers.metavar[1:-1], name)
+ kwargs['help'] = description
self.subcommands[name] = SubparserWrapper(self, name, self._subparsers.add_parser(name, **kwargs))
self.subcommands[name].set_defaults(entrypoint=handler)
@@ -619,11 +621,11 @@ class MILC(object):
return handler
- def subcommand(self, description, **kwargs):
+ def subcommand(self, description, hidden=False, **kwargs):
"""Decorator to register a subcommand.
"""
def subcommand_function(handler):
- return self.add_subcommand(handler, description, **kwargs)
+ return self.add_subcommand(handler, description, hidden=hidden, **kwargs)
return subcommand_function
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py
index 234ffb12ca..8e2d0cdbf4 100755
--- a/lib/python/qmk/cli/compile.py
+++ b/lib/python/qmk/cli/compile.py
@@ -37,7 +37,7 @@ def compile(cli):
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
# Compile the keymap
- command = compile_configurator_json(cli.args.filename)
+ command = compile_configurator_json(user_keymap)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
diff --git a/lib/python/qmk/cli/docs.py b/lib/python/qmk/cli/docs.py
index b419891396..163c8b8015 100644
--- a/lib/python/qmk/cli/docs.py
+++ b/lib/python/qmk/cli/docs.py
@@ -1,21 +1,19 @@
"""Serve QMK documentation locally
"""
import http.server
+import os
from milc import cli
-class DocsHandler(http.server.SimpleHTTPRequestHandler):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, directory='docs', **kwargs)
-
-
@cli.argument('-p', '--port', default=8936, type=int, help='Port number to use.')
@cli.subcommand('Run a local webserver for QMK documentation.')
def docs(cli):
"""Spin up a local HTTPServer instance for the QMK docs.
"""
- with http.server.HTTPServer(('', cli.config.docs.port), DocsHandler) as httpd:
+ os.chdir('docs')
+
+ with http.server.HTTPServer(('', cli.config.docs.port), http.server.SimpleHTTPRequestHandler) as httpd:
cli.log.info("Serving QMK docs at http://localhost:%d/", cli.config.docs.port)
cli.log.info("Press Control+C to exit.")
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py
index 1010eafb33..6ddc5571b4 100755
--- a/lib/python/qmk/cli/doctor.py
+++ b/lib/python/qmk/cli/doctor.py
@@ -2,13 +2,24 @@
Check up for QMK environment.
"""
+import os
import platform
import shutil
import subprocess
+import glob
from milc import cli
+def _udev_rule(vid, pid=None):
+ """ Helper function that return udev rules
+ """
+ if pid:
+ return 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", ATTRS{idProduct}=="%s", MODE:="0666"' % (vid, pid)
+ else:
+ return 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", MODE:="0666"' % vid
+
+
@cli.subcommand('Basic QMK environment checks')
def doctor(cli):
"""Basic QMK environment checks.
@@ -17,7 +28,6 @@ def doctor(cli):
TODO(unclaimed):
* [ ] Compile a trivial program with each compiler
- * [ ] Check for udev entries on linux
"""
cli.log.info('QMK Doctor is checking your environment.')
@@ -39,30 +49,49 @@ def doctor(cli):
ok = False
# Determine our OS and run platform specific tests
- OS = platform.system()
+ OS = platform.system() # noqa (N806), uppercase name is ok in this instance
if OS == "Darwin":
cli.log.info("Detected {fg_cyan}macOS.")
elif OS == "Linux":
cli.log.info("Detected {fg_cyan}Linux.")
- if shutil.which('systemctl'):
- mm_check = subprocess.run(['systemctl', 'list-unit-files'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, universal_newlines=True)
- if mm_check.returncode == 0:
- mm = False
- for line in mm_check.stdout.split('\n'):
- if 'ModemManager' in line and 'enabled' in line:
- mm = True
-
- if mm:
- cli.log.warn("{bg_yellow}Detected ModemManager. Please disable it if you are using a Pro-Micro.")
-
- else:
- cli.log.error('{bg_red}Could not run `systemctl list-unit-files`:')
- cli.log.error(mm_check.stderr)
-
- else:
- cli.log.warn("Can't find systemctl to check for ModemManager.")
+ # Checking for udev rules
+ udev_dir = "/etc/udev/rules.d/"
+ # These are the recommended udev rules
+ desired_rules = {
+ 'dfu': {_udev_rule("03eb", "2ff4"), _udev_rule("03eb", "2ffb"), _udev_rule("03eb", "2ff0")},
+ 'tmk': {_udev_rule("feed")},
+ 'input_club': {_udev_rule("1c11")},
+ 'stm32': {_udev_rule("1eaf", "0003"), _udev_rule("0483", "df11")},
+ 'caterina': {'ATTRS{idVendor}=="2a03", ENV{ID_MM_DEVICE_IGNORE}="1"', 'ATTRS{idVendor}=="2341", ENV{ID_MM_DEVICE_IGNORE}="1"'},
+ }
+
+ if os.path.exists(udev_dir):
+ udev_rules = [rule for rule in glob.iglob(os.path.join(udev_dir, "*.rules")) if os.path.isfile(rule)]
+ # Collect all rules from the config files
+ current_rules = set()
+ for rule in udev_rules:
+ with open(rule, "r") as fd:
+ for line in fd.readlines():
+ 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():
+ if not rules.issubset(current_rules):
+ # If the rules for catalina are not present, check if ModemManager is running
+ if bootloader == "caterina":
+ if shutil.which("systemctl"):
+ mm_check = subprocess.run(["systemctl", "--quiet", "is-active", "ModemManager.service"], timeout=10)
+ if mm_check.returncode == 0:
+ ok = False
+ cli.log.warn("{bg_yellow}Detected ModemManager without udev rules. Please either disable it or set the appropriate udev rules if you are using a Pro Micro.")
+ else:
+ cli.log.warn("Can't find systemctl to check for ModemManager.")
+ else:
+ cli.log.warn("{bg_yellow}Missing udev rules for '%s' boards. You'll need to use `sudo` in order to flash them.", bootloader)
else:
cli.log.info("Assuming {fg_cyan}Windows.")
diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py
index 031cb94967..e897174a20 100644
--- a/lib/python/qmk/cli/flash.py
+++ b/lib/python/qmk/cli/flash.py
@@ -4,6 +4,7 @@ You can compile a keymap already in the repo or using a QMK Configurator export.
A bootloader must be specified.
"""
import subprocess
+from argparse import FileType
import qmk.path
from milc import cli
@@ -28,7 +29,7 @@ def print_bootloader_help():
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
-@cli.argument('filename', nargs='?', arg_only=True, help='The configurator export JSON to compile. Use this if you dont want to specify a keymap and keyboard.')
+@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export JSON to compile.')
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
@@ -65,7 +66,7 @@ def flash(cli):
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
# Convert the JSON into a C file and write it to disk.
- command = compile_configurator_json(cli.args.filename, cli.args.bootloader)
+ command = compile_configurator_json(user_keymap, cli.args.bootloader)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index f83a89578e..6067d49ae7 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -25,16 +25,14 @@ def create_make_command(keyboard, keymap, target=None):
return ['make', ':'.join((keyboard, keymap, target))]
-def parse_configurator_json(configurator_filename):
+def parse_configurator_json(configurator_file):
"""Open and parse a configurator json export
"""
- file = open(configurator_filename)
- user_keymap = json.load(file)
- file.close()
+ user_keymap = json.load(configurator_file)
return user_keymap
-def compile_configurator_json(configurator_filename, bootloader=None):
+def compile_configurator_json(user_keymap, bootloader=None):
"""Convert a configurator export JSON file into a C file
Args:
@@ -47,9 +45,6 @@ def compile_configurator_json(configurator_filename, bootloader=None):
Returns:
A command to run to compile and flash the C file.
"""
- # Parse the configurator json
- user_keymap = parse_configurator_json(configurator_filename)
-
# Write the keymap C file
qmk.keymap.write(user_keymap['keyboard'], user_keymap['keymap'], user_keymap['layout'], user_keymap['layers'])
diff --git a/lib/python/qmk/tests/test_qmk_errors.py b/lib/python/qmk/tests/test_qmk_errors.py
index 1d8690b7ef..948e7ef741 100644
--- a/lib/python/qmk/tests/test_qmk_errors.py
+++ b/lib/python/qmk/tests/test_qmk_errors.py
@@ -1,7 +1,7 @@
from qmk.errors import NoSuchKeyboardError
-def test_NoSuchKeyboardError():
+def test_nosuchkeyboarderror():
try:
raise NoSuchKeyboardError("test message")
except NoSuchKeyboardError as e: