~ruther/qmk_firmware

bc38c38f8c25dcbe759bc4d9d707a0069b3c6c59 — Zach White 4 years ago a3e7f3e
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>
2 files changed, 121 insertions(+), 68 deletions(-)

M bin/qmk
M lib/python/qmk/cli/__init__.py
M bin/qmk => bin/qmk +0 -44
@@ 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}'



M lib/python/qmk/cli/__init__.py => lib/python/qmk/cli/__init__.py +121 -24
@@ 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