11import hashlib
22import os
3+ import shutil
34import subprocess
45import sys
56from typing import Dict , Optional , Tuple
@@ -82,6 +83,7 @@ def ensure(
8283 py = self .get_py (env_name )
8384
8485 should_remove_old = False
86+ should_install_deps = False
8587
8688 if py :
8789 # check the version of the python executable
@@ -91,60 +93,61 @@ def ensure(
9193 should_remove_old = True
9294
9395 if reqirements_file and self .should_reinstall (env_name , reqirements_file ):
94- should_remove_old = True
96+ should_install_deps = True
9597
96- if not should_remove_old :
98+ if not should_remove_old and not should_install_deps :
9799 return py
98100
99101 log_file = get_logging_file ()
100102 print ("\n ```Step\n # Setting up workflow environment\n " , flush = True )
101103 if should_remove_old :
102- print (f"- Dependencies of { env_name } have been changed ." , flush = True )
104+ print (f"- Python version of { env_name } needs to be updated ." , flush = True )
103105 print (f"- Removing the old { env_name } ..." , flush = True )
104- self .remove (env_name )
106+ self .remove (env_name , py_version )
107+
108+ # create the environment if it doesn't exist or needs to be recreated
109+ if should_remove_old or not py :
110+ if py_version :
111+ print (f"- Creating { env_name } with { py_version } ..." , flush = True )
112+ create_ok , msg = self .create (env_name , py_version )
113+ else :
114+ print (f"- Creating { env_name } with current Python version..." , flush = True )
115+ create_ok , msg = self .create_with_virtualenv (env_name )
116+
117+ if not create_ok :
118+ print (f"- Failed to create { env_name } ." , flush = True )
119+ print (f"\n For more details, check { log_file } ." , flush = True )
120+ print ("\n ```" , flush = True )
121+ print (
122+ f"\n \n Failed to create { env_name } , the workflow will not run." ,
123+ flush = True ,
124+ )
125+ logger .warning (f"Failed to create { env_name } : { msg } " )
126+ sys .exit (0 )
105127
106- # create the environment
107- if py_version :
108- print (f"- Creating { env_name } with { py_version } ..." , flush = True )
109- create_ok , msg = self .create (env_name , py_version )
110- else :
111- print (f"- Creating { env_name } with current Python version..." , flush = True )
112- create_ok , msg = self .create_with_virtualenv (env_name )
113-
114- if not create_ok :
115- print (f"- Failed to create { env_name } ." , flush = True )
116- print (f"\n For more details, check { log_file } ." , flush = True )
117- print ("\n ```" , flush = True )
118- print (
119- f"\n \n Failed to create { env_name } , the workflow will not run." ,
120- flush = True ,
121- )
122- logger .warning (f"Failed to create { env_name } : { msg } " )
123- sys .exit (0 )
124- # return None
125-
126- # install the requirements
128+ # install or update the requirements
127129 if reqirements_file :
128130 filename = os .path .basename (reqirements_file )
129- print (f"- Installing dependencies from { filename } ..." , flush = True )
131+ action = "Updating" if should_install_deps else "Installing"
132+ print (f"- { action } dependencies from { filename } ..." , flush = True )
130133 install_ok , msg = self .install (env_name , reqirements_file )
131134 if not install_ok :
132- print (f"- Failed to install dependencies from { filename } ." , flush = True )
135+ print (f"- Failed to { action . lower () } dependencies from { filename } ." , flush = True )
133136 print (f"\n For more details, check { log_file } ." , flush = True )
134137 print ("\n ```" , flush = True )
135138 print (
136- "\n \n Failed to install dependencies, the workflow will not run." ,
139+ f"\n \n Failed to { action .lower ()} dependencies, "
140+ "the workflow may not run correctly." ,
137141 flush = True ,
138142 )
139- logger .warning (f"Failed to install dependencies: { msg } " )
143+ logger .warning (f"Failed to { action . lower () } dependencies: { msg } " )
140144 sys .exit (0 )
141- # return None
142-
143- # save the hash of the requirements file content
144- dep_hash = self .get_dep_hash (reqirements_file )
145- cache_file = os .path .join (ENV_CACHE_DIR , f"{ env_name } " )
146- with open (cache_file , "w" , encoding = "utf-8" ) as f :
147- f .write (dep_hash )
145+ else :
146+ # save the hash of the requirements file content
147+ dep_hash = self .get_dep_hash (reqirements_file )
148+ cache_file = os .path .join (ENV_CACHE_DIR , f"{ env_name } " )
149+ with open (cache_file , "w" , encoding = "utf-8" ) as f :
150+ f .write (dep_hash )
148151
149152 print ("\n ```" , flush = True )
150153 return self .get_py (env_name )
@@ -160,16 +163,31 @@ def create_with_virtualenv(self, env_name: str) -> Tuple[bool, str]:
160163 try :
161164 # Use virtualenv.cli_run to create a virtual environment
162165 virtualenv .cli_run ([env_path , "--python" , sys .executable ])
166+
167+ # Create sitecustomize.py in the lib/site-packages directory
168+ site_packages_dir = os .path .join (env_path , "Lib" , "site-packages" )
169+ if os .path .exists (site_packages_dir ):
170+ sitecustomize_path = os .path .join (site_packages_dir , "sitecustomize.py" )
171+ with open (sitecustomize_path , "w" ) as f :
172+ f .write ("import sys\n " )
173+ f .write ('sys.path = [path for path in sys.path if path.find("conda") == -1]' )
174+
163175 return True , ""
164176 except Exception as e :
165177 return False , str (e )
166178
167179 def install (self , env_name : str , requirements_file : str ) -> Tuple [bool , str ]:
168180 """
169- Install requirements into the python environment.
181+ Install or update requirements in the python environment.
170182
171183 Args:
172- requirements: the absolute path to the requirements file.
184+ env_name: the name of the python environment
185+ requirements_file: the absolute path to the requirements file.
186+
187+ Returns:
188+ A tuple (success, message), where success is a boolean indicating
189+ whether the installation was successful, and message is a string
190+ containing output or error information.
173191 """
174192 py = self .get_py (env_name )
175193 if not py :
@@ -178,6 +196,7 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
178196 if not os .path .exists (requirements_file ):
179197 return False , "Dependencies file not found."
180198
199+ # Base command
181200 cmd = [
182201 py ,
183202 "-m" ,
@@ -189,17 +208,24 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
189208 PYPI_TUNA ,
190209 "--no-warn-script-location" ,
191210 ]
211+
212+ # Check if this is an update or a fresh install
213+ cache_file = os .path .join (ENV_CACHE_DIR , f"{ env_name } " )
214+ if os .path .exists (cache_file ):
215+ # This is an update, add --upgrade flag
216+ cmd .append ("--upgrade" )
217+
192218 env = os .environ .copy ()
193- env .pop ("PYTHONPATH" )
219+ env .pop ("PYTHONPATH" , None )
194220 with subprocess .Popen (
195221 cmd , stdout = subprocess .DEVNULL , stderr = subprocess .PIPE , env = env
196222 ) as proc :
197223 _ , err = proc .communicate ()
198224
199225 if proc .returncode != 0 :
200- return False , err .decode (" utf-8" )
226+ return False , f"Installation failed: { err .decode (' utf-8' ) } "
201227
202- return True , ""
228+ return True , "Installation successful "
203229
204230 def should_reinstall (self , env_name : str , requirements_file : str ) -> bool :
205231 """
@@ -247,7 +273,26 @@ def create(self, env_name: str, py_version: str) -> Tuple[bool, str]:
247273 return False , msg
248274 return True , ""
249275
250- def remove (self , env_name : str ) -> bool :
276+ def remove (self , env_name : str , py_version : Optional [str ] = None ) -> bool :
277+ if py_version :
278+ return self .remove_by_mamba (env_name )
279+ return self .remove_by_del (env_name )
280+
281+ def remove_by_del (self , env_name : str ) -> bool :
282+ """
283+ Remove the python environment.
284+ """
285+ env_path = os .path .join (MAMBA_PY_ENVS , env_name )
286+ try :
287+ # Remove the environment directory
288+ if os .path .exists (env_path ):
289+ shutil .rmtree (env_path )
290+ return True
291+ except Exception as e :
292+ print (f"Failed to remove environment { env_name } : { e } " )
293+ return False
294+
295+ def remove_by_mamba (self , env_name : str ) -> bool :
251296 """
252297 Remove the python environment.
253298 """
0 commit comments