esp-idf/tools/check_python_dependencies.py
Roland Dobai b28d7e6850 Tools: Improve the Python package system
Introduce features into the Python package management system & manage
package versions outside of ESP-IDF repo.
2022-01-17 16:54:36 +01:00

96 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import re
import sys
PYTHON_PACKAGE_RE = re.compile(r'[^<>=~]+')
try:
import pkg_resources
except ImportError:
print('pkg_resources cannot be imported probably because the pip package is not installed and/or using a '
'legacy Python interpreter. Please refer to the Get Started section of the ESP-IDF Programming Guide for '
'setting up the required packages.')
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='ESP-IDF Python package dependency checker')
parser.add_argument('--requirements', '-r',
help='Path to a requirements file (can be used multiple times)',
action='append', default=[])
parser.add_argument('--constraints', '-c', default=[],
help='Path to a constraints file (can be used multiple times)',
action='append')
args = parser.parse_args()
required_set = set()
for req_path in args.requirements:
with open(req_path) as f:
required_set |= set(i for i in map(str.strip, f.readlines()) if len(i) > 0 and not i.startswith('#'))
constr_dict = {} # for example package_name -> package_name==1.0
for const_path in args.constraints:
with open(const_path) as f:
for con in [i for i in map(str.strip, f.readlines()) if len(i) > 0 and not i.startswith('#')]:
if con.startswith('file://'):
con = os.path.basename(con)
elif con.startswith('--only-binary'):
continue
elif con.startswith('-e') and '#egg=' in con: # version control URLs, take the egg= part at the end only
con_m = re.search(r'#egg=([^\s]+)', con)
if not con_m:
print('Malformed input. Cannot find name in {}'.format(con))
sys.exit(1)
con = con_m[1]
name_m = PYTHON_PACKAGE_RE.search(con)
if not name_m:
print('Malformed input. Cannot find name in {}'.format(con))
sys.exit(1)
constr_dict[name_m[0]] = con
# We need to constrain package dependencies as well. So all installed packages need to be checked.
# For example package A requires package B. We have only A in our requirements. But the newest version of B could
# broke at some time and in that case we add a constraint for B (on the server) but don't have to update the
# requirement file (in the ESP-IDF repo).
required_set |= set(i.key for i in pkg_resources.working_set)
not_satisfied = []
for requirement in required_set:
# If there is a version-specific constraint for the requirement then use it. Otherwise, just use the
# requirement as is.
to_require = constr_dict.get(requirement, requirement)
try:
pkg_resources.require(to_require)
except pkg_resources.ResolutionError:
not_satisfied.append(to_require)
if len(not_satisfied) > 0:
print('The following Python requirements are not satisfied:')
print(os.linesep.join(not_satisfied))
if 'IDF_PYTHON_ENV_PATH' in os.environ:
# We are running inside a private virtual environment under IDF_TOOLS_PATH,
# ask the user to run install.bat again.
install_script = 'install.bat' if sys.platform == 'win32' else 'install.sh'
print('To install the missing packages, please run "{}"'.format(install_script))
else:
print('Please follow the instructions found in the "Set up the tools" section of '
'ESP-IDF Getting Started Guide.')
print('Diagnostic information:')
idf_python_env_path = os.environ.get('IDF_PYTHON_ENV_PATH')
print(' IDF_PYTHON_ENV_PATH: {}'.format(idf_python_env_path or '(not set)'))
print(' Python interpreter used: {}'.format(sys.executable))
if not idf_python_env_path or idf_python_env_path not in sys.executable:
print(' Warning: python interpreter not running from IDF_PYTHON_ENV_PATH')
print(' PATH: {}'.format(os.getenv('PATH')))
sys.exit(1)
print('Python requirements are satisfied.')