1+ import logging
12import os
23import shutil
34import subprocess
45import tempfile
56from enum import Enum
6- from typing import List , Set
7+ from typing import List , Optional , Set
78
89import pandas as pd
910
@@ -78,16 +79,17 @@ class CondaEnvironment:
7879 Path to the requirements file.
7980 python_version_file_path : str
8081 Path to the python version file.
81- logs_file_path : str
82- Path to the logs file.
82+ logs_file_path : str, optional
83+ Where to log the output of the conda commands.
84+ If None, the output is shown in stdout.
8385 """
8486
8587 def __init__ (
8688 self ,
8789 env_name : str ,
8890 requirements_file_path : str ,
8991 python_version_file_path : str ,
90- logs_file_path : str ,
92+ logs_file_path : Optional [ str ] = None ,
9193 ):
9294 if not self ._conda_available ():
9395 raise Exception ("Conda is not available on this machine." )
@@ -97,21 +99,21 @@ def __init__(
9799 self .python_version_file_path = python_version_file_path
98100 self ._conda_prefix = self ._get_conda_prefix ()
99101 self ._logs_file_path = logs_file_path
100- self ._logs_file = subprocess . PIPE
102+ self ._logs = None
101103
102104 def __enter__ (self ):
103- self ._logs_file = open (self ._logs_file_path , "wb " )
105+ self ._logs = open (self ._logs_file_path , "w " )
104106 existing_envs = self .get_existing_envs ()
105107 if self .env_name in existing_envs :
106- print ( f "Found existing conda environment '{ self . env_name } '." )
108+ logging . info ( "Found existing conda environment '%s '." , self . env_name )
107109 else :
108110 self .create ()
109111 self .install_requirements ()
110112 return self
111113
112114 def __exit__ (self , exc_type , exc_value , traceback ):
113115 self .deactivate ()
114- self ._logs_file .close ()
116+ self ._logs .close ()
115117
116118 def _conda_available (self ) -> bool :
117119 """Checks if conda is available on the machine."""
@@ -129,7 +131,7 @@ def _get_conda_prefix(self) -> str:
129131
130132 def create (self ):
131133 """Creates a conda environment with the specified name and python version."""
132- print ( f "Creating a new conda environment '{ self . env_name } '..." )
134+ logging . info ( "Creating a new conda environment '%s '..." , self . env_name )
133135
134136 with open (
135137 self .python_version_file_path , "r" , encoding = "UTF-8"
@@ -147,55 +149,60 @@ def create(self):
147149 f"python={ python_version } " ,
148150 "--yes" ,
149151 ],
150- stdout = self ._logs_file ,
151- stderr = self . _logs_file ,
152+ stdout = self ._logs ,
153+ stderr = subprocess . STDOUT ,
152154 )
153155 except subprocess .CalledProcessError as err :
154156 raise Exception (
155157 f"Failed to create conda environment '{ self .env_name } ' with python "
156158 f"version { python_version } ."
157- f"Error { err .returncode } : { err .output } "
159+ " Please check the model logs for details. \n "
160+ f"- Error code returned { err .returncode } : { err .output } "
158161 ) from None
159162
160163 def delete (self ):
161164 """Deletes the conda environment with the specified name."""
162- print ( f "Deleting conda environment '{ self . env_name } '..." )
165+ logging . info ( "Deleting conda environment '%s '..." , self . env_name )
163166
164167 try :
165168 subprocess .check_call (
166169 ["conda" , "env" , "remove" , "-n" , f"{ self .env_name } " , "--yes" ],
167- stdout = self . _logs_file ,
168- stderr = self . _logs_file ,
170+ stdout = subprocess . DEVNULL ,
171+ stderr = subprocess . STDOUT ,
169172 )
170173 except subprocess .CalledProcessError as err :
171174 raise Exception (
172175 f"Failed to delete conda environment '{ self .env_name } '."
173- f"Error { err .returncode } : { err .output } "
176+ " Please check the model logs for details. \n "
177+ f"- Error code returned { err .returncode } : { err .output } "
174178 ) from None
175179
176180 def get_existing_envs (self ) -> Set [str ]:
177181 """Gets the names of all existing conda environments."""
178- print ("Checking existing conda environments..." )
182+ logging .info ("Checking existing conda environments..." )
183+
179184 list_envs_command = """
180185 conda env list | awk '{print $1}'
181186 """
187+
182188 try :
183189 envs = subprocess .check_output (
184190 list_envs_command ,
185191 shell = True ,
186- stderr = self ._logs_file ,
192+ stderr = self ._logs ,
187193 )
188194 except subprocess .CalledProcessError as err :
189195 raise Exception (
190196 f"Failed to list conda environments."
191- f"Error { err .returncode } : { err .output } "
197+ " Please check the model logs for details. \n "
198+ f"- Error code returned { err .returncode } : { err .output } "
192199 ) from None
193200 envs = set (envs .decode ("UTF-8" ).split ("\n " ))
194201 return envs
195202
196203 def activate (self ):
197204 """Activates the conda environment with the specified name."""
198- print ( f "Activating conda environment '{ self . env_name } '..." )
205+ logging . info ( "Activating conda environment '%s '..." , self . env_name )
199206
200207 activation_command = f"""
201208 eval $(conda shell.bash hook)
@@ -205,19 +212,20 @@ def activate(self):
205212 try :
206213 subprocess .check_call (
207214 activation_command ,
208- stdout = self ._logs_file ,
209- stderr = self . _logs_file ,
215+ stdout = self ._logs ,
216+ stderr = subprocess . STDOUT ,
210217 shell = True ,
211218 )
212219 except subprocess .CalledProcessError as err :
213220 raise Exception (
214221 f"Failed to activate conda environment '{ self .env_name } '."
215- f"Error { err .returncode } : { err .output } "
222+ " Please check the model logs for details. \n "
223+ f"- Error code returned { err .returncode } : { err .output } "
216224 ) from None
217225
218226 def deactivate (self ):
219227 """Deactivates the conda environment with the specified name."""
220- print ( f "Deactivating conda environment '{ self . env_name } '..." )
228+ logging . info ( "Deactivating conda environment '%s '..." , self . env_name )
221229
222230 deactivation_command = f"""
223231 eval $(conda shell.bash hook)
@@ -228,27 +236,31 @@ def deactivate(self):
228236 subprocess .check_call (
229237 deactivation_command ,
230238 shell = True ,
231- stdout = self ._logs_file ,
232- stderr = self . _logs_file ,
239+ stdout = self ._logs ,
240+ stderr = subprocess . STDOUT ,
233241 )
234242 except subprocess .CalledProcessError as err :
235243 raise Exception (
236244 f"Failed to deactivate conda environment '{ self .env_name } '."
237- f"Error { err .returncode } : { err .output } "
245+ " Please check the model logs for details. \n "
246+ f"- Error code returned { err .returncode } : { err .output } "
238247 ) from None
239248
240249 def install_requirements (self ):
241250 """Installs the requirements from the specified requirements file."""
242- print (f"Installing requirements in conda environment '{ self .env_name } '..." )
251+ logging .info (
252+ "Installing requirements in conda environment '%s'..." , self .env_name
253+ )
243254
244255 try :
245256 self .run_commands (
246257 ["pip" , "install" , "-r" , self .requirements_file_path ],
247258 )
248259 except subprocess .CalledProcessError as err :
249260 raise Exception (
250- f"Failed to install requirements from { self .requirements_file_path } ."
251- f"Error { err .returncode } : { err .output } "
261+ f"Failed to install the depencies specified in the requirements.txt file."
262+ " Please check the model logs for details. \n "
263+ f"- Error code returned { err .returncode } : { err .output } "
252264 ) from None
253265
254266 def run_commands (self , commands : List [str ]):
@@ -266,24 +278,33 @@ def run_commands(self, commands: List[str]):
266278 { " " .join (commands )}
267279 """
268280 subprocess .check_call (
269- full_command ,
270- shell = True ,
271- stdout = self ._logs_file ,
272- stderr = self ._logs_file ,
281+ full_command , shell = True , stdout = self ._logs , stderr = subprocess .STDOUT
273282 )
274283
275284
276285class ModelRunner :
277286 """Wraps the model package and provides a uniform run method."""
278287
279- def __init__ (self , model_package : str ):
288+ def __init__ (self , model_package : str , logs : Optional [ str ] = None ):
280289 self .model_package = model_package
290+
291+ # Save log to the model package if logs is not specified
292+ if logs is None :
293+ logs_file_path = f"{ model_package } /model_run_logs.txt"
294+
295+ logging .basicConfig (
296+ filename = logs_file_path ,
297+ format = "[%(asctime)s] %(levelname)s - %(message)s" ,
298+ level = logging .INFO ,
299+ datefmt = "%Y-%m-%d %H:%M:%S" ,
300+ )
301+
281302 # TODO: change env name to the model id
282303 self ._conda_environment = CondaEnvironment (
283304 env_name = "new-openlayer" ,
284305 requirements_file_path = f"{ model_package } /requirements.txt" ,
285306 python_version_file_path = f"{ model_package } /python_version" ,
286- logs_file_path = f" { model_package } /logs.txt" ,
307+ logs_file_path = logs_file_path ,
287308 )
288309
289310 def __del__ (self ):
@@ -317,6 +338,7 @@ def run(self, input_data: pd.DataFrame) -> pd.DataFrame:
317338
318339 # Run the model in the conda environment
319340 with self ._conda_environment as env :
341+ logging .info ("Running %s rows through the model..." , len (input_data ))
320342 try :
321343 env .run_commands (
322344 [
@@ -329,11 +351,16 @@ def run(self, input_data: pd.DataFrame) -> pd.DataFrame:
329351 ]
330352 )
331353 except subprocess .CalledProcessError as err :
354+ logging .error (
355+ "Failed to run the model. Check the stacktrace above for details."
356+ )
332357 raise Exception (
333- f"Failed to run the model in conda environment '{ env .env_name } '."
334- f"Error { err .returncode } : { err .output } "
358+ "Failed to run the model in the conda environment."
359+ " Please check the model logs for details. \n "
360+ f" Error { err .returncode } : { err .output } "
335361 ) from None
336362
363+ logging .info ("Successfully ran data through the model!" )
337364 # Read the output data from the csv file
338365 output_data = pd .read_csv (f"{ temp_dir } /output_data.csv" )
339366
0 commit comments