@@ -83,52 +83,26 @@ def setup_pipenv_in_package():
83
83
env .Execute (
84
84
env .VerboseAction (
85
85
'"$PYTHONEXE" -m venv --clear "%s"' % penv_dir ,
86
- "Creating a new virtual environment for Python dependencies" ,
86
+ "Creating pioarduino Python virtual environment: %s" % penv_dir ,
87
87
)
88
88
)
89
-
90
89
assert os .path .isfile (
91
90
pip_path
92
91
), "Error: Failed to create a proper virtual environment. Missing the `pip` binary!"
93
92
94
93
penv_python = os .path .join (penv_dir , "Scripts" , "python.exe" ) if IS_WINDOWS else os .path .join (penv_dir , "bin" , "python" )
95
94
env .Replace (PYTHONEXE = penv_python )
96
- print (f"PYTHONEXE updated to penv environment: { penv_python } " )
97
95
96
+ # Setup virtual environment if needed and find path to Python exe
98
97
setup_pipenv_in_package ()
99
- # Update global PYTHON_EXE variable after potential pipenv setup
98
+ # Set Python Scons Var to env Python
100
99
PYTHON_EXE = env .subst ("$PYTHONEXE" )
101
- python_exe = PYTHON_EXE
102
-
103
- # Ensure penv Python directory is in PATH for subprocess calls
104
- python_dir = os .path .dirname (PYTHON_EXE )
105
- current_path = os .environ .get ("PATH" , "" )
106
- if python_dir not in current_path :
107
- os .environ ["PATH" ] = python_dir + os .pathsep + current_path
100
+ # Remove PYTHONHOME if set
101
+ os .environ .pop ('PYTHONHOME' , None )
108
102
109
- # Verify the Python executable exists
103
+ # check for python binary, exit with error when not found
110
104
assert os .path .isfile (PYTHON_EXE ), f"Python executable not found: { PYTHON_EXE } "
111
105
112
- if os .path .isfile (python_exe ):
113
- # Update sys.path to include penv site-packages
114
- if IS_WINDOWS :
115
- penv_site_packages = os .path .join (penv_dir , "Lib" , "site-packages" )
116
- else :
117
- # Find the actual site-packages directory in the venv
118
- penv_lib_dir = os .path .join (penv_dir , "lib" )
119
- if os .path .isdir (penv_lib_dir ):
120
- for python_dir in os .listdir (penv_lib_dir ):
121
- if python_dir .startswith ("python" ):
122
- penv_site_packages = os .path .join (penv_lib_dir , python_dir , "site-packages" )
123
- break
124
- else :
125
- penv_site_packages = None
126
- else :
127
- penv_site_packages = None
128
-
129
- if penv_site_packages and os .path .isdir (penv_site_packages ) and penv_site_packages not in sys .path :
130
- sys .path .insert (0 , penv_site_packages )
131
-
132
106
def add_to_pythonpath (path ):
133
107
"""
134
108
Add a path to the PYTHONPATH environment variable (cross-platform).
@@ -138,43 +112,63 @@ def add_to_pythonpath(path):
138
112
"""
139
113
# Normalize the path for the current OS
140
114
normalized_path = os .path .normpath (path )
141
-
115
+
142
116
# Add to PYTHONPATH environment variable
143
117
if "PYTHONPATH" in os .environ :
144
118
current_paths = os .environ ["PYTHONPATH" ].split (os .pathsep )
145
119
normalized_current_paths = [os .path .normpath (p ) for p in current_paths ]
146
120
if normalized_path not in normalized_current_paths :
147
- os .environ ["PYTHONPATH" ] = normalized_path + os .pathsep + os .environ .get ("PYTHONPATH" , "" )
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 )
148
124
else :
149
125
os .environ ["PYTHONPATH" ] = normalized_path
150
-
126
+
151
127
# Also add to sys.path for immediate availability
152
128
if normalized_path not in sys .path :
153
129
sys .path .insert (0 , normalized_path )
154
130
131
+
155
132
def setup_python_paths ():
156
133
"""
157
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.
158
138
"""
159
139
# Get the directory containing the Python executable
160
140
python_dir = os .path .dirname (PYTHON_EXE )
161
- add_to_pythonpath (python_dir )
162
-
163
- # Try to find site-packages directory using the actual Python executable
164
- result = subprocess .run (
165
- [PYTHON_EXE , "-c" , "import site; print(site.getsitepackages()[0])" ],
166
- capture_output = True ,
167
- text = True ,
168
- timeout = 5
169
- )
170
- if result .returncode == 0 :
171
- site_packages = result .stdout .strip ()
172
- if os .path .isdir (site_packages ):
173
- add_to_pythonpath (site_packages )
174
141
175
- # Setup Python paths based on the actual Python executable
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
+
176
169
setup_python_paths ()
177
170
171
+
178
172
def _get_executable_path (python_exe , executable_name ):
179
173
"""
180
174
Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
@@ -290,14 +284,7 @@ def install_python_deps():
290
284
291
285
# Update uv executable path after installation
292
286
uv_executable = _get_uv_executable_path (PYTHON_EXE )
293
-
294
- # Add Scripts directory to PATH for Windows
295
- if IS_WINDOWS :
296
- python_dir = os .path .dirname (PYTHON_EXE )
297
- scripts_dir = os .path .join (python_dir , "Scripts" )
298
- if os .path .isdir (scripts_dir ):
299
- os .environ ["PATH" ] = scripts_dir + os .pathsep + os .environ .get ("PATH" , "" )
300
-
287
+
301
288
except subprocess .TimeoutExpired :
302
289
print ("Error: uv installation timed out" )
303
290
return False
@@ -335,7 +322,7 @@ def _get_installed_uv_packages():
335
322
for p in packages :
336
323
result [p ["name" ]] = pepver_to_semver (p ["version" ])
337
324
else :
338
- print (f"Warning: pip list failed with exit code { result_obj .returncode } " )
325
+ print (f"Warning: uv pip list failed with exit code { result_obj .returncode } " )
339
326
if result_obj .stderr :
340
327
print (f"Error output: { result_obj .stderr .strip ()} " )
341
328
@@ -393,10 +380,12 @@ def _get_installed_uv_packages():
393
380
def install_esptool ():
394
381
"""
395
382
Install esptool from package folder "tool-esptoolpy" using uv package manager.
396
- Also determines the path to the esptool executable binary.
397
383
398
384
Returns:
399
- str: Path to esptool executable, or 'esptool' as fallback
385
+ str: Path to esptool executable
386
+
387
+ Raises:
388
+ SystemExit: If esptool installation fails
400
389
"""
401
390
try :
402
391
subprocess .check_call (
@@ -405,29 +394,28 @@ def install_esptool():
405
394
stderr = subprocess .DEVNULL ,
406
395
env = os .environ
407
396
)
408
- esptool_binary_path = _get_esptool_executable_path (PYTHON_EXE )
409
- return esptool_binary_path
397
+ return _get_esptool_executable_path (PYTHON_EXE )
410
398
except (subprocess .CalledProcessError , FileNotFoundError ):
411
399
pass
412
400
413
401
esptool_repo_path = env .subst (platform .get_package_dir ("tool-esptoolpy" ) or "" )
414
- if esptool_repo_path and os .path .isdir (esptool_repo_path ):
415
- uv_executable = _get_uv_executable_path ( PYTHON_EXE )
416
- try :
417
- subprocess . check_call ([
418
- uv_executable , "pip" , "install" , "--quiet" ,
419
- f"--python= { PYTHON_EXE } " ,
420
- "-e" , esptool_repo_path
421
- ], env = os . environ )
422
-
423
- esptool_binary_path = _get_esptool_executable_path ( PYTHON_EXE )
424
- return esptool_binary_path
425
-
426
- except subprocess . CalledProcessError as e :
427
- print ( f"Warning: Failed to install esptool: { e } " )
428
- return 'esptool' # Fallback
429
-
430
- return 'esptool' # Fallback
402
+ if not esptool_repo_path or not os .path .isdir (esptool_repo_path ):
403
+ print ( "Error: esptool package directory not found" )
404
+ sys . exit ( 1 )
405
+
406
+ uv_executable = _get_uv_executable_path ( PYTHON_EXE )
407
+ try :
408
+ subprocess . check_call ([
409
+ uv_executable , "pip" , "install" , "--quiet" ,
410
+ f"--python= { PYTHON_EXE } " ,
411
+ "-e" , esptool_repo_path
412
+ ], env = os . environ )
413
+
414
+ return _get_esptool_executable_path ( PYTHON_EXE )
415
+
416
+ except subprocess . CalledProcessError as e :
417
+ print ( f"Error: Failed to install esptool: { e } " )
418
+ sys . exit ( 1 )
431
419
432
420
433
421
# Install Python dependencies
0 commit comments