check_python_dependencies.py 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env python
  2. #
  3. # SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. import argparse
  6. import os
  7. import re
  8. import sys
  9. PYTHON_PACKAGE_RE = re.compile(r'[^<>=~]+')
  10. try:
  11. import pkg_resources
  12. except ImportError:
  13. print('pkg_resources cannot be imported probably because the pip package is not installed and/or using a '
  14. 'legacy Python interpreter. Please refer to the Get Started section of the ESP-IDF Programming Guide for '
  15. 'setting up the required packages.')
  16. sys.exit(1)
  17. if __name__ == '__main__':
  18. parser = argparse.ArgumentParser(description='ESP-IDF Python package dependency checker')
  19. parser.add_argument('--requirements', '-r',
  20. help='Path to a requirements file (can be used multiple times)',
  21. action='append', default=[])
  22. parser.add_argument('--constraints', '-c', default=[],
  23. help='Path to a constraints file (can be used multiple times)',
  24. action='append')
  25. args = parser.parse_args()
  26. required_set = set()
  27. for req_path in args.requirements:
  28. with open(req_path) as f:
  29. required_set |= set(i for i in map(str.strip, f.readlines()) if len(i) > 0 and not i.startswith('#'))
  30. constr_dict = {} # for example package_name -> package_name==1.0
  31. for const_path in args.constraints:
  32. with open(const_path) as f:
  33. for con in [i for i in map(str.strip, f.readlines()) if len(i) > 0 and not i.startswith('#')]:
  34. if con.startswith('file://'):
  35. con = os.path.basename(con)
  36. elif con.startswith('--only-binary'):
  37. continue
  38. elif con.startswith('-e') and '#egg=' in con: # version control URLs, take the egg= part at the end only
  39. con_m = re.search(r'#egg=([^\s]+)', con)
  40. if not con_m:
  41. print('Malformed input. Cannot find name in {}'.format(con))
  42. sys.exit(1)
  43. con = con_m[1]
  44. name_m = PYTHON_PACKAGE_RE.search(con)
  45. if not name_m:
  46. print('Malformed input. Cannot find name in {}'.format(con))
  47. sys.exit(1)
  48. constr_dict[name_m[0]] = con
  49. # We need to constrain package dependencies as well. So all installed packages need to be checked.
  50. # For example package A requires package B. We have only A in our requirements. But the newest version of B could
  51. # broke at some time and in that case we add a constraint for B (on the server) but don't have to update the
  52. # requirement file (in the ESP-IDF repo).
  53. required_set |= set(i.key for i in pkg_resources.working_set)
  54. not_satisfied = []
  55. for requirement in required_set:
  56. # If there is a version-specific constraint for the requirement then use it. Otherwise, just use the
  57. # requirement as is.
  58. to_require = constr_dict.get(requirement, requirement)
  59. try:
  60. pkg_resources.require(to_require)
  61. except pkg_resources.ResolutionError:
  62. not_satisfied.append(to_require)
  63. if len(not_satisfied) > 0:
  64. print('The following Python requirements are not satisfied:')
  65. print(os.linesep.join(not_satisfied))
  66. if 'IDF_PYTHON_ENV_PATH' in os.environ:
  67. # We are running inside a private virtual environment under IDF_TOOLS_PATH,
  68. # ask the user to run install.bat again.
  69. install_script = 'install.bat' if sys.platform == 'win32' else 'install.sh'
  70. print('To install the missing packages, please run "{}"'.format(install_script))
  71. else:
  72. print('Please follow the instructions found in the "Set up the tools" section of '
  73. 'ESP-IDF Getting Started Guide.')
  74. print('Diagnostic information:')
  75. idf_python_env_path = os.environ.get('IDF_PYTHON_ENV_PATH')
  76. print(' IDF_PYTHON_ENV_PATH: {}'.format(idf_python_env_path or '(not set)'))
  77. print(' Python interpreter used: {}'.format(sys.executable))
  78. if not idf_python_env_path or idf_python_env_path not in sys.executable:
  79. print(' Warning: python interpreter not running from IDF_PYTHON_ENV_PATH')
  80. print(' PATH: {}'.format(os.getenv('PATH')))
  81. sys.exit(1)
  82. print('Python requirements are satisfied.')