Merge branch 'fix/idf_tools_diff_python_dir' into 'master'

fix(tools): Add additional Python environment checks

Closes IDFGH-12140

See merge request espressif/esp-idf!29162
This commit is contained in:
Roland Dobai 2024-03-01 19:20:44 +08:00
commit dc987dab7f

View File

@ -95,6 +95,9 @@ IDF_TOOLS_INSTALL_CMD = os.environ.get('IDF_TOOLS_INSTALL_CMD')
IDF_TOOLS_EXPORT_CMD = os.environ.get('IDF_TOOLS_INSTALL_CMD') IDF_TOOLS_EXPORT_CMD = os.environ.get('IDF_TOOLS_INSTALL_CMD')
IDF_DL_URL = 'https://dl.espressif.com/dl/esp-idf' IDF_DL_URL = 'https://dl.espressif.com/dl/esp-idf'
IDF_PIP_WHEELS_URL = os.environ.get('IDF_PIP_WHEELS_URL', 'https://dl.espressif.com/pypi') IDF_PIP_WHEELS_URL = os.environ.get('IDF_PIP_WHEELS_URL', 'https://dl.espressif.com/pypi')
PYTHON_VENV_DIR_TEMPLATE = 'idf{}_py{}_env'
PYTHON_VER_MAJOR_MINOR = f'{sys.version_info.major}.{sys.version_info.minor}'
VENV_VER_FILE = 'idf_version.txt'
class GlobalVarsStore: class GlobalVarsStore:
@ -1687,11 +1690,11 @@ def get_python_env_path() -> Tuple[str, str, str, str]:
""" """
Returns tuple of Python environment path, Python env. path with subdir and full path from Python (i.e. with executable). Returns tuple of Python environment path, Python env. path with subdir and full path from Python (i.e. with executable).
""" """
python_ver_major_minor = f'{sys.version_info.major}.{sys.version_info.minor}'
idf_version = get_idf_version() idf_version = get_idf_version()
idf_python_env_path = os.getenv('IDF_PYTHON_ENV_PATH') or os.path.join(g.idf_tools_path, 'python_env', idf_python_env_path = os.getenv('IDF_PYTHON_ENV_PATH') or os.path.join(g.idf_tools_path,
f'idf{idf_version}_py{python_ver_major_minor}_env') 'python_env',
PYTHON_VENV_DIR_TEMPLATE.format(idf_version,
PYTHON_VER_MAJOR_MINOR))
python_exe, subdir = get_python_exe_and_subdir() python_exe, subdir = get_python_exe_and_subdir()
idf_python_export_path = os.path.join(idf_python_env_path, subdir) idf_python_export_path = os.path.join(idf_python_env_path, subdir)
@ -2122,6 +2125,23 @@ def process_tool(
return tool_export_paths, tool_export_vars, tool_found return tool_export_paths, tool_export_vars, tool_found
def check_python_venv_compatibility(idf_python_env_path: str, idf_version: str) -> None:
try:
with open(os.path.join(idf_python_env_path, VENV_VER_FILE), 'r') as f:
read_idf_version = f.read().strip()
if read_idf_version != idf_version:
fatal(f'Python environment is set to {idf_python_env_path} which was generated for '
f'ESP-IDF {read_idf_version} instead of the current {idf_version}. '
'The issue can be solved by (1) removing the directory and re-running the install script, '
'or (2) unsetting the IDF_PYTHON_ENV_PATH environment variable, or (3) '
're-runing the install script from a clean shell where an ESP-IDF environment is '
'not active.')
raise SystemExit(1)
except OSError as e:
# perhaps the environment was generated before the support for VENV_VER_FILE was added
warn(f'Error while accessing the ESP-IDF version file in the Python environment: {e}')
def action_export(args: Any) -> None: def action_export(args: Any) -> None:
""" """
Exports all necessary environment variables and paths needed for tools used. Exports all necessary environment variables and paths needed for tools used.
@ -2166,6 +2186,8 @@ def action_export(args: Any) -> None:
if os.getenv('ESP_IDF_VERSION') != idf_version: if os.getenv('ESP_IDF_VERSION') != idf_version:
export_vars['ESP_IDF_VERSION'] = idf_version export_vars['ESP_IDF_VERSION'] = idf_version
check_python_venv_compatibility(idf_python_env_path, idf_version)
idf_tools_dir = os.path.join(g.idf_path, 'tools') # type: ignore idf_tools_dir = os.path.join(g.idf_path, 'tools') # type: ignore
idf_tools_dir = to_shell_specific_paths([idf_tools_dir])[0] idf_tools_dir = to_shell_specific_paths([idf_tools_dir])[0]
if current_path and idf_tools_dir not in current_path: if current_path and idf_tools_dir not in current_path:
@ -2582,7 +2604,9 @@ def action_install_python_env(args): # type: ignore
venv_can_upgrade = False venv_can_upgrade = False
if not os.path.exists(virtualenv_python): if os.path.exists(virtualenv_python):
check_python_venv_compatibility(idf_python_env_path, idf_version)
else:
if subprocess.run([sys.executable, '-m', 'venv', '-h'], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0: if subprocess.run([sys.executable, '-m', 'venv', '-h'], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0:
# venv available # venv available
virtualenv_options = ['--clear'] # delete environment if already exists virtualenv_options = ['--clear'] # delete environment if already exists
@ -2592,10 +2616,34 @@ def action_install_python_env(args): # type: ignore
venv_can_upgrade = True venv_can_upgrade = True
info(f'Creating a new Python environment in {idf_python_env_path}') info(f'Creating a new Python environment in {idf_python_env_path}')
try:
environ_idf_python_env_path = os.environ['IDF_PYTHON_ENV_PATH']
correct_env_path = environ_idf_python_env_path.endswith(PYTHON_VENV_DIR_TEMPLATE.format(idf_version,
PYTHON_VER_MAJOR_MINOR))
if not correct_env_path and re.search(PYTHON_VENV_DIR_TEMPLATE.format(r'\d+\.\d+', r'\d+\.\d+'),
environ_idf_python_env_path):
warn(f'IDF_PYTHON_ENV_PATH is set to {environ_idf_python_env_path} but it does not match '
f'the detected {idf_version} ESP-IDF version and/or the used {PYTHON_VER_MAJOR_MINOR} '
'version of Python. If you have not set IDF_PYTHON_ENV_PATH intentionally then it is '
'recommended to re-run this script from a clean shell where an ESP-IDF environment is '
'not active.')
except KeyError:
# if IDF_PYTHON_ENV_PATH not defined then the above checks can be skipped
pass
subprocess.check_call([sys.executable, '-m', 'venv', subprocess.check_call([sys.executable, '-m', 'venv',
*virtualenv_options, *virtualenv_options,
idf_python_env_path], idf_python_env_path],
stdout=sys.stdout, stderr=sys.stderr) stdout=sys.stdout, stderr=sys.stderr)
try:
with open(os.path.join(idf_python_env_path, VENV_VER_FILE), 'w') as f:
f.write(idf_version)
except OSError as e:
warn(f'Error while generating the ESP-IDF version file in the Python environment: {e}')
else: else:
# The embeddable Python for Windows doesn't have the built-in venv module # The embeddable Python for Windows doesn't have the built-in venv module
install_legacy_python_virtualenv(idf_python_env_path) install_legacy_python_virtualenv(idf_python_env_path)