Skip to content

Commit ac99273

Browse files
authored
Raise more informative ROBOT error (#37)
1 parent b2fa206 commit ac99273

2 files changed

Lines changed: 75 additions & 15 deletions

File tree

scripts/from_bioregistry.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from tqdm.contrib.logging import logging_redirect_tqdm
1919

2020
import bioontologies
21+
from bioontologies.robot import ROBOTError
2122

2223

2324
@click.command()
@@ -27,19 +28,29 @@ def main() -> None:
2728
resource.prefix
2829
for resource in bioregistry.resources()
2930
if resource.get_download_owl() or resource.get_download_obograph()
30-
][282:]
31+
]
3132
for prefix in tqdm(prefixes, desc="Parsing OWL ontologies", unit="ontology"):
32-
tqdm.write(click.style("\n" + prefix, fg="green"))
33+
tqdm.write(click.style(f"\n[{prefix}]", fg="green"))
3334
with logging_redirect_tqdm():
34-
document = bioontologies.get_obograph_by_prefix(prefix, cache=False)
3535
try:
36-
graph = document.squeeze(standardize=True, prefix=prefix)
37-
except ValueError:
38-
tqdm.write(f"[{prefix}] failed to parse")
39-
else:
40-
tqdm.write(
41-
click.style(f"[{prefix}] parsed {graph.title} - v{graph.version}", fg="red")
36+
document = bioontologies.get_obograph_by_prefix(prefix, cache=False, reason=False)
37+
except ROBOTError as e:
38+
tqdm.write(click.style(f"[{prefix}] {e}", fg="red"))
39+
continue
40+
41+
try:
42+
graph = document.squeeze(
43+
standardize=True, prefix=prefix, tqdm_kwargs={"leave": False}
4244
)
45+
except ValueError as e:
46+
tqdm.write(click.style(f"[{prefix}] failed to parse\n\n{e}", fg="red"))
47+
else:
48+
if graph.version:
49+
msg = f"[{prefix}] parsed {graph.title} - v{graph.version}"
50+
else:
51+
msg = f"[{prefix}] parsed {graph.title}"
52+
53+
tqdm.write(click.style(msg, fg="green"))
4354

4455

4556
if __name__ == "__main__":

src/bioontologies/robot.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import subprocess
1111
import tempfile
12+
import textwrap
1213
from contextlib import contextmanager
1314
from dataclasses import dataclass
1415
from pathlib import Path
@@ -24,6 +25,7 @@
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:
8587
def 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+
443492
def convert(
444493
input_path: str | Path,
445494
output_path: str | Path,

0 commit comments

Comments
 (0)