__init__.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. """QMK CLI Subcommands
  2. We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
  3. """
  4. import os
  5. import shlex
  6. import sys
  7. from importlib.util import find_spec
  8. from pathlib import Path
  9. from subprocess import run
  10. from milc import cli, __VERSION__
  11. from milc.questions import yesno
  12. def _run_cmd(*command):
  13. """Run a command in a subshell.
  14. """
  15. if 'windows' in cli.platform.lower():
  16. safecmd = map(shlex.quote, command)
  17. safecmd = ' '.join(safecmd)
  18. command = [os.environ['SHELL'], '-c', safecmd]
  19. return run(command)
  20. def _find_broken_requirements(requirements):
  21. """ Check if the modules in the given requirements.txt are available.
  22. Args:
  23. requirements
  24. The path to a requirements.txt file
  25. Returns a list of modules that couldn't be imported
  26. """
  27. with Path(requirements).open() as fd:
  28. broken_modules = []
  29. for line in fd.readlines():
  30. line = line.strip().replace('<', '=').replace('>', '=')
  31. if len(line) == 0 or line[0] == '#' or line.startswith('-r'):
  32. continue
  33. if '#' in line:
  34. line = line.split('#')[0]
  35. module_name = line.split('=')[0] if '=' in line else line
  36. module_import = module_name.replace('-', '_')
  37. # Not every module is importable by its own name.
  38. if module_name == "pep8-naming":
  39. module_import = "pep8ext_naming"
  40. elif module_name == 'pyusb':
  41. module_import = 'usb.core'
  42. if not find_spec(module_import):
  43. broken_modules.append(module_name)
  44. return broken_modules
  45. def _broken_module_imports(requirements):
  46. """Make sure we can import all the python modules.
  47. """
  48. broken_modules = _find_broken_requirements(requirements)
  49. for module in broken_modules:
  50. print('Could not find module %s!' % module)
  51. if broken_modules:
  52. return True
  53. return False
  54. # Make sure our python is new enough
  55. #
  56. # Supported version information
  57. #
  58. # Based on the OSes we support these are the minimum python version available by default.
  59. # Last update: 2021 Jan 02
  60. #
  61. # Arch: 3.9
  62. # Debian: 3.7
  63. # Fedora 31: 3.7
  64. # Fedora 32: 3.8
  65. # Fedora 33: 3.9
  66. # FreeBSD: 3.7
  67. # Gentoo: 3.7
  68. # macOS: 3.9 (from homebrew)
  69. # msys2: 3.8
  70. # Slackware: 3.7
  71. # solus: 3.7
  72. # void: 3.9
  73. if sys.version_info[0] != 3 or sys.version_info[1] < 7:
  74. print('Error: Your Python is too old! Please upgrade to Python 3.7 or later.')
  75. exit(127)
  76. milc_version = __VERSION__.split('.')
  77. if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
  78. requirements = Path('requirements.txt').resolve()
  79. print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
  80. exit(127)
  81. # Check to make sure we have all our dependencies
  82. msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
  83. if _broken_module_imports('requirements.txt'):
  84. if yesno('Would you like to install the required Python modules?'):
  85. _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
  86. else:
  87. print()
  88. print(msg_install % (str(Path('requirements.txt').resolve()),))
  89. print()
  90. exit(1)
  91. if cli.config.user.developer:
  92. args = sys.argv[1:]
  93. while args and args[0][0] == '-':
  94. del args[0]
  95. if not args or args[0] != 'config':
  96. if _broken_module_imports('requirements-dev.txt'):
  97. if yesno('Would you like to install the required developer Python modules?'):
  98. _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
  99. elif yesno('Would you like to disable developer mode?'):
  100. _run_cmd(sys.argv[0], 'config', 'user.developer=None')
  101. else:
  102. print()
  103. print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
  104. print('You can also turn off developer mode: qmk config user.developer=None')
  105. print()
  106. exit(1)
  107. # Import our subcommands
  108. from . import c2json # noqa
  109. from . import cformat # noqa
  110. from . import chibios # noqa
  111. from . import clean # noqa
  112. from . import compile # noqa
  113. from . import config # noqa
  114. from . import console # noqa
  115. from . import docs # noqa
  116. from . import doctor # noqa
  117. from . import fileformat # noqa
  118. from . import flash # noqa
  119. from . import format # noqa
  120. from . import generate # noqa
  121. from . import hello # noqa
  122. from . import info # noqa
  123. from . import json2c # noqa
  124. from . import lint # noqa
  125. from . import list # noqa
  126. from . import kle2json # noqa
  127. from . import multibuild # noqa
  128. from . import new # noqa
  129. from . import pyformat # noqa
  130. from . import pytest # noqa