Browse Source

Move the module checking and updating to lib/python (#12416)

* move the module checking and updating to lib/python

* make flake8 happy

* Update lib/python/qmk/cli/__init__.py

Co-authored-by: Erovia <Erovia@users.noreply.github.com>

* prompt the user to disable developer mode

* pyformat

* flake8

Co-authored-by: Erovia <Erovia@users.noreply.github.com>
Zach White 4 năm trước cách đây
mục cha
commit
bc38c38f8c
2 tập tin đã thay đổi với 121 bổ sung68 xóa
  1. 0 44
      bin/qmk
  2. 121 24
      lib/python/qmk/cli/__init__.py

+ 0 - 44
bin/qmk

@@ -3,7 +3,6 @@
 """
 import os
 import sys
-from importlib.util import find_spec
 from pathlib import Path
 
 # Add the QMK python libs to our path
@@ -12,52 +11,9 @@ qmk_dir = script_dir.parent
 python_lib_dir = Path(qmk_dir / 'lib' / 'python').resolve()
 sys.path.append(str(python_lib_dir))
 
-
-def _check_modules(requirements):
-    """ Check if the modules in the given requirements.txt are available.
-    """
-    with Path(qmk_dir / requirements).open() as fd:
-        for line in fd.readlines():
-            line = line.strip().replace('<', '=').replace('>', '=')
-
-            if len(line) == 0 or line[0] == '#' or line.startswith('-r'):
-                continue
-
-            if '#' in line:
-                line = line.split('#')[0]
-
-            module = dict()
-            module['name'] = line.split('=')[0] if '=' in line else line
-            module['import'] = module['name'].replace('-', '_')
-
-            # Not every module is importable by its own name.
-            if module['name'] == "pep8-naming":
-                module['import'] = "pep8ext_naming"
-
-            if not find_spec(module['import']):
-                print('Could not find module %s!' % module['name'])
-                print('Please run `python3 -m pip install -r %s` to install required python dependencies.' % (qmk_dir / requirements,))
-                if developer:
-                    print('You can also turn off developer mode: qmk config user.developer=None')
-                print()
-                exit(255)
-
-
-developer = False
-# Make sure our modules have been setup
-_check_modules('requirements.txt')
-
 # Setup the CLI
 import milc  # noqa
 
-# For developers additional modules are needed
-if milc.cli.config.user.developer:
-    # Do not run the check for 'config',
-    # so users can turn off developer mode
-    if len(sys.argv) == 1 or (len(sys.argv) > 1 and 'config' != sys.argv[1]):
-        developer = True
-        _check_modules('requirements-dev.txt')
-
 milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
 
 

+ 121 - 24
lib/python/qmk/cli/__init__.py

@@ -2,33 +2,79 @@
 
 We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
 """
+import os
+import shlex
 import sys
+from importlib.util import find_spec
+from pathlib import Path
+from subprocess import run
 
 from milc import cli, __VERSION__
+from milc.questions import yesno
 
-from . import c2json
-from . import cformat
-from . import chibios
-from . import clean
-from . import compile
-from . import config
-from . import docs
-from . import doctor
-from . import fileformat
-from . import flash
-from . import format
-from . import generate
-from . import hello
-from . import info
-from . import json2c
-from . import lint
-from . import list
-from . import kle2json
-from . import multibuild
-from . import new
-from . import pyformat
-from . import pytest
 
+def _run_cmd(*command):
+    """Run a command in a subshell.
+    """
+    if 'windows' in cli.platform.lower():
+        safecmd = map(shlex.quote, command)
+        safecmd = ' '.join(safecmd)
+        command = [os.environ['SHELL'], '-c', safecmd]
+
+    return run(command)
+
+
+def _find_broken_requirements(requirements):
+    """ Check if the modules in the given requirements.txt are available.
+
+    Args:
+
+        requirements
+            The path to a requirements.txt file
+
+    Returns a list of modules that couldn't be imported
+    """
+    with Path(requirements).open() as fd:
+        broken_modules = []
+
+        for line in fd.readlines():
+            line = line.strip().replace('<', '=').replace('>', '=')
+
+            if len(line) == 0 or line[0] == '#' or line.startswith('-r'):
+                continue
+
+            if '#' in line:
+                line = line.split('#')[0]
+
+            module_name = line.split('=')[0] if '=' in line else line
+            module_import = module_name.replace('-', '_')
+
+            # Not every module is importable by its own name.
+            if module_name == "pep8-naming":
+                module_import = "pep8ext_naming"
+
+            if not find_spec(module_import):
+                broken_modules.append(module_name)
+
+        return broken_modules
+
+
+def _broken_module_imports(requirements):
+    """Make sure we can import all the python modules.
+    """
+    broken_modules = _find_broken_requirements(requirements)
+
+    for module in broken_modules:
+        print('Could not find module %s!' % module)
+
+    if broken_modules:
+        return True
+
+    return False
+
+
+# Make sure our python is new enough
+#
 # Supported version information
 #
 # 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:
 milc_version = __VERSION__.split('.')
 
 if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
-    from pathlib import Path
-
     requirements = Path('requirements.txt').resolve()
 
     print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
     exit(127)
+
+# Check to make sure we have all our dependencies
+msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
+
+if _broken_module_imports('requirements.txt'):
+    if yesno('Would you like to install the required Python modules?'):
+        _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
+    else:
+        print()
+        print(msg_install % (str(Path('requirements.txt').resolve()),))
+        print()
+        exit(1)
+
+if cli.config.user.developer:
+    args = sys.argv[1:]
+    while args and args[0][0] == '-':
+        del args[0]
+    if not args or args[0] != 'config':
+        if _broken_module_imports('requirements-dev.txt'):
+            if yesno('Would you like to install the required developer Python modules?'):
+                _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
+            elif yesno('Would you like to disable developer mode?'):
+                _run_cmd(sys.argv[0], 'config', 'user.developer=None')
+            else:
+                print()
+                print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
+                print('You can also turn off developer mode: qmk config user.developer=None')
+                print()
+                exit(1)
+
+# Import our subcommands
+from . import c2json  # noqa
+from . import cformat  # noqa
+from . import chibios  # noqa
+from . import clean  # noqa
+from . import compile  # noqa
+from . import config  # noqa
+from . import docs  # noqa
+from . import doctor  # noqa
+from . import fileformat  # noqa
+from . import flash  # noqa
+from . import format  # noqa
+from . import generate  # noqa
+from . import hello  # noqa
+from . import info  # noqa
+from . import json2c  # noqa
+from . import lint  # noqa
+from . import list  # noqa
+from . import kle2json  # noqa
+from . import multibuild  # noqa
+from . import new  # noqa
+from . import pyformat  # noqa
+from . import pytest  # noqa