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,60 @@ 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, the workflow may not run correctly ." ,
137140 flush = True ,
138141 )
139- logger .warning (f"Failed to install dependencies: { msg } " )
142+ logger .warning (f"Failed to { action . lower () } dependencies: { msg } " )
140143 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 )
144+ else :
145+ # save the hash of the requirements file content
146+ dep_hash = self .get_dep_hash (reqirements_file )
147+ cache_file = os .path .join (ENV_CACHE_DIR , f"{ env_name } " )
148+ with open (cache_file , "w" , encoding = "utf-8" ) as f :
149+ f .write (dep_hash )
148150
149151 print ("\n ```" , flush = True )
150152 return self .get_py (env_name )
@@ -160,16 +162,31 @@ def create_with_virtualenv(self, env_name: str) -> Tuple[bool, str]:
160162 try :
161163 # Use virtualenv.cli_run to create a virtual environment
162164 virtualenv .cli_run ([env_path , "--python" , sys .executable ])
165+
166+ # Create sitecustomize.py in the lib/site-packages directory
167+ site_packages_dir = os .path .join (env_path , "Lib" , "site-packages" )
168+ if os .path .exists (site_packages_dir ):
169+ sitecustomize_path = os .path .join (site_packages_dir , "sitecustomize.py" )
170+ with open (sitecustomize_path , "w" ) as f :
171+ f .write ("import sys\n " )
172+ f .write ("sys.path = [path for path in sys.path if path.find(\" conda\" ) == -1]" )
173+
163174 return True , ""
164175 except Exception as e :
165176 return False , str (e )
166177
167178 def install (self , env_name : str , requirements_file : str ) -> Tuple [bool , str ]:
168179 """
169- Install requirements into the python environment.
180+ Install or update requirements in the python environment.
170181
171182 Args:
172- requirements: the absolute path to the requirements file.
183+ env_name: the name of the python environment
184+ requirements_file: the absolute path to the requirements file.
185+
186+ Returns:
187+ A tuple (success, message), where success is a boolean indicating
188+ whether the installation was successful, and message is a string
189+ containing output or error information.
173190 """
174191 py = self .get_py (env_name )
175192 if not py :
@@ -178,6 +195,7 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
178195 if not os .path .exists (requirements_file ):
179196 return False , "Dependencies file not found."
180197
198+ # Base command
181199 cmd = [
182200 py ,
183201 "-m" ,
@@ -189,17 +207,24 @@ def install(self, env_name: str, requirements_file: str) -> Tuple[bool, str]:
189207 PYPI_TUNA ,
190208 "--no-warn-script-location" ,
191209 ]
210+
211+ # Check if this is an update or a fresh install
212+ cache_file = os .path .join (ENV_CACHE_DIR , f"{ env_name } " )
213+ if os .path .exists (cache_file ):
214+ # This is an update, add --upgrade flag
215+ cmd .append ("--upgrade" )
216+
192217 env = os .environ .copy ()
193- env .pop ("PYTHONPATH" )
218+ env .pop ("PYTHONPATH" , None )
194219 with subprocess .Popen (
195220 cmd , stdout = subprocess .DEVNULL , stderr = subprocess .PIPE , env = env
196221 ) as proc :
197222 _ , err = proc .communicate ()
198223
199224 if proc .returncode != 0 :
200- return False , err .decode (" utf-8" )
225+ return False , f"Installation failed: { err .decode (' utf-8' ) } "
201226
202- return True , " "
227+ return True , f"Installation successful "
203228
204229 def should_reinstall (self , env_name : str , requirements_file : str ) -> bool :
205230 """
@@ -247,7 +272,28 @@ def create(self, env_name: str, py_version: str) -> Tuple[bool, str]:
247272 return False , msg
248273 return True , ""
249274
250- def remove (self , env_name : str ) -> bool :
275+ def remove (self , env_name : str , py_version : Optional [str ] = None ) -> bool :
276+ if py_version :
277+ return self .remove_by_mamba (env_name )
278+ return self .remove_by_del (env_name )
279+
280+ def remove_by_del (self , env_name : str ) -> bool :
281+ """
282+ Remove the python environment.
283+ """
284+ env_path = os .path .join (MAMBA_PY_ENVS , env_name )
285+ try :
286+ # Remove the environment directory
287+ if os .path .exists (env_path ):
288+ shutil .rmtree (env_path )
289+ return True
290+ except Exception as e :
291+ print (f"Failed to remove environment { env_name } : { e } " )
292+ return False
293+
294+
295+
296+ def remove_by_mamba (self , env_name : str ) -> bool :
251297 """
252298 Remove the python environment.
253299 """
0 commit comments