@@ -83,52 +83,26 @@ def setup_pipenv_in_package():
8383 env .Execute (
8484 env .VerboseAction (
8585 '"$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 ,
8787 )
8888 )
89-
9089 assert os .path .isfile (
9190 pip_path
9291 ), "Error: Failed to create a proper virtual environment. Missing the `pip` binary!"
9392
9493 penv_python = os .path .join (penv_dir , "Scripts" , "python.exe" ) if IS_WINDOWS else os .path .join (penv_dir , "bin" , "python" )
9594 env .Replace (PYTHONEXE = penv_python )
96- print (f"PYTHONEXE updated to penv environment: { penv_python } " )
9795
96+ # Setup virtual environment if needed and find path to Python exe
9897setup_pipenv_in_package ()
99- # Update global PYTHON_EXE variable after potential pipenv setup
98+ # Set Python Scons Var to env Python
10099PYTHON_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 )
108102
109- # Verify the Python executable exists
103+ # check for python binary, exit with error when not found
110104assert os .path .isfile (PYTHON_EXE ), f"Python executable not found: { PYTHON_EXE } "
111105
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-
132106def add_to_pythonpath (path ):
133107 """
134108 Add a path to the PYTHONPATH environment variable (cross-platform).
@@ -138,43 +112,63 @@ def add_to_pythonpath(path):
138112 """
139113 # Normalize the path for the current OS
140114 normalized_path = os .path .normpath (path )
141-
115+
142116 # Add to PYTHONPATH environment variable
143117 if "PYTHONPATH" in os .environ :
144118 current_paths = os .environ ["PYTHONPATH" ].split (os .pathsep )
145119 normalized_current_paths = [os .path .normpath (p ) for p in current_paths ]
146120 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 )
148124 else :
149125 os .environ ["PYTHONPATH" ] = normalized_path
150-
126+
151127 # Also add to sys.path for immediate availability
152128 if normalized_path not in sys .path :
153129 sys .path .insert (0 , normalized_path )
154130
131+
155132def setup_python_paths ():
156133 """
157134 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.
158138 """
159139 # Get the directory containing the Python executable
160140 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 )
174141
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+
176169setup_python_paths ()
177170
171+
178172def _get_executable_path (python_exe , executable_name ):
179173 """
180174 Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
@@ -290,14 +284,7 @@ def install_python_deps():
290284
291285 # Update uv executable path after installation
292286 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+
301288 except subprocess .TimeoutExpired :
302289 print ("Error: uv installation timed out" )
303290 return False
@@ -335,7 +322,7 @@ def _get_installed_uv_packages():
335322 for p in packages :
336323 result [p ["name" ]] = pepver_to_semver (p ["version" ])
337324 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 } " )
339326 if result_obj .stderr :
340327 print (f"Error output: { result_obj .stderr .strip ()} " )
341328
@@ -393,10 +380,12 @@ def _get_installed_uv_packages():
393380def install_esptool ():
394381 """
395382 Install esptool from package folder "tool-esptoolpy" using uv package manager.
396- Also determines the path to the esptool executable binary.
397383
398384 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
400389 """
401390 try :
402391 subprocess .check_call (
@@ -405,29 +394,28 @@ def install_esptool():
405394 stderr = subprocess .DEVNULL ,
406395 env = os .environ
407396 )
408- esptool_binary_path = _get_esptool_executable_path (PYTHON_EXE )
409- return esptool_binary_path
397+ return _get_esptool_executable_path (PYTHON_EXE )
410398 except (subprocess .CalledProcessError , FileNotFoundError ):
411399 pass
412400
413401 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 )
431419
432420
433421# Install Python dependencies
0 commit comments