Skip to content

Commit 3695ad8

Browse files
committed
Improve error handling for ilastik command execution
- Capture stdout and stderr when commands fail to show actual error messages - Detect line continuations (backslashes) to properly run multiline commands in shell - Better error messages showing both stdout and stderr output - This will help debug ilastik failures that were previously hidden by --redirect_output
1 parent c486e0c commit 3695ad8

2 files changed

Lines changed: 26 additions & 5 deletions

File tree

imc/scripts/predict.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def predict_with_ilastik(
7070
"""
7171
Use a trained ilastik model to classify pixels in an IMC image.
7272
"""
73+
# Don't redirect output if not quiet, so errors are visible
74+
# Even when quiet, we might want to see errors, so we'll capture stderr separately
7375
quiet_arg = "\n --redirect_output /dev/null \\" if quiet else ""
7476
cmd = f"""{ilastik_sh} \\
7577
--headless \\
@@ -79,7 +81,8 @@ def predict_with_ilastik(
7981
"""
8082
# Shell expansion of input files won't happen in subprocess call
8183
cmd += " ".join([x.replace_(" ", r"\ ").as_posix() for x in tiff_files])
82-
return run_shell_command(cmd, quiet=True)
84+
# Don't use quiet=True so we can see errors even when redirecting ilastik output
85+
return run_shell_command(cmd, quiet=quiet)
8386

8487

8588
def get_ilastik(lib_dir: Path, version: str = "1.3.3post2") -> Path:

imc/utils.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,9 @@ def run_shell_command(cmd: str, dry_run: bool = False, quiet: bool = False) -> i
347347
# and needs a command to be called prior such as conda activate, etc
348348
symbol = any(x in cmd for x in ["&", "&&", "|"])
349349
source = cmd.startswith("source")
350-
shell = bool(symbol or source)
350+
# Commands with line continuations (backslashes) need shell processing
351+
has_line_continuation = "\\" in cmd and "\n" in cmd
352+
shell = bool(symbol or source or has_line_continuation)
351353
if not quiet:
352354
print(
353355
"Running command:\n",
@@ -358,19 +360,35 @@ def run_shell_command(cmd: str, dry_run: bool = False, quiet: bool = False) -> i
358360
if shell:
359361
if not quiet:
360362
print("Running command in shell.")
361-
code = subprocess.call(cmd, shell=shell)
363+
# Capture stderr to show actual error messages when command fails
364+
result = subprocess.run(cmd, shell=shell, capture_output=True, text=True)
365+
code = result.returncode
366+
stdout_output = result.stdout
367+
stderr_output = result.stderr
362368
else:
363369
# Allow spaces in file names
364370
c = re.findall(r"\S+", cmd.replace(r"\ ", "__space__").replace("\\\n", ""))
365371
c = [x.replace("__space__", " ") for x in c]
366-
code = subprocess.call(c, shell=shell)
372+
# Capture stderr to show actual error messages when command fails
373+
result = subprocess.run(c, shell=shell, capture_output=True, text=True)
374+
code = result.returncode
375+
stdout_output = result.stdout
376+
stderr_output = result.stderr
367377
if code != 0:
368-
error_msg = "Process for command below failed with exit code {}.\nCommand:\n{}\nTerminating pipeline.\n".format(
378+
error_msg = "Process for command below failed with exit code {}.\nCommand:\n{}\n".format(
369379
code,
370380
textwrap.dedent(cmd)
371381
)
382+
if stdout_output:
383+
error_msg += f"\nStdout:\n{stdout_output}\n"
384+
if stderr_output:
385+
error_msg += f"\nStderr:\n{stderr_output}\n"
386+
error_msg += "Terminating pipeline.\n"
372387
print(error_msg)
373388
sys.exit(code)
389+
elif not quiet and stdout_output:
390+
# Print output if not quiet and there's output
391+
print(stdout_output)
374392
if not shell:
375393
pass
376394
# usage = resource.getrusage(resource.RUSAGE_SELF)

0 commit comments

Comments
 (0)