Skip to content

Commit 757bd99

Browse files
authored
simplify venv setup
by using fixed path and addsitedir
1 parent 020fd73 commit 757bd99

File tree

1 file changed

+47
-145
lines changed

1 file changed

+47
-145
lines changed

builder/main.py

Lines changed: 47 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import json
1717
import os
1818
import re
19+
import site
1920
import semantic_version
2021
import shlex
2122
import subprocess
@@ -61,19 +62,23 @@
6162
platform = env.PioPlatform()
6263
projectconfig = env.GetProjectConfig()
6364
terminal_cp = locale.getpreferredencoding().lower()
64-
PYTHON_EXE = env.subst("$PYTHONEXE") # Global Python executable path
65-
66-
# Framework directory path
6765
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
68-
6966
platformio_dir = projectconfig.get("platformio", "core_dir")
67+
68+
# Global Python executable path, replaced later with venv python path
69+
PYTHON_EXE = env.subst("$PYTHONEXE")
7070
penv_dir = os.path.join(platformio_dir, "penv")
7171

72-
pip_path = os.path.join(
73-
penv_dir,
74-
"Scripts" if IS_WINDOWS else "bin",
75-
"pip" + (".exe" if IS_WINDOWS else ""),
76-
)
72+
73+
def get_executable_path(executable_name):
74+
"""
75+
Get the path to an executable based on the penv_dir.
76+
"""
77+
exe_suffix = ".exe" if IS_WINDOWS else ""
78+
scripts_dir = "Scripts" if IS_WINDOWS else "bin"
79+
80+
return os.path.join(penv_dir, scripts_dir, f"{executable_name}{exe_suffix}")
81+
7782

