aboutsummaryrefslogtreecommitdiff
path: root/lib/python
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python')
-rw-r--r--lib/python/qmk/c_parse.py2
-rw-r--r--lib/python/qmk/cli/__init__.py72
-rw-r--r--lib/python/qmk/cli/config.py116
-rw-r--r--lib/python/qmk/cli/console.py302
-rw-r--r--lib/python/qmk/commands.py1
-rw-r--r--lib/python/qmk/constants.py2
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py10
7 files changed, 350 insertions, 155 deletions
diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py
index d4f39c883..991373d56 100644
--- a/lib/python/qmk/c_parse.py
+++ b/lib/python/qmk/c_parse.py
@@ -8,7 +8,7 @@ from milc import cli
8from qmk.comment_remover import comment_remover 8from qmk.comment_remover import comment_remover
9 9
10default_key_entry = {'x': -1, 'y': 0, 'w': 1} 10default_key_entry = {'x': -1, 'y': 0, 'w': 1}
11single_comment_regex = re.compile(r' */[/*].*$') 11single_comment_regex = re.compile(r'\s+/[/*].*$')
12multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE) 12multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
13 13
14 14
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index 6fe769fe7..02b721f34 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -12,6 +12,20 @@ from subprocess import run
12from milc import cli, __VERSION__ 12from milc import cli, __VERSION__
13from milc.questions import yesno 13from milc.questions import yesno
14 14
15import_names = {
16 # A mapping of package name to importable name
17 'pep8-naming': 'pep8ext_naming',
18 'pyusb': 'usb.core',
19}
20
21safe_commands = [
22 # A list of subcommands we always run, even when the module imports fail
23 'clone',
24 'config',
25 'env',
26 'setup',
27]
28
15 29
16def _run_cmd(*command): 30def _run_cmd(*command):
17 """Run a command in a subshell. 31 """Run a command in a subshell.
@@ -50,8 +64,8 @@ def _find_broken_requirements(requirements):
50 module_import = module_name.replace('-', '_') 64 module_import = module_name.replace('-', '_')
51 65
52 # Not every module is importable by its own name. 66 # Not every module is importable by its own name.
53 if module_name == "pep8-naming": 67 if module_name in import_names:
54 module_import = "pep8ext_naming" 68 module_import = import_names[module_name]
55 69
56 if not find_spec(module_import): 70 if not find_spec(module_import):
57 broken_modules.append(module_name) 71 broken_modules.append(module_name)
@@ -107,32 +121,31 @@ if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
107 121
108# Check to make sure we have all our dependencies 122# Check to make sure we have all our dependencies
109msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.' 123msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
110 124args = sys.argv[1:]
111if _broken_module_imports('requirements.txt'): 125while args and args[0][0] == '-':
112 if yesno('Would you like to install the required Python modules?'): 126 del args[0]
113 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt') 127
114 else: 128if not args or args[0] not in safe_commands:
115 print() 129 if _broken_module_imports('requirements.txt'):
116 print(msg_install % (str(Path('requirements.txt').resolve()),)) 130 if yesno('Would you like to install the required Python modules?'):
117 print() 131 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
118 exit(1) 132 else:
119 133 print()
120if cli.config.user.developer: 134 print(msg_install % (str(Path('requirements.txt').resolve()),))
121 args = sys.argv[1:] 135 print()
122 while args and args[0][0] == '-': 136 exit(1)
123 del args[0] 137
124 if not args or args[0] != 'config': 138 if cli.config.user.developer and _broken_module_imports('requirements-dev.txt'):
125 if _broken_module_imports('requirements-dev.txt'): 139 if yesno('Would you like to install the required developer Python modules?'):
126 if yesno('Would you like to install the required developer Python modules?'): 140 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
127 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt') 141 elif yesno('Would you like to disable developer mode?'):
128 elif yesno('Would you like to disable developer mode?'): 142 _run_cmd(sys.argv[0], 'config', 'user.developer=None')
129 _run_cmd(sys.argv[0], 'config', 'user.developer=None') 143 else:
130 else: 144 print()
131 print() 145 print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
132 print(msg_install % (str(Path('requirements-dev.txt').resolve()),)) 146 print('You can also turn off developer mode: qmk config user.developer=None')
133 print('You can also turn off developer mode: qmk config user.developer=None') 147 print()
134 print() 148 exit(1)
135 exit(1)
136 149
137# Import our subcommands 150# Import our subcommands
138from . import c2json # noqa 151from . import c2json # noqa
@@ -140,7 +153,8 @@ from . import cformat # noqa
140from . import chibios # noqa 153from . import chibios # noqa
141from . import clean # noqa 154from . import clean # noqa
142from . import compile # noqa 155from . import compile # noqa
143from . import config # noqa 156from milc.subcommand import config # noqa
157from . import console # noqa
144from . import docs # noqa 158from . import docs # noqa
145from . import doctor # noqa 159from . import doctor # noqa
146from . import fileformat # noqa 160from . import fileformat # noqa
diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py
deleted file mode 100644
index e17d8bb9b..000000000
--- a/lib/python/qmk/cli/config.py
+++ /dev/null
@@ -1,116 +0,0 @@
1"""Read and write configuration settings
2"""
3from milc import cli
4
5
6def print_config(section, key):
7 """Print a single config setting to stdout.
8 """
9 cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key])
10
11
12def show_config():
13 """Print the current configuration to stdout.
14 """
15 for section in cli.config:
16 for key in cli.config[section]:
17 print_config(section, key)
18
19
20def parse_config_token(config_token):
21 """Split a user-supplied configuration-token into its components.
22 """
23 section = option = value = None
24
25 if '=' in config_token and '.' not in config_token:
26 cli.log.error('Invalid configuration token, the key must be of the form <section>.<option>: %s', config_token)
27 return section, option, value
28
29 # Separate the key (<section>.<option>) from the value
30 if '=' in config_token:
31 key, value = config_token.split('=')
32 else:
33 key = config_token
34
35 # Extract the section and option from the key
36 if '.' in key:
37 section, option = key.split('.', 1)
38 else:
39 section = key
40
41 return section, option, value
42
43
44def set_config(section, option, value):
45 """Set a config key in the running config.
46 """
47 log_string = '%s.%s{fg_cyan}:{fg_reset} %s {fg_cyan}->{fg_reset} %s'
48 if cli.args.read_only:
49 log_string += ' {fg_red}(change not written)'
50
51 cli.echo(log_string, section, option, cli.config[section][option], value)
52
53 if not cli.args.read_only:
54 if value == 'None':
55 del cli.config[section][option]
56 else:
57 cli.config[section][option] = value
58
59
60@cli.argument('-ro', '--read-only', arg_only=True, action='store_true', help='Operate in read-only mode.')
61@cli.argument('configs', nargs='*', arg_only=True, help='Configuration options to read or write.')
62@cli.subcommand("Read and write configuration settings.")
63def config(cli):
64 """Read and write config settings.
65
66 This script iterates over the config_tokens supplied as argument. Each config_token has the following form:
67
68 section[.key][=value]
69
70 If only a section (EG 'compile') is supplied all keys for that section will be displayed.
71
72 If section.key is supplied the value for that single key will be displayed.
73
74 If section.key=value is supplied the value for that single key will be set.
75
76 If section.key=None is supplied the key will be deleted.
77
78 No validation is done to ensure that the supplied section.key is actually used by qmk scripts.
79 """
80 if not cli.args.configs:
81 return show_config()
82
83 # Process config_tokens
84 save_config = False
85
86 for argument in cli.args.configs:
87 # Split on space in case they quoted multiple config tokens
88 for config_token in argument.split(' '):
89 section, option, value = parse_config_token(config_token)
90
91 # Validation
92 if option and '.' in option:
93 cli.log.error('Config keys may not have more than one period! "%s" is not valid.', config_token)
94 return False
95
96 # Do what the user wants
97 if section and option and value:
98 # Write a configuration option
99 set_config(section, option, value)
100 if not cli.args.read_only:
101 save_config = True
102
103 elif section and option:
104 # Display a single key
105 print_config(section, option)
106
107 elif section:
108 # Display an entire section
109 for key in cli.config[section]:
110 print_config(section, key)
111
112 # Ending actions
113 if save_config:
114 cli.save_config()
115
116 return True
diff --git a/lib/python/qmk/cli/console.py b/lib/python/qmk/cli/console.py
new file mode 100644
index 000000000..45ff0c8be
--- /dev/null
+++ b/lib/python/qmk/cli/console.py
@@ -0,0 +1,302 @@
1"""Acquire debugging information from usb hid devices
2
3cli implementation of https://www.pjrc.com/teensy/hid_listen.html
4"""
5from pathlib import Path
6from threading import Thread
7from time import sleep, strftime
8
9import hid
10import usb.core
11
12from milc import cli
13
14LOG_COLOR = {
15 'next': 0,
16 'colors': [
17 '{fg_blue}',
18 '{fg_cyan}',
19 '{fg_green}',
20 '{fg_magenta}',
21 '{fg_red}',
22 '{fg_yellow}',
23 ],
24}
25
26KNOWN_BOOTLOADERS = {
27 # VID , PID
28 ('03EB', '2FEF'): 'atmel-dfu: ATmega16U2',
29 ('03EB', '2FF0'): 'atmel-dfu: ATmega32U2',
30 ('03EB', '2FF3'): 'atmel-dfu: ATmega16U4',
31 ('03EB', '2FF4'): 'atmel-dfu: ATmega32U4',
32 ('03EB', '2FF9'): 'atmel-dfu: AT90USB64',
33 ('03EB', '2FFA'): 'atmel-dfu: AT90USB162',
34 ('03EB', '2FFB'): 'atmel-dfu: AT90USB128',
35 ('03EB', '6124'): 'Microchip SAM-BA',
36 ('0483', 'DF11'): 'stm32-dfu: STM32 BOOTLOADER',
37 ('16C0', '05DC'): 'USBasp: USBaspLoader',
38 ('16C0', '05DF'): 'bootloadHID: HIDBoot',
39 ('16C0', '0478'): 'halfkay: Teensy Halfkay',
40 ('1B4F', '9203'): 'caterina: Pro Micro 3.3V',
41 ('1B4F', '9205'): 'caterina: Pro Micro 5V',
42 ('1B4F', '9207'): 'caterina: LilyPadUSB',
43 ('1C11', 'B007'): 'kiibohd: Kiibohd DFU Bootloader',
44 ('1EAF', '0003'): 'stm32duino: Maple 003',
45 ('1FFB', '0101'): 'caterina: Polou A-Star 32U4 Bootloader',
46 ('2341', '0036'): 'caterina: Arduino Leonardo',
47 ('2341', '0037'): 'caterina: Arduino Micro',
48 ('239A', '000C'): 'caterina: Adafruit Feather 32U4',
49 ('239A', '000D'): 'caterina: Adafruit ItsyBitsy 32U4 3v',
50 ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
51 ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
52 ('2A03', '0036'): 'caterina: Arduino Leonardo',
53 ('2A03', '0037'): 'caterina: Arduino Micro',
54 ('314B', '0106'): 'apm32-dfu: APM32 DFU ISP Mode'
55}
56
57
58class MonitorDevice(object):
59 def __init__(self, hid_device, numeric):
60 self.hid_device = hid_device
61 self.numeric = numeric
62 self.device = hid.Device(path=hid_device['path'])
63 self.current_line = ''
64
65 cli.log.info('Console Connected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', hid_device)
66
67 def read(self, size, encoding='ascii', timeout=1):
68 """Read size bytes from the device.
69 """
70 return self.device.read(size, timeout).decode(encoding)
71
72 def read_line(self):
73 """Read from the device's console until we get a \n.
74 """
75 while '\n' not in self.current_line:
76 self.current_line += self.read(32).replace('\x00', '')
77
78 lines = self.current_line.split('\n', 1)
79 self.current_line = lines[1]
80
81 return lines[0]
82
83 def run_forever(self):
84 while True:
85 try:
86 message = {**self.hid_device, 'text': self.read_line()}
87 identifier = (int2hex(message['vendor_id']), int2hex(message['product_id'])) if self.numeric else (message['manufacturer_string'], message['product_string'])
88 message['identifier'] = ':'.join(identifier)
89 message['ts'] = '{style_dim}{fg_green}%s{style_reset_all} ' % (strftime(cli.config.general.datetime_fmt),) if cli.args.timestamp else ''
90
91 cli.echo('%(ts)s%(color)s%(identifier)s:%(index)d{style_reset_all}: %(text)s' % message)
92
93 except hid.HIDException:
94 break
95
96
97class FindDevices(object):
98 def __init__(self, vid, pid, index, numeric):
99 self.vid = vid
100 self.pid = pid
101 self.index = index
102 self.numeric = numeric
103
104 def run_forever(self):
105 """Process messages from our queue in a loop.
106 """
107 live_devices = {}
108 live_bootloaders = {}
109
110 while True:
111 try:
112 for device in list(live_devices):
113 if not live_devices[device]['thread'].is_alive():
114 cli.log.info('Console Disconnected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', live_devices[device])
115 del live_devices[device]
116
117 for device in self.find_devices():
118 if device['path'] not in live_devices:
119 device['color'] = LOG_COLOR['colors'][LOG_COLOR['next']]
120 LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
121 live_devices[device['path']] = device
122
123 try:
124 monitor = MonitorDevice(device, self.numeric)
125 device['thread'] = Thread(target=monitor.run_forever, daemon=True)
126
127 device['thread'].start()
128 except Exception as e:
129 device['e'] = e
130 device['e_name'] = e.__class__.__name__
131 cli.log.error("Could not connect to %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s:%(vendor_id)04X:%(product_id)04X:%(index)d): %(e_name)s: %(e)s", device)
132 if cli.config.general.verbose:
133 cli.log.exception(e)
134 del live_devices[device['path']]
135
136 if cli.args.bootloaders:
137 for device in self.find_bootloaders():
138 if device.address in live_bootloaders:
139 live_bootloaders[device.address]._qmk_found = True
140 else:
141 name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))]
142 cli.log.info('Bootloader Connected: {style_bright}{fg_magenta}%s', name)
143 device._qmk_found = True
144 live_bootloaders[device.address] = device
145
146 for device in list(live_bootloaders):
147 if live_bootloaders[device]._qmk_found:
148 live_bootloaders[device]._qmk_found = False
149 else:
150 name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))]
151 cli.log.info('Bootloader Disconnected: {style_bright}{fg_magenta}%s', name)
152 del live_bootloaders[device]
153
154 sleep(.1)
155
156 except KeyboardInterrupt:
157 break
158
159 def is_bootloader(self, hid_device):
160 """Returns true if the device in question matches a known bootloader vid/pid.
161 """
162 return (int2hex(hid_device.idVendor), int2hex(hid_device.idProduct)) in KNOWN_BOOTLOADERS
163
164 def is_console_hid(self, hid_device):
165 """Returns true when the usage page indicates it's a teensy-style console.
166 """
167 return hid_device['usage_page'] == 0xFF31 and hid_device['usage'] == 0x0074
168
169 def is_filtered_device(self, hid_device):
170 """Returns True if the device should be included in the list of available consoles.
171 """
172 return int2hex(hid_device['vendor_id']) == self.vid and int2hex(hid_device['product_id']) == self.pid
173
174 def find_devices_by_report(self, hid_devices):
175 """Returns a list of available teensy-style consoles by doing a brute-force search.
176
177 Some versions of linux don't report usage and usage_page. In that case we fallback to reading the report (possibly inaccurately) ourselves.
178 """
179 devices = []
180
181 for device in hid_devices:
182 path = device['path'].decode('utf-8')
183
184 if path.startswith('/dev/hidraw'):
185 number = path[11:]
186 report = Path(f'/sys/class/hidraw/hidraw{number}/device/report_descriptor')
187
188 if report.exists():
189 rp = report.read_bytes()
190
191 if rp[1] == 0x31 and rp[3] == 0x09:
192 devices.append(device)
193
194 return devices
195
196 def find_bootloaders(self):
197 """Returns a list of available bootloader devices.
198 """
199 return list(filter(self.is_bootloader, usb.core.find(find_all=True)))
200
201 def find_devices(self):
202 """Returns a list of available teensy-style consoles.
203 """
204 hid_devices = hid.enumerate()
205 devices = list(filter(self.is_console_hid, hid_devices))
206
207 if not devices:
208 devices = self.find_devices_by_report(hid_devices)
209
210 if self.vid and self.pid:
211 devices = list(filter(self.is_filtered_device, devices))
212
213 # Add index numbers
214 device_index = {}
215 for device in devices:
216 id = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id'])))
217
218 if id not in device_index:
219 device_index[id] = 0
220
221 device_index[id] += 1
222 device['index'] = device_index[id]
223
224 return devices
225
226
227def int2hex(number):
228 """Returns a string representation of the number as hex.
229 """
230 return "%04X" % number
231
232
233def list_devices(device_finder):
234 """Show the user a nicely formatted list of devices.
235 """
236 devices = device_finder.find_devices()
237
238 if devices:
239 cli.log.info('Available devices:')
240 for dev in devices:
241 color = LOG_COLOR['colors'][LOG_COLOR['next']]
242 LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
243 cli.log.info("\t%s%s:%s:%d{style_reset_all}\t%s %s", color, int2hex(dev['vendor_id']), int2hex(dev['product_id']), dev['index'], dev['manufacturer_string'], dev['product_string'])
244
245 if cli.args.bootloaders:
246 bootloaders = device_finder.find_bootloaders()
247
248 if bootloaders:
249 cli.log.info('Available Bootloaders:')
250
251 for dev in bootloaders:
252 cli.log.info("\t%s:%s\t%s", int2hex(dev.idVendor), int2hex(dev.idProduct), KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))])
253
254
255@cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.')
256@cli.argument('-d', '--device', help='Device to select - uses format <pid>:<vid>[:<index>].')
257@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available hid_listen devices.')
258@cli.argument('-n', '--numeric', arg_only=True, action='store_true', help='Show VID/PID instead of names.')
259@cli.argument('-t', '--timestamp', arg_only=True, action='store_true', help='Print the timestamp for received messages as well.')
260@cli.argument('-w', '--wait', type=int, default=1, help="How many seconds to wait between checks (Default: 1)")
261@cli.subcommand('Acquire debugging information from usb hid devices.', hidden=False if cli.config.user.developer else True)
262def console(cli):
263 """Acquire debugging information from usb hid devices
264 """
265 vid = None
266 pid = None
267 index = 1
268
269 if cli.config.console.device:
270 device = cli.config.console.device.split(':')
271
272 if len(device) == 2:
273 vid, pid = device
274
275 elif len(device) == 3:
276 vid, pid, index = device
277
278 if not index.isdigit():
279 cli.log.error('Device index must be a number! Got "%s" instead.', index)
280 exit(1)
281
282 index = int(index)
283
284 if index < 1:
285 cli.log.error('Device index must be greater than 0! Got %s', index)
286 exit(1)
287
288 else:
289 cli.log.error('Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "%s".', cli.config.console.device)
290 cli.print_help()
291 exit(1)
292
293 vid = vid.upper()
294 pid = pid.upper()
295
296 device_finder = FindDevices(vid, pid, index, cli.args.numeric)
297
298 if cli.args.list:
299 return list_devices(device_finder)
300
301 print('Looking for devices...', flush=True)
302 device_finder.run_forever()
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index ee049e8af..3a35c1103 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -201,6 +201,7 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
201 f'VERBOSE={verbose}', 201 f'VERBOSE={verbose}',
202 f'COLOR={color}', 202 f'COLOR={color}',
203 'SILENT=false', 203 'SILENT=false',
204 f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
204 ]) 205 ])
205 206
206 return make_command 207 return make_command
diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py
index 3ed69f3bf..49e5e0eb4 100644
--- a/lib/python/qmk/constants.py
+++ b/lib/python/qmk/constants.py
@@ -10,7 +10,7 @@ QMK_FIRMWARE = Path.cwd()
10MAX_KEYBOARD_SUBFOLDERS = 5 10MAX_KEYBOARD_SUBFOLDERS = 5
11 11
12# Supported processor types 12# Supported processor types
13CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32G431', 'STM32G474' 13CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66F18', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L433', 'STM32L443'
14LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None 14LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None
15VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' 15VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'
16 16
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
index d13e42d40..afdbc8142 100644
--- a/lib/python/qmk/tests/test_cli_commands.py
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -7,7 +7,7 @@ is_windows = 'windows' in platform.platform().lower()
7 7
8 8
9def check_subcommand(command, *args): 9def check_subcommand(command, *args):
10 cmd = ['bin/qmk', command, *args] 10 cmd = ['qmk', command, *args]
11 result = cli.run(cmd, stdin=DEVNULL, combined_output=True) 11 result = cli.run(cmd, stdin=DEVNULL, combined_output=True)
12 return result 12 return result
13 13
@@ -16,7 +16,7 @@ def check_subcommand_stdin(file_to_read, command, *args):
16 """Pipe content of a file to a command and return output. 16 """Pipe content of a file to a command and return output.
17 """ 17 """
18 with open(file_to_read, encoding='utf-8') as my_file: 18 with open(file_to_read, encoding='utf-8') as my_file:
19 cmd = ['bin/qmk', command, *args] 19 cmd = ['qmk', command, *args]
20 result = cli.run(cmd, stdin=my_file, combined_output=True) 20 result = cli.run(cmd, stdin=my_file, combined_output=True)
21 return result 21 return result
22 22
@@ -61,12 +61,6 @@ def test_flash_bootloaders():
61 check_returncode(result, [1]) 61 check_returncode(result, [1])
62 62
63 63
64def test_config():
65 result = check_subcommand('config')
66 check_returncode(result)
67 assert 'general.color' in result.stdout
68
69
70def test_kle2json(): 64def test_kle2json():
71 result = check_subcommand('kle2json', 'lib/python/qmk/tests/kle.txt', '-f') 65 result = check_subcommand('kle2json', 'lib/python/qmk/tests/kle.txt', '-f')
72 check_returncode(result) 66 check_returncode(result)