Skip to content

Commit f62b9f3

Browse files
authored
improve error handling (#317)
1 parent f83ad8c commit f62b9f3

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

src/workflow/CommandExecutor.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, workflow_dir: Path, logger: Logger, parameter_manager: Parame
2727

2828
def run_multiple_commands(
2929
self, commands: list[str]
30-
) -> None:
30+
) -> bool:
3131
"""
3232
Executes multiple shell commands concurrently in separate threads.
3333
@@ -38,17 +38,28 @@ def run_multiple_commands(
3838
Args:
3939
commands (list[str]): A list where each element is a list representing
4040
a command and its arguments.
41+
42+
Returns:
43+
bool: True if all commands succeeded, False if any failed.
4144
"""
4245
# Log the start of command execution
4346
self.logger.log(f"Running {len(commands)} commands in parallel...", 1)
4447
start_time = time.time()
4548

49+
results = []
50+
lock = threading.Lock()
51+
52+
def run_and_track(cmd):
53+
success = self.run_command(cmd)
54+
with lock:
55+
results.append(success)
56+
4657
# Initialize a list to keep track of threads
4758
threads = []
4859

4960
# Start a new thread for each command
5061
for cmd in commands:
51-
thread = threading.Thread(target=self.run_command, args=(cmd,))
62+
thread = threading.Thread(target=run_and_track, args=(cmd,))
5263
thread.start()
5364
threads.append(thread)
5465

@@ -60,7 +71,9 @@ def run_multiple_commands(
6071
end_time = time.time()
6172
self.logger.log(f"Total time to run {len(commands)} commands: {end_time - start_time:.2f} seconds", 1)
6273

63-
def run_command(self, command: list[str]) -> None:
74+
return all(results)
75+
76+
def run_command(self, command: list[str]) -> bool:
6477
"""
6578
Executes a specified shell command and logs its execution details.
6679
@@ -110,7 +123,9 @@ def run_command(self, command: list[str]) -> None:
110123

111124
# Check for errors
112125
if process.returncode != 0:
113-
self.logger.log(f"ERRORS OCCURRED: Process exited with code {process.returncode}", 2)
126+
self.logger.log(f"ERROR: Command failed with exit code {process.returncode}: {command[0]}", 0)
127+
return False
128+
return True
114129

115130
def _stream_output(self, process: subprocess.Popen) -> None:
116131
"""
@@ -157,7 +172,7 @@ def read_stderr():
157172
stdout_thread.join()
158173
stderr_thread.join()
159174

160-
def run_topp(self, tool: str, input_output: dict, custom_params: dict = {}) -> None:
175+
def run_topp(self, tool: str, input_output: dict, custom_params: dict = {}) -> bool:
161176
"""
162177
Constructs and executes commands for the specified tool OpenMS TOPP tool based on the given
163178
input and output configurations. Ensures that all input/output file lists
@@ -176,6 +191,9 @@ def run_topp(self, tool: str, input_output: dict, custom_params: dict = {}) -> N
176191
input_output (dict): A dictionary specifying the input/output parameter names (as key) and their corresponding file paths (as value).
177192
custom_params (dict): A dictionary of custom parameters to pass to the tool.
178193
194+
Returns:
195+
bool: True if all commands succeeded, False if any failed.
196+
179197
Raises:
180198
ValueError: If the lengths of input/output file lists are inconsistent,
181199
except for single string inputs.
@@ -240,9 +258,9 @@ def run_topp(self, tool: str, input_output: dict, custom_params: dict = {}) -> N
240258

241259
# Run command(s)
242260
if len(commands) == 1:
243-
self.run_command(commands[0])
261+
return self.run_command(commands[0])
244262
elif len(commands) > 1:
245-
self.run_multiple_commands(commands)
263+
return self.run_multiple_commands(commands)
246264
else:
247265
raise Exception("No commands to execute.")
248266

src/workflow/StreamlitUI.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,10 @@ def execution_section(
10811081
with open(log_path, "r", encoding="utf-8") as f:
10821082
content = f.read()
10831083
# Check if workflow finished successfully
1084-
if "WORKFLOW FINISHED" not in content:
1085-
st.error("**Workflow did not complete. Check log file for errors.**")
1086-
elif "ERRORS OCCURRED" in content or "ERROR:" in content:
1087-
st.warning("**Workflow completed with errors. Some results may be missing.**")
1084+
if "WORKFLOW FINISHED" in content:
1085+
st.success("**Workflow completed successfully.**")
1086+
else:
1087+
st.error("**Errors occurred, check log file.**")
10881088
st.code(content, language="neon", line_numbers=False)
10891089

10901090
def _show_queue_status(self, status: dict) -> None:

src/workflow/WorkflowManager.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ def workflow_process(self) -> None:
101101
if results_dir.exists():
102102
shutil.rmtree(results_dir)
103103
results_dir.mkdir(parents=True)
104-
self.execution()
105-
self.logger.log("WORKFLOW FINISHED")
104+
success = self.execution()
105+
if success:
106+
self.logger.log("WORKFLOW FINISHED")
106107
except Exception as e:
107108
self.logger.log(f"ERROR: {e}")
108109
self.logger.log("".join(traceback.format_exception(e)))
@@ -258,14 +259,15 @@ def configure(self) -> None:
258259
###################################
259260
pass
260261

261-
def execution(self) -> None:
262+
def execution(self) -> bool:
262263
"""
263-
Add your workflow steps here
264+
Add your workflow steps here.
265+
Returns True on success, False on error.
264266
"""
265267
###################################
266268
# Add your workflow steps here
267269
###################################
268-
pass
270+
return True
269271

270272
def results(self) -> None:
271273
"""

0 commit comments

Comments
 (0)