aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/qmk44
-rw-r--r--lib/python/qmk/cli/__init__.py145
2 files changed, 121 insertions, 68 deletions
diff --git a/bin/qmk b/bin/qmk
index a3c1be328..47b50f83b 100755
--- a/bin/qmk
+++ b/bin/qmk
@@ -3,7 +3,6 @@
3""" 3"""
4import os 4import os
5import sys 5import sys
6from importlib.util import find_spec
7from pathlib import Path 6from pathlib import Path
8 7
9# Add the QMK python libs to our path 8# Add the QMK python libs to our path
@@ -12,52 +11,9 @@ qmk_dir = script_dir.parent
12python_lib_dir = Path(qmk_dir / 'lib' / 'python').resolve() 11python_lib_dir = Path(qmk_dir / 'lib' / 'python').resolve()
13sys.path.append(str(python_lib_dir)) 12sys.path.append(str(python_lib_dir))
14 13
15
16def _check_modules(requirements):
17 """ Check if the modules in the given requirements.txt are available.
18 """
19 with Path(qmk_dir / requirements).open() as fd:
20 for line in fd.readlines():
21 line = line.strip().replace('<', '=').replace('>', '=')
22
23 if len(line) == 0 or line[0] == '#' or line.startswith('-r'):
24 continue
25
26 if '#' in line:
27 line = line.split('#')[0]
28
29 module = dict()
30 module['name'] = line.split('=')[0] if '=' in line else line
31 module['import'] = module['name'].replace('-', '_')
32
33 # Not every module is importable by its own name.
34 if module['name'] == "pep8-naming":
35 module['import'] = "pep8ext_naming"
36
37 if not find_spec(module['import']):
38 print('Could not find module %s!' % module['name'])
39 print('Please run `python3 -m pip install -r %s` to install required python dependencies.' % (qmk_dir / requirements,))
40 if developer:
41 print('You can also turn off developer mode: qmk config user.developer=None')
42 print()
43 exit(255)
44
45
46developer = False
47# Make sure our modules have been setup
48_check_modules('requirements.txt')
49
50# Setup the CLI 14# Setup the CLI
51import milc # noqa 15import milc # noqa
52 16
53# For developers additional modules are needed
54if milc.cli.config.user.developer:
55 # Do not run the check for 'config',
56 # so users can turn off developer mode
57 if len(sys.argv) == 1 or (len(sys.argv) > 1 and 'config' != sys.argv[1]):
58 developer = True
59 _check_modules('requirements-dev.txt')
60
61milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}' 17milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
62 18
63 19
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index 008e57f76..6fe769fe7 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -2,33 +2,79 @@
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 os
6import shlex
5import sys 7import sys
8from importlib.util import find_spec
9from pathlib import Path
10from subprocess import run
6 11
7from milc import cli, __VERSION__ 12from milc import cli, __VERSION__
13from milc.questions import yesno
8 14
9from . import c2json
10from . import cformat
11from . import chibios
12from . import clean
13from . import compile
14from . import config
15from . import docs
16from . import doctor
17from . import fileformat
18from . import flash
19from . import format
20from . import generate
21from . import hello
22from . import info
23from . import json2c
24from . import lint
25from . import list
26from . import kle2json
27from . import multibuild
28from . import new
29from . import pyformat
30from . import pytest
31 15
16def _run_cmd(*command):
17 """Run a command in a subshell.
18 """
19 if 'windows' in cli.platform.lower():
20 safecmd = map(shlex.quote, command)
21 safecmd = ' '.join(safecmd)
22 command = [os.environ['SHELL'], '-c', safecmd]
23
24 return run(command)
25
26
27def _find_broken_requirements(requirements):
28 """ Check if the modules in the given requirements.txt are available.
29
30 Args:
31
32 requirements
33 The path to a requirements.txt file
34
35 Returns a list of modules that couldn't be imported
36 """
37 with Path(requirements).open() as fd:
38 broken_modules = []
39
40 for line in fd.readlines():
41 line = line.strip().replace('<', '=').replace('>', '=')
42
43 if len(line) == 0 or line[0] == '#' or line.startswith('-r'):
44 continue
45
46 if '#' in line:
47 line = line.split('#')[0]
48
49 module_name = line.split('=')[0] if '=' in line else line
50 module_import = module_name.replace('-', '_')
51
52 # Not every module is importable by its own name.
53 if module_name == "pep8-naming":
54 module_import = "pep8ext_naming"
55
56 if not find_spec(module_import):
57 broken_modules.append(module_name)
58
59 return broken_modules
60
61
62def _broken_module_imports(requirements):
63 """Make sure we can import all the python modules.
64 """
65 broken_modules = _find_broken_requirements(requirements)
66
67 for module in broken_modules:
68 print('Could not find module %s!' % module)
69
70 if broken_modules:
71 return True
72
73 return False
74
75
76# Make sure our python is new enough
77#
32# Supported version information 78# Supported version information
33# 79#
34# Based on the OSes we support these are the minimum python version available by default. 80# Based on the OSes we support these are the minimum python version available by default.
@@ -54,9 +100,60 @@ if sys.version_info[0] != 3 or sys.version_info[1] < 7:
54milc_version = __VERSION__.split('.') 100milc_version = __VERSION__.split('.')
55 101
56if int(milc_version[0]) < 2 and int(milc_version[1]) < 3: 102if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
57 from pathlib import Path
58
59 requirements = Path('requirements.txt').resolve() 103 requirements = Path('requirements.txt').resolve()
60 104
61 print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}') 105 print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
62 exit(127) 106 exit(127)
107
108# Check to make sure we have all our dependencies
109msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
110
111if _broken_module_imports('requirements.txt'):
112 if yesno('Would you like to install the required Python modules?'):
113 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
114 else:
115 print()
116 print(msg_install % (str(Path('requirements.txt').resolve()),))
117 print()
118 exit(1)
119
120if cli.config.user.developer:
121 args = sys.argv[1:]
122 while args and args[0][0] == '-':
123 del args[0]
124 if not args or args[0] != 'config':
125 if _broken_module_imports('requirements-dev.txt'):
126 if yesno('Would you like to install the required developer Python modules?'):
127 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
128 elif yesno('Would you like to disable developer mode?'):
129 _run_cmd(sys.argv[0], 'config', 'user.developer=None')
130 else:
131 print()
132 print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
133 print('You can also turn off developer mode: qmk config user.developer=None')
134 print()
135 exit(1)
136
137# Import our subcommands
138from . import c2json # noqa
139from . import cformat # noqa
140from . import chibios # noqa
141from . import clean # noqa
142from . import compile # noqa
143from . import config # noqa
144from . import docs # noqa
145from . import doctor # noqa
146from . import fileformat # noqa
147from . import flash # noqa
148from . import format # noqa
149from . import generate # noqa
150from . import hello # noqa
151from . import info # noqa
152from . import json2c # noqa
153from . import lint # noqa
154from . import list # noqa
155from . import kle2json # noqa
156from . import multibuild # noqa
157from . import new # noqa
158from . import pyformat # noqa
159from . import pytest # noqa