aboutsummaryrefslogtreecommitdiff
path: root/lib/python
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python')
-rw-r--r--lib/python/milc.py88
-rw-r--r--lib/python/qmk/cli/__init__.py7
-rw-r--r--lib/python/qmk/cli/cformat.py5
-rwxr-xr-xlib/python/qmk/cli/doctor.py11
-rwxr-xr-xlib/python/qmk/cli/json2c.py8
-rwxr-xr-xlib/python/qmk/cli/kle2json.py8
-rwxr-xr-xlib/python/qmk/cli/new/keymap.py2
-rw-r--r--lib/python/qmk/keymap.py5
8 files changed, 74 insertions, 60 deletions
diff --git a/lib/python/milc.py b/lib/python/milc.py
index 83edfc7f5..eb18984eb 100644
--- a/lib/python/milc.py
+++ b/lib/python/milc.py
@@ -242,15 +242,24 @@ class SubparserWrapper(object):
242 242
243 This also stores the default for the argument in `self.cli.default_arguments`. 243 This also stores the default for the argument in `self.cli.default_arguments`.
244 """ 244 """
245 if 'action' in kwargs and kwargs['action'] == 'store_boolean': 245 if kwargs.get('action') == 'store_boolean':
246 # Store boolean will call us again with the enable/disable flag arguments 246 # Store boolean will call us again with the enable/disable flag arguments
247 return handle_store_boolean(self, *args, **kwargs) 247 return handle_store_boolean(self, *args, **kwargs)
248 248
249 self.cli.acquire_lock() 249 self.cli.acquire_lock()
250 argument_name = self.cli.get_argument_name(*args, **kwargs)
251
250 self.subparser.add_argument(*args, **kwargs) 252 self.subparser.add_argument(*args, **kwargs)
253
254 if kwargs.get('action') == 'store_false':
255 self.cli._config_store_false.append(argument_name)
256
257 if kwargs.get('action') == 'store_true':
258 self.cli._config_store_true.append(argument_name)
259
251 if self.submodule not in self.cli.default_arguments: 260 if self.submodule not in self.cli.default_arguments:
252 self.cli.default_arguments[self.submodule] = {} 261 self.cli.default_arguments[self.submodule] = {}
253 self.cli.default_arguments[self.submodule][self.cli.get_argument_name(*args, **kwargs)] = kwargs.get('default') 262 self.cli.default_arguments[self.submodule][argument_name] = kwargs.get('default')
254 self.cli.release_lock() 263 self.cli.release_lock()
255 264
256 265
@@ -268,11 +277,13 @@ class MILC(object):
268 277
269 # Define some basic info 278 # Define some basic info
270 self.acquire_lock() 279 self.acquire_lock()
280 self._config_store_true = []
281 self._config_store_false = []
271 self._description = None 282 self._description = None
272 self._entrypoint = None 283 self._entrypoint = None
273 self._inside_context_manager = False 284 self._inside_context_manager = False
274 self.ansi = ansi_colors 285 self.ansi = ansi_colors
275 self.arg_only = [] 286 self.arg_only = {}
276 self.config = self.config_source = None 287 self.config = self.config_source = None
277 self.config_file = None 288 self.config_file = None
278 self.default_arguments = {} 289 self.default_arguments = {}
@@ -377,7 +388,7 @@ class MILC(object):
377 self.add_argument('--log-file', help='File to write log messages to') 388 self.add_argument('--log-file', help='File to write log messages to')
378 self.add_argument('--color', action='store_boolean', default=True, help='color in output') 389 self.add_argument('--color', action='store_boolean', default=True, help='color in output')
379 self.add_argument('--config-file', help='The location for the configuration file') 390 self.add_argument('--config-file', help='The location for the configuration file')
380 self.arg_only.append('config_file') 391 self.arg_only['config_file'] = ['general']
381 392
382 def add_subparsers(self, title='Sub-commands', **kwargs): 393 def add_subparsers(self, title='Sub-commands', **kwargs):
383 if self._inside_context_manager: 394 if self._inside_context_manager:
@@ -427,17 +438,20 @@ class MILC(object):
427 raise RuntimeError('You must run this before the with statement!') 438 raise RuntimeError('You must run this before the with statement!')
428 439
429 def argument_function(handler): 440 def argument_function(handler):
430 if 'arg_only' in kwargs and kwargs['arg_only']: 441 subcommand_name = handler.__name__.replace("_", "-")
442
443 if kwargs.get('arg_only'):
431 arg_name = self.get_argument_name(*args, **kwargs) 444 arg_name = self.get_argument_name(*args, **kwargs)
432 self.arg_only.append(arg_name) 445 if arg_name not in self.arg_only:
446 self.arg_only[arg_name] = []
447 self.arg_only[arg_name].append(subcommand_name)
433 del kwargs['arg_only'] 448 del kwargs['arg_only']
434 449
435 name = handler.__name__.replace("_", "-")
436 if handler is self._entrypoint: 450 if handler is self._entrypoint:
437 self.add_argument(*args, **kwargs) 451 self.add_argument(*args, **kwargs)
438 452
439 elif name in self.subcommands: 453 elif subcommand_name in self.subcommands:
440 self.subcommands[name].add_argument(*args, **kwargs) 454 self.subcommands[subcommand_name].add_argument(*args, **kwargs)
441 455
442 else: 456 else:
443 raise RuntimeError('Decorated function is not entrypoint or subcommand!') 457 raise RuntimeError('Decorated function is not entrypoint or subcommand!')
@@ -511,35 +525,37 @@ class MILC(object):
511 if argument in ('subparsers', 'entrypoint'): 525 if argument in ('subparsers', 'entrypoint'):
512 continue 526 continue
513 527
514 if argument not in self.arg_only: 528 # Find the argument's section
515 # Find the argument's section 529 # Underscores in command's names are converted to dashes during initialization.
516 # Underscores in command's names are converted to dashes during initialization. 530 # TODO(Erovia) Find a better solution
517 # TODO(Erovia) Find a better solution 531 entrypoint_name = self._entrypoint.__name__.replace("_", "-")
518 entrypoint_name = self._entrypoint.__name__.replace("_", "-") 532 if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]:
519 if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]: 533 argument_found = True
520 argument_found = True 534 section = self._entrypoint.__name__
521 section = self._entrypoint.__name__ 535 if argument in self.default_arguments['general']:
522 if argument in self.default_arguments['general']: 536 argument_found = True
523 argument_found = True 537 section = 'general'
524 section = 'general' 538
525 539 if not argument_found:
526 if not argument_found: 540 raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!')
527 raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!') 541 exit(1)
528 exit(1) 542
543 if argument not in self.arg_only or section not in self.arg_only[argument]:
544 # Determine the arg value and source
545 arg_value = getattr(self.args, argument)
546 if argument in self._config_store_true and arg_value:
547 passed_on_cmdline = True
548 elif argument in self._config_store_false and not arg_value:
549 passed_on_cmdline = True
550 elif arg_value is not None:
551 passed_on_cmdline = True
552 else:
553 passed_on_cmdline = False
529 554
530 # Merge this argument into self.config 555 # Merge this argument into self.config
531 if argument in self.default_arguments['general'] or argument in self.default_arguments[entrypoint_name]: 556 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]):
532 arg_value = getattr(self.args, argument) 557 self.config[section][argument] = arg_value
533 if arg_value is not None: 558 self.config_source[section][argument] = 'argument'
534 self.config[section][argument] = arg_value
535 self.config_source[section][argument] = 'argument'
536 else:
537 if argument not in self.config[entrypoint_name]:
538 # Check if the argument exist for this section
539 arg = getattr(self.args, argument)
540 if arg is not None:
541 self.config[section][argument] = arg
542 self.config_source[section][argument] = 'argument'
543 559
544 self.release_lock() 560 self.release_lock()
545 561
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index eb524217c..394a1353b 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -2,6 +2,8 @@
2 2
3We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. 3We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
4""" 4"""
5import sys
6
5from milc import cli 7from milc import cli
6 8
7from . import cformat 9from . import cformat
@@ -19,5 +21,6 @@ from . import new
19from . import pyformat 21from . import pyformat
20from . import pytest 22from . import pytest
21 23
22if not hasattr(cli, 'config_source'): 24if sys.version_info[0] != 3 or sys.version_info[1] < 6:
23 cli.log.warning("Your QMK CLI is out of date. Please upgrade with `pip3 install --upgrade qmk` or by using your package manager.") 25 cli.log.error('Your Python is too old! Please upgrade to Python 3.6 or later.')
26 exit(127)
diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py
index 536ee30fd..0cd8b6192 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):
22 cli.log.warn('No changes detected. Use "qmk cformat -a" to format all files') 22 cli.log.warn('No changes detected. Use "qmk cformat -a" to format all files')
23 return False 23 return False
24 if files and all_files: 24 if files and all_files:
25 cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(cli.args.files)) 25 cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(files))
26 # 3.6+: Can remove the str casting, python will cast implicitly 26 subprocess.run(clang_format + [file for file in files], check=True)
27 subprocess.run(clang_format + [str(file) for file in files], check=True)
28 cli.log.info('Successfully formatted the C code.') 27 cli.log.info('Successfully formatted the C code.')
29 28
30 except subprocess.CalledProcessError: 29 except subprocess.CalledProcessError:
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py
index 3c74fae69..3c4624837 100755
--- a/lib/python/qmk/cli/doctor.py
+++ b/lib/python/qmk/cli/doctor.py
@@ -135,16 +135,15 @@ def check_udev_rules():
135 } 135 }
136 136
137 if udev_dir.exists(): 137 if udev_dir.exists():
138 udev_rules = [str(rule_file) for rule_file in udev_dir.glob('*.rules')] 138 udev_rules = [rule_file for rule_file in udev_dir.glob('*.rules')]
139 current_rules = set() 139 current_rules = set()
140 140
141 # Collect all rules from the config files 141 # Collect all rules from the config files
142 for rule_file in udev_rules: 142 for rule_file in udev_rules:
143 with open(rule_file, "r") as fd: 143 for line in rule_file.read_text().split('\n'):
144 for line in fd.readlines(): 144 line = line.strip()
145 line = line.strip() 145 if not line.startswith("#") and len(line):
146 if not line.startswith("#") and len(line): 146 current_rules.add(line)
147 current_rules.add(line)
148 147
149 # Check if the desired rules are among the currently present rules 148 # Check if the desired rules are among the currently present rules
150 for bootloader, rules in desired_rules.items(): 149 for bootloader, rules in desired_rules.items():
diff --git a/lib/python/qmk/cli/json2c.py b/lib/python/qmk/cli/json2c.py
index 46c4d04bb..521840507 100755
--- a/lib/python/qmk/cli/json2c.py
+++ b/lib/python/qmk/cli/json2c.py
@@ -10,29 +10,27 @@ import qmk.path
10 10
11@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to') 11@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
12@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") 12@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
13@cli.argument('filename', arg_only=True, help='Configurator JSON file') 13@cli.argument('filename', type=qmk.path.normpath, arg_only=True, help='Configurator JSON file')
14@cli.subcommand('Creates a keymap.c from a QMK Configurator export.') 14@cli.subcommand('Creates a keymap.c from a QMK Configurator export.')
15def json2c(cli): 15def json2c(cli):
16 """Generate a keymap.c from a configurator export. 16 """Generate a keymap.c from a configurator export.
17 17
18 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. 18 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.
19 """ 19 """
20 cli.args.filename = qmk.path.normpath(cli.args.filename)
21
22 # Error checking 20 # Error checking
23 if not cli.args.filename.exists(): 21 if not cli.args.filename.exists():
24 cli.log.error('JSON file does not exist!') 22 cli.log.error('JSON file does not exist!')
25 cli.print_usage() 23 cli.print_usage()
26 exit(1) 24 exit(1)
27 25
28 if str(cli.args.filename) == '-': 26 if cli.args.filename.name == '-':
29 # TODO(skullydazed/anyone): Read file contents from STDIN 27 # TODO(skullydazed/anyone): Read file contents from STDIN
30 cli.log.error('Reading from STDIN is not (yet) supported.') 28 cli.log.error('Reading from STDIN is not (yet) supported.')
31 cli.print_usage() 29 cli.print_usage()
32 exit(1) 30 exit(1)
33 31
34 # Environment processing 32 # Environment processing
35 if cli.args.output == ('-'): 33 if cli.args.output.name == ('-'):
36 cli.args.output = None 34 cli.args.output = None
37 35
38 # Parse the configurator json 36 # Parse the configurator json
diff --git a/lib/python/qmk/cli/kle2json.py b/lib/python/qmk/cli/kle2json.py
index 3ed6594e0..5268462f9 100755
--- a/lib/python/qmk/cli/kle2json.py
+++ b/lib/python/qmk/cli/kle2json.py
@@ -37,12 +37,12 @@ def kle2json(cli):
37 file_path = Path(os.environ['ORIG_CWD'], cli.args.filename) 37 file_path = Path(os.environ['ORIG_CWD'], cli.args.filename)
38 # Check for valid file_path for more graceful failure 38 # Check for valid file_path for more graceful failure
39 if not file_path.exists(): 39 if not file_path.exists():
40 return cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', str(file_path)) 40 return cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', file_path)
41 out_path = file_path.parent 41 out_path = file_path.parent
42 raw_code = file_path.open().read() 42 raw_code = file_path.open().read()
43 # Check if info.json exists, allow overwrite with force 43 # Check if info.json exists, allow overwrite with force
44 if Path(out_path, "info.json").exists() and not cli.args.force: 44 if Path(out_path, "info.json").exists() and not cli.args.force:
45 cli.log.error('File {fg_cyan}%s/info.json{style_reset_all} already exists, use -f or --force to overwrite.', str(out_path)) 45 cli.log.error('File {fg_cyan}%s/info.json{style_reset_all} already exists, use -f or --force to overwrite.', out_path)
46 return False 46 return False
47 try: 47 try:
48 # Convert KLE raw to x/y coordinates (using kle2xy package from skullydazed) 48 # Convert KLE raw to x/y coordinates (using kle2xy package from skullydazed)
@@ -69,7 +69,7 @@ def kle2json(cli):
69 # Replace layout in keyboard json 69 # Replace layout in keyboard json
70 keyboard = keyboard.replace('"LAYOUT_JSON_HERE"', layout) 70 keyboard = keyboard.replace('"LAYOUT_JSON_HERE"', layout)
71 # Write our info.json 71 # Write our info.json
72 file = open(str(out_path) + "/info.json", "w") 72 file = open(out_path + "/info.json", "w")
73 file.write(keyboard) 73 file.write(keyboard)
74 file.close() 74 file.close()
75 cli.log.info('Wrote out {fg_cyan}%s/info.json', str(out_path)) 75 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 5ae262856..474fe7974 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):
40 exit(1) 40 exit(1)
41 41
42 # create user directory with default keymap files 42 # create user directory with default keymap files
43 shutil.copytree(str(keymap_path_default), str(keymap_path_new), symlinks=True) 43 shutil.copytree(keymap_path_default, keymap_path_new, symlinks=True)
44 44
45 # end message to user 45 # end message to user
46 cli.log.info("%s keymap directory created in: %s", keymap, keymap_path_new) 46 cli.log.info("%s keymap directory created in: %s", keymap, keymap_path_new)
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 4aa87de20..69cdc8d5b 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -1,6 +1,5 @@
1"""Functions that help you work with QMK keymaps. 1"""Functions that help you work with QMK keymaps.
2""" 2"""
3import os
4from pathlib import Path 3from pathlib import Path
5 4
6import qmk.path 5import qmk.path
@@ -127,7 +126,7 @@ def list_keymaps(keyboard_name):
127 while kb_path != keyboards_dir: 126 while kb_path != keyboards_dir:
128 keymaps_dir = kb_path / "keymaps" 127 keymaps_dir = kb_path / "keymaps"
129 if keymaps_dir.exists(): 128 if keymaps_dir.exists():
130 names = names.union([keymap for keymap in os.listdir(str(keymaps_dir)) if (keymaps_dir / keymap / "keymap.c").is_file()]) 129 names = names.union([keymap for keymap in keymaps_dir.iterdir() if (keymaps_dir / keymap / "keymap.c").is_file()])
131 kb_path = kb_path.parent 130 kb_path = kb_path.parent
132 131
133 # if community layouts are supported, get them 132 # if community layouts are supported, get them
@@ -135,6 +134,6 @@ def list_keymaps(keyboard_name):
135 for layout in rules_mk["LAYOUTS"].split(): 134 for layout in rules_mk["LAYOUTS"].split():
136 cl_path = Path.cwd() / "layouts" / "community" / layout 135 cl_path = Path.cwd() / "layouts" / "community" / layout
137 if cl_path.exists(): 136 if cl_path.exists():
138 names = names.union([keymap for keymap in os.listdir(str(cl_path)) if (cl_path / keymap / "keymap.c").is_file()]) 137 names = names.union([keymap for keymap in cl_path.iterdir() if (cl_path / keymap / "keymap.c").is_file()])
139 138
140 return sorted(names) 139 return sorted(names)