7883
def setup_pipenv_in_package():
7984
"""
@@ -87,144 +92,49 @@ def setup_pipenv_in_package():
8792
)
8893
)
8994
assert os.path.isfile(
90-
pip_path
95+
get_executable_path("pip")
9196
), "Error: Failed to create a proper virtual environment. Missing the `pip` binary!"
9297

93-
penv_python = os.path.join(penv_dir, "Scripts", "python.exe") if IS_WINDOWS else os.path.join(penv_dir, "bin", "python")
94-
env.Replace(PYTHONEXE=penv_python)
9598

96-
# Setup virtual environment if needed and find path to Python exe
99+
# Setup virtual environment if needed
97100
setup_pipenv_in_package()
101+
98102
# Set Python Scons Var to env Python
99-
PYTHON_EXE = env.subst("$PYTHONEXE")
100-
# Remove PYTHONHOME if set
101-
os.environ.pop('PYTHONHOME', None)
103+
penv_python = get_executable_path("python")
104+
env.Replace(PYTHONEXE=penv_python)
105+
PYTHON_EXE = penv_python
102106

103107
# check for python binary, exit with error when not found
104108
assert os.path.isfile(PYTHON_EXE), f"Python executable not found: {PYTHON_EXE}"
105109

106-
def add_to_pythonpath(path):
107-
"""
108-
Add a path to the PYTHONPATH environment variable (cross-platform).
109-
110-
Args:
111-
path (str): The path to add to PYTHONPATH
112-
"""
113-
# Normalize the path for the current OS
114-
normalized_path = os.path.normpath(path)
115-
116-
# Add to PYTHONPATH environment variable
117-
if "PYTHONPATH" in os.environ:
118-
current_paths = os.environ["PYTHONPATH"].split(os.pathsep)
119-
normalized_current_paths = [os.path.normpath(p) for p in current_paths]
120-
if normalized_path not in normalized_current_paths:
121-
# Rebuild PYTHONPATH with normalized paths to avoid duplicates
122-
normalized_current_paths.insert(0, normalized_path)
123-
os.environ["PYTHONPATH"] = os.pathsep.join(normalized_current_paths)
124-
else:
125-
os.environ["PYTHONPATH"] = normalized_path
126-
127-
# Also add to sys.path for immediate availability
128-
if normalized_path not in sys.path:
129-
sys.path.insert(0, normalized_path)
130-
131110

132111
def setup_python_paths():
133112
"""
134-
Setup Python paths based on the actual Python executable being used.
135-
136-
This function configures both PYTHONPATH environment variable and sys.path
137-
to include the Python executable directory and site-packages directory.
113+
Setup Python module search paths using the penv_dir.
138114
"""
139-
# Get the directory containing the Python executable
140-
python_dir = os.path.dirname(PYTHON_EXE)
141-
142-
# Add Scripts directory to PATH for Windows
143-
if IS_WINDOWS:
144-
scripts_dir = os.path.join(python_dir, "Scripts")
145-
if os.path.isdir(scripts_dir):
146-
os.environ["PATH"] = scripts_dir + os.pathsep + os.environ.get("PATH", "")
147-
else:
148-
bin_dir = os.path.join(python_dir, "bin")
149-
if os.path.isdir(bin_dir):
150-
os.environ["PATH"] = bin_dir + os.pathsep + os.environ.get("PATH", "")
151-
152-
penv_site_packages = None
153-
if python_dir not in sys.path:
154-
add_to_pythonpath(python_dir)
155-
if IS_WINDOWS:
156-
penv_site_packages = os.path.join(penv_dir, "Lib", "site-packages")
157-
else:
158-
# Find the actual site-packages directory in the venv
159-
penv_lib_dir = os.path.join(penv_dir, "lib")
160-
if os.path.isdir(penv_lib_dir):
161-
for python_version_dir in os.listdir(penv_lib_dir):
162-
if python_version_dir.startswith("python"):
163-
penv_site_packages = os.path.join(penv_lib_dir, python_version_dir, "site-packages")
164-
break
165-
166-
if penv_site_packages and os.path.isdir(penv_site_packages) and penv_site_packages not in sys.path:
167-
add_to_pythonpath(penv_site_packages)
168-
169-
setup_python_paths()
170-
171-
172-
def _get_executable_path(python_exe, executable_name):
173-
"""
174-
Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
175-
176-
Args:
177-
python_exe (str): Path to Python executable
178-
executable_name (str): Name of the executable to find (e.g., 'esptool', 'uv')
179-
180-
Returns:
181-
str: Path to executable or fallback to executable name
182-
"""
183-
184-
python_dir = os.path.dirname(python_exe)
115+
# Add penv_dir to module search path
116+
site.addsitedir(penv_dir)
185117

186-
if IS_WINDOWS:
187-
executable_path = os.path.join(python_dir, f"{executable_name}.exe")
188-
else:
189-
# For Unix-like systems, executables are typically in the same directory as python
190-
# or in a bin subdirectory
191-
executable_path = os.path.join(python_dir, executable_name)
192-
193-
# If not found in python directory, try bin subdirectory
194-
if not os.path.isfile(executable_path):
195-
bin_dir = os.path.join(python_dir, "bin")
196-
executable_path = os.path.join(bin_dir, executable_name)
197-
198-
if os.path.isfile(executable_path):
199-
return executable_path
118+
# Add site-packages directory
119+
site_packages = (
120+
os.path.join(penv_dir, "Lib", "site-packages") if IS_WINDOWS
121+
else next(
122+
(os.path.join(penv_dir, "lib", d, "site-packages")
123+
for d in os.listdir(os.path.join(penv_dir, "lib"))
124+
if d.startswith("python")),
125+
None
126+
) if os.path.isdir(os.path.join(penv_dir, "lib")) else None
127+
)
200128

201-
return executable_name # Fallback to command name
129+
if site_packages and os.path.isdir(site_packages):
130+
site.addsitedir(site_packages)
202131

203132

204-
def _get_esptool_executable_path(python_exe):
205-
"""
206-
Get the path to the esptool executable binary.
207-
208-
Args:
209-
python_exe (str): Path to Python executable
210-
211-
Returns:
212-
str: Path to esptool executable
213-
"""
214-
return _get_executable_path(python_exe, "esptool")
215-
133+
setup_python_paths()
216134

217-
def _get_uv_executable_path(python_exe):
218-
"""
219-
Get the path to the uv executable binary.
220-
221-
Args:
222-
python_exe (str): Path to Python executable
223-
224-
Returns:
225-
str: Path to uv executable
226-
"""
227-
return _get_executable_path(python_exe, "uv")
135+
# Set executable paths from tools
136+
esptool_binary_path = get_executable_path("esptool")
137+
uv_executable = get_executable_path("uv")
228138

229139

230140
def get_packages_to_install(deps, installed_packages):
@@ -254,9 +164,6 @@ def install_python_deps():
254164
Returns:
255165
bool: True if successful, False otherwise
256166
"""
257-
# Get uv executable path
258-
uv_executable = _get_uv_executable_path(PYTHON_EXE)
259-
260167
try:
261168
result = subprocess.run(
262169
[uv_executable, "--version"],
@@ -275,15 +182,12 @@ def install_python_deps():
275182
capture_output=True,
276183
text=True,
277184
timeout=30, # 30 second timeout
278-
env=os.environ # Use modified environment with custom PYTHONPATH
185+
env=os.environ # Use modified environment with venv Python
279186
)
280187
if result.returncode != 0:
281188
if result.stderr:
282189
print(f"Error output: {result.stderr.strip()}")
283190
return False
284-
285-
# Update uv executable path after installation
286-
uv_executable = _get_uv_executable_path(PYTHON_EXE)
287191

