@@ -111,6 +111,67 @@ def setup_python_paths(env):
111111setup_python_paths (env )
112112
113113
114+ def _get_executable_path (python_exe , executable_name ):
115+ """
116+ Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
117+
118+ Args:
119+ python_exe (str): Path to Python executable
120+ executable_name (str): Name of the executable to find (e.g., 'esptool', 'uv')
121+
122+ Returns:
123+ str: Path to executable or fallback to executable name
124+ """
125+ if not python_exe or not os .path .isfile (python_exe ):
126+ return executable_name # Fallback to command name
127+
128+ python_dir = os .path .dirname (python_exe )
129+
130+ if sys .platform == "win32" :
131+ scripts_dir = os .path .join (python_dir , "Scripts" )
132+ executable_path = os .path .join (scripts_dir , f"{ executable_name } .exe" )
133+ else :
134+ # For Unix-like systems, executables are typically in the same directory as python
135+ # or in a bin subdirectory
136+ executable_path = os .path .join (python_dir , executable_name )
137+
138+ # If not found in python directory, try bin subdirectory
139+ if not os .path .isfile (executable_path ):
140+ bin_dir = os .path .join (python_dir , "bin" )
141+ executable_path = os .path .join (bin_dir , executable_name )
142+
143+ if os .path .isfile (executable_path ):
144+ return executable_path
145+
146+ return executable_name # Fallback to command name
147+
148+
149+ def _get_esptool_executable_path (python_exe ):
150+ """
151+ Get the path to the esptool executable binary.
152+
153+ Args:
154+ python_exe (str): Path to Python executable
155+
156+ Returns:
157+ str: Path to esptool executable
158+ """
159+ return _get_executable_path (python_exe , "esptool" )
160+
161+
162+ def _get_uv_executable_path (python_exe ):
163+ """
164+ Get the path to the uv executable binary.
165+
166+ Args:
167+ python_exe (str): Path to Python executable
168+
169+ Returns:
170+ str: Path to uv executable
171+ """
172+ return _get_executable_path (python_exe , "uv" )
173+
174+
114175def get_packages_to_install (deps , installed_packages ):
115176 """
116177 Generator for Python packages that need to be installed.
@@ -138,9 +199,12 @@ def install_python_deps():
138199 Returns:
139200 bool: True if successful, False otherwise
140201 """
202+ # Get uv executable path
203+ uv_executable = _get_uv_executable_path (env .subst ("$PYTHONEXE" ))
204+
141205 try :
142206 result = subprocess .run (
143- ["uv" , "--version" ],
207+ [uv_executable , "--version" ],
144208 capture_output = True ,
145209 text = True ,
146210 timeout = 3
@@ -162,6 +226,17 @@ def install_python_deps():
162226 if result .stderr :
163227 print (f"Error output: { result .stderr .strip ()} " )
164228 return False
229+
230+ # Update uv executable path after installation
231+ uv_executable = _get_uv_executable_path (env .subst ("$PYTHONEXE" ))
232+
233+ # Add Scripts directory to PATH for Windows
234+ if sys .platform == "win32" :
235+ python_dir = os .path .dirname (env .subst ("$PYTHONEXE" ))
236+ scripts_dir = os .path .join (python_dir , "Scripts" )
237+ if os .path .isdir (scripts_dir ):
238+ os .environ ["PATH" ] = scripts_dir + os .pathsep + os .environ .get ("PATH" , "" )
239+
165240 except subprocess .TimeoutExpired :
166241 print ("Error: uv installation timed out" )
167242 return False
@@ -182,7 +257,7 @@ def _get_installed_uv_packages():
182257 """
183258 result = {}
184259 try :
185- cmd = ["uv" , "pip" , "list" , "--format=json" ]
260+ cmd = [uv_executable , "pip" , "list" , "--format=json" ]
186261 result_obj = subprocess .run (
187262 cmd ,
188263 capture_output = True ,
@@ -221,7 +296,7 @@ def _get_installed_uv_packages():
221296 packages_list = [f"{ p } { python_deps [p ]} " for p in packages_to_install ]
222297
223298 cmd = [
224- "uv" , "pip" , "install" ,
299+ uv_executable , "pip" , "install" ,
225300 f"--python={ env .subst ('$PYTHONEXE' )} " ,
226301 "--quiet" , "--upgrade"
227302 ] + packages_list
@@ -265,56 +340,30 @@ def install_esptool(env):
265340 Returns:
266341 str: Path to esptool executable, or 'esptool' as fallback
267342 """
268- def _get_esptool_executable_path (python_exe ):
269- """
270- Get the path to the esptool executable binary.
271-
272- Args:
273- python_exe (str): Path to Python executable
274-
275- Returns:
276- str: Path to esptool executable
277- """
278- if not python_exe or not os .path .isfile (python_exe ):
279- return 'esptool' # Fallback
280-
281- python_dir = os .path .dirname (python_exe )
282-
283- if sys .platform == "win32" :
284- scripts_dir = os .path .join (python_dir , "Scripts" )
285- esptool_exe = os .path .join (scripts_dir , "esptool.exe" )
286- else :
287- scripts_dir = os .path .join (python_dir )
288- esptool_exe = os .path .join (scripts_dir , "esptool" )
289-
290- if os .path .isfile (esptool_exe ):
291- return esptool_exe
292-
293- return 'esptool'
294-
343+ python_exe = env .subst ("$PYTHONEXE" )
344+
295345 try :
296346 subprocess .check_call (
297- [env . subst ( "$PYTHONEXE" ) , "-c" , "import esptool" ],
347+ [python_exe , "-c" , "import esptool" ],
298348 stdout = subprocess .DEVNULL ,
299349 stderr = subprocess .DEVNULL ,
300350 env = os .environ
301351 )
302- python_exe = env .subst ("$PYTHONEXE" )
303352 esptool_binary_path = _get_esptool_executable_path (python_exe )
304353 return esptool_binary_path
305354 except (subprocess .CalledProcessError , FileNotFoundError ):
306355 pass
307356
308357 esptool_repo_path = env .subst (platform .get_package_dir ("tool-esptoolpy" ) or "" )
309358 if esptool_repo_path and os .path .isdir (esptool_repo_path ):
359+ uv_executable = _get_uv_executable_path (python_exe )
310360 try :
311361 subprocess .check_call ([
312- "uv" , "pip" , "install" , "--quiet" ,
313- f"--python={ env . subst ( '$PYTHONEXE' ) } " ,
362+ uv_executable , "pip" , "install" , "--quiet" ,
363+ f"--python={ python_exe } " ,
314364 "-e" , esptool_repo_path
315365 ], env = os .environ )
316366
317- python_exe = env .subst ("$PYTHONEXE" )
318367 esptool_binary_path = _get_esptool_executable_path (python_exe )
319368 return esptool_binary_path
320369
0 commit comments