99import os
1010import subprocess
1111import tempfile
12+ import textwrap
1213from contextlib import contextmanager
1314from dataclasses import dataclass
1415from pathlib import Path
2425
2526__all__ = [
2627 "ParseResults" ,
28+ "ROBOTError" ,
2729 "convert" ,
2830 "convert_to_obograph" ,
2931 "convert_to_obograph_local" ,
@@ -59,7 +61,7 @@ def is_available() -> bool:
5961 return False
6062
6163 try :
62- check_output (["java" , "--help" ]) # noqa:S607,S603
64+ check_output (["java" , "--help" ]) # noqa: S607
6365 except Exception :
6466 logger .error (
6567 "java --help failed - this means the java runtime environment (JRE) "
@@ -85,11 +87,19 @@ def is_available() -> bool:
8587def call_robot (args : list [str ]) -> str :
8688 """Run a robot command and return the output as a string."""
8789 rr = ["java" , "-jar" , str (get_robot_jar_path ()), * args ]
88- logger .debug ("Running shell command: %s" , args )
89- ret = check_output ( # noqa:S603
90- rr ,
91- cwd = os .path .dirname (__file__ ),
92- )
90+ logger .debug ("Running shell command: %s" , rr )
91+ try :
92+ ret = check_output ( # noqa:S603
93+ rr ,
94+ cwd = os .path .dirname (__file__ ),
95+ )
96+ except subprocess .CalledProcessError as e :
97+ raise ROBOTError (
98+ command = e .cmd ,
99+ return_code = e .returncode ,
100+ output = e .output .decode () if e .output is not None else None ,
101+ ) from None
102+
93103 return ret .decode ()
94104
95105
@@ -440,6 +450,45 @@ def _path_context(path: None | str | Path, name: str = "output.json"):
440450 yield Path (directory ).joinpath (name )
441451
442452
453+ class ROBOTError (Exception ):
454+ """Custom error for ROBOT command failures that includes output preview."""
455+
456+ def __init__ (
457+ self ,
458+ command : list [str ],
459+ return_code : int ,
460+ output : str | None = None ,
461+ preview_length : int = 500 ,
462+ ) -> None :
463+ """Initialize a wrapper around a ROBOT exception.
464+
465+ :param command: The command that was executed and failed
466+ :param return_code: The exit code returned by the command
467+ :param output: The stdout/stderr output from the command execution
468+ :param preview_length:
469+ Maximum number of characters to include in the
470+ error message preview. Default is 500 characters.
471+
472+ The error message will contain the command, return code, and a preview
473+ of the output truncated to preview_length characters.
474+ """
475+ self .command = command
476+ self .return_code = return_code
477+ self .output = output or "<no output>"
478+ self .preview_length = preview_length
479+
480+ # Create the error message
481+ command_str = " " .join (command )
482+ output_preview = textwrap .indent (textwrap .shorten (self .output , preview_length ), " " )
483+
484+ message = (
485+ f"Command `{ command_str } ` returned non-zero exit status { return_code } .\n \n "
486+ f"Output:\n \n { output_preview } "
487+ )
488+
489+ super ().__init__ (message )
490+
491+
443492def convert (
444493 input_path : str | Path ,
445494 output_path : str | Path ,
0 commit comments