288192
except subprocess.TimeoutExpired:
289193
print("Error: uv installation timed out")
@@ -312,7 +216,7 @@ def _get_installed_uv_packages():
312216
text=True,
313217
encoding='utf-8',
314218
timeout=30, # 30 second timeout
315-
env=os.environ # Use modified environment with custom PYTHONPATH
219+
env=os.environ # Use modified environment with venv Python
316220
)
317221

318222
if result_obj.returncode == 0:
@@ -355,7 +259,7 @@ def _get_installed_uv_packages():
355259
capture_output=True,
356260
text=True,
357261
timeout=30, # 30 second timeout for package installation
358-
env=os.environ # Use modified environment with custom PYTHONPATH
262+
env=os.environ # Use modified environment with venv Python
359263
)
360264

361265
if result.returncode != 0:
@@ -394,35 +298,33 @@ def install_esptool():
394298
stderr=subprocess.DEVNULL,
395299
env=os.environ
396300
)
397-
return _get_esptool_executable_path(PYTHON_EXE)
301+
return
398302
except (subprocess.CalledProcessError, FileNotFoundError):
399303
pass
400304

401305
esptool_repo_path = env.subst(platform.get_package_dir("tool-esptoolpy") or "")
402306
if not esptool_repo_path or not os.path.isdir(esptool_repo_path):
403307
print("Error: esptool package directory not found")
404308
sys.exit(1)
405-
406-
uv_executable = _get_uv_executable_path(PYTHON_EXE)
309+
407310
try:
408311
subprocess.check_call([
409312
uv_executable, "pip", "install", "--quiet",
410313
f"--python={PYTHON_EXE}",
411314
"-e", esptool_repo_path
412315
], env=os.environ)
413316

414-
return _get_esptool_executable_path(PYTHON_EXE)
317+
return
415318

416319
except subprocess.CalledProcessError as e:
417320
print(f"Error: Failed to install esptool: {e}")
418321
sys.exit(1)
419322

420323

421-
# Install Python dependencies
324+
# Install espressif32 Python dependencies
422325
install_python_deps()
423-
424326
# Install esptool after dependencies
425-
esptool_binary_path = install_esptool()
327+
install_esptool()
426328

427329

428330
def BeforeUpload(target, source, env):

0 commit comments

Comments
 (0)