From 5342ab3e1012df99041e93644065d1110273b32a Mon Sep 17 00:00:00 2001 From: skullY Date: Sat, 10 Oct 2020 11:20:42 -0700 Subject: [PATCH] check if requirements are installed before importing qmk_firmware --- qmk_cli/helpers.py | 95 ++++++++++++++++++++++++++++----------- qmk_cli/script_qmk.py | 102 ++++++++++++++++++------------------------ 2 files changed, 112 insertions(+), 85 deletions(-) diff --git a/qmk_cli/helpers.py b/qmk_cli/helpers.py index 2a6e155..fce1750 100644 --- a/qmk_cli/helpers.py +++ b/qmk_cli/helpers.py @@ -1,36 +1,79 @@ """Useful helper functions. """ -from milc import cli, format_ansi +import os +from functools import lru_cache +from importlib.util import find_spec +from pathlib import Path + +from milc import cli -def question(question, boolean=True, default=''): - """Asks the user to answer a question. +def find_broken_requirements(requirements): + """ Check if the modules in the given requirements.txt are available. - This keeps re-asking until it gets acceptible input. + Args: + + requirements + The path to a requirements.txt file + + Returns a list of modules that couldn't be imported """ - if cli.args.yes: - return True + with Path(requirements).open() as fd: + broken_modules = [] - if default and default.lower() == 'y': - answer_key = 'Y/n' - elif default and default.lower() == 'n': - answer_key = 'y/N' - else: - answer_key = 'y/n' + for line in fd.readlines(): + line = line.strip().replace('<', '=').replace('>', '=') - prompt = format_ansi('\n*** %s [%s] ' % (question, answer_key)) + if len(line) == 0 or line[0] == '#' or line.startswith('-r'): + continue - while True: - answer = input(prompt) - print() - if answer == '' and default.lower() == 'y': - answer = 'y' - elif answer == '' and default.lower() == 'n': - answer = 'n' + if '#' in line: + line = line.split('#')[0] - if answer.lower() in ['y', 'yes']: - return True - elif answer.lower() in ['n', 'no']: - return False - else: - cli.echo('Invalid answer!') + module_name = module_import_name = line.split('=')[0] if '=' in line else line + + # Not every module is importable by its own name. + if module_name == "pep8-naming": + module_import_name = "pep8ext_naming" + + if not find_spec(module_import_name): + broken_modules.append(module_name) + + return broken_modules + + +@lru_cache(maxsize=2) +def find_qmk_firmware(): + """Look for qmk_firmware in the usual places. + + This function returns the path to qmk_firmware, or the default location if one does not exist. + """ + if in_qmk_firmware(): + return in_qmk_firmware() + + if cli.config.user.qmk_home: + return Path(cli.config.user.qmk_home).expanduser().resolve() + + if 'QMK_HOME' in os.environ: + path = Path(os.environ['QMK_HOME']).expanduser() + if path.exists(): + return path.resolve() + return path + + return Path.home() / 'qmk_firmware' + + + +def in_qmk_firmware(): + """Returns the path to the qmk_firmware we are currently in, or None if we are not inside qmk_firmware. + """ + cur_dir = Path.cwd() + while len(cur_dir.parents) > 0: + found_lib = cur_dir / 'lib/python/qmk/cli/__init__.py' + found_quantum = cur_dir / 'quantum' + if found_lib.is_file() and found_quantum.is_dir(): + return cur_dir + + # Move up a directory before the next iteration + cur_dir = cur_dir / '..' + cur_dir = cur_dir.resolve() diff --git a/qmk_cli/script_qmk.py b/qmk_cli/script_qmk.py index 9337da4..e60fdc0 100644 --- a/qmk_cli/script_qmk.py +++ b/qmk_cli/script_qmk.py @@ -3,15 +3,16 @@ This program can be run from anywhere, with or without a qmk_firmware repository. If a qmk_firmware repository can be located we will use that to augment our available subcommands. """ -import argparse -import platform import os import subprocess import sys -from functools import lru_cache -from pathlib import Path +from platform import platform +from traceback import print_exc import milc +import milc.subcommand.config # noqa + +from .helpers import find_broken_requirements, find_qmk_firmware, in_qmk_firmware milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}' @@ -23,50 +24,7 @@ def qmk_main(cli): cli.print_help() -@lru_cache(maxsize=2) -def in_qmk_firmware(): - """Returns the path to the qmk_firmware we are currently in, or None if we are not inside qmk_firmware. - """ - cur_dir = Path.cwd() - while len(cur_dir.parents) > 0: - found_bin = cur_dir / 'bin' / 'qmk' - # An additional check for something that exists in the root of qmk_firmware, - # but not in the script's install directory, to avoid recursive execution, - # if started from install directory. - # e.g.: cd ~/.local/bin && ./qmk - found_quantum = cur_dir / 'quantum' - if found_bin.is_file() and found_quantum.is_dir(): - command = [sys.executable, found_bin.as_posix()] - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - if result.returncode == 0: - return cur_dir - - # Move up a directory before the next iteration - cur_dir = cur_dir / '..' - cur_dir = cur_dir.resolve() - - -def find_qmk_firmware(): - """Look for qmk_firmware in the usual places. - - This function returns the path to qmk_firmware, or the default location if one does not exist. - """ - if in_qmk_firmware(): - return in_qmk_firmware() - - if milc.cli.config.user.qmk_home: - return Path(milc.cli.config.user.qmk_home).expanduser().resolve() - - if 'QMK_HOME' in os.environ: - path = Path(os.environ['QMK_HOME']).expanduser() - if path.exists(): - return path.resolve() - return path - - return Path.home() / 'qmk_firmware' - - +# Python setuptools entrypoint def main(): """Setup the environment before dispatching to the entrypoint. """ @@ -75,7 +33,7 @@ def main(): print('Warning: Your Python version is out of date! Some subcommands may not work!') print('Please upgrade to Python 3.6 or later.') - if 'windows' in platform.platform().lower(): + if 'windows' in platform().lower(): msystem = os.environ.get('MSYSTEM', '') if 'mingw64' not in sys.executable or 'MINGW64' not in msystem: @@ -91,21 +49,47 @@ def main(): os.environ['QMK_HOME'] = str(qmk_firmware) os.environ['ORIG_CWD'] = os.getcwd() - # Import the subcommand modules import qmk_cli.subcommands + # Check out and initialize the qmk_firmware environment if qmk_firmware.exists(): + # All subcommands are run relative to the qmk_firmware root to make it easier to use the right copy of qmk_firmware. os.chdir(str(qmk_firmware)) - sys.path.append(str(qmk_firmware / 'lib' / 'python')) - try: - import qmk.cli - except ImportError: - if qmk_firmware.name != 'qmk_firmware': - print('Warning: %s does not end in "qmk_firmware". Do you need to set QMK_HOME to "%s/qmk_firmware"?' % (qmk_firmware,qmk_firmware)) - print('Error: %s is too old or not set up correctly!' % qmk_firmware) - print('Please update it or remove it completely before continuing.') - sys.exit(1) + # Check to make sure we have all the requirements + broken_modules = find_broken_requirements('requirements.txt') + broken_dev_modules = find_broken_requirements('requirements-dev.txt') if milc.cli.config.user.developer else [] + + if broken_modules or broken_dev_modules: + for module in broken_modules + broken_dev_modules: + print('Could not find module %s!' % module) + + msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.' + if broken_modules: + print() + print(msg_install % (qmk_firmware / 'requirements.txt',)) + + if broken_dev_modules: + print() + print(msg_install % (qmk_firmware / 'requirements-dev.txt',)) + print('You can also turn off developer mode: qmk config user.developer=None') + + print() + + else: + # Environment looks good, include the qmk_firmware subcommands + sys.path.append(str(qmk_firmware / 'lib/python')) + + try: + import qmk.cli + + except ImportError as e: + if qmk_firmware.name != 'qmk_firmware': + print('Warning: %s does not end in "qmk_firmware". Do you need to set QMK_HOME to "%s/qmk_firmware"?' % (qmk_firmware, qmk_firmware)) + + print('Error: %s: %s', (e.__class__.__name__, e)) + print_exc() + sys.exit(1) # Call the entrypoint return_code = milc.cli()