From a0a1c90b41211642939a32c7df9393353d78454d Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Fri, 2 May 2025 22:30:50 -0400 Subject: [PATCH 01/13] Raise more informative RobotError --- src/bioontologies/robot.py | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 408254e..fd8ce82 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from pathlib import Path from subprocess import check_output -from typing import Any, Literal +from typing import Any, List, Literal, Optional import bioregistry import pystow @@ -422,6 +422,33 @@ def _path_context(path: None | str | Path, name: str = "output.json"): with tempfile.TemporaryDirectory() as directory: yield Path(directory).joinpath(name) +class RobotError(Exception): + """Custom error for Robot command failures that includes output preview.""" + + def __init__( + self, + command: List[str], + returncode: int, + output: Optional[str] = None, + preview_length: int = 500 + ): + self.command = command + self.returncode = returncode + self.output = output + self.preview_length = preview_length + + # Create the error message + command_str = str(command) + output_preview = output[:preview_length] + "..." if output and len(output) > preview_length else output + + message = ( + f"Command {command_str} returned non-zero exit status {returncode}. \n" + f"Output: {output_preview}" + ) + + super().__init__(message) + + def convert( input_path: str | Path, @@ -504,10 +531,17 @@ def convert( if debug: args.append("-vvv") logger.debug("Running shell command: %s", args) - ret = check_output( # noqa:S603 - args, - cwd=os.path.dirname(__file__), - ) + try: + ret = check_output( # noqa:S603 + args, + cwd=os.path.dirname(__file__), + ) + except subprocess.CalledProcessError as e: + raise RobotError( + command=e.cmd, + returncode=e.returncode, + output=e.output.decode() + ) from None return ret.decode() From 7c230caaaab5bcdf6e26b64dc1bb340cd6463f1b Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Fri, 2 May 2025 22:41:01 -0400 Subject: [PATCH 02/13] Update type annotations --- src/bioontologies/robot.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index fd8ce82..7f06098 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from pathlib import Path from subprocess import check_output -from typing import Any, List, Literal, Optional +from typing import Any, Literal import bioregistry import pystow @@ -427,9 +427,9 @@ class RobotError(Exception): def __init__( self, - command: List[str], + command: list[str], returncode: int, - output: Optional[str] = None, + output: str | None = None, preview_length: int = 500 ): self.command = command @@ -439,7 +439,10 @@ def __init__( # Create the error message command_str = str(command) - output_preview = output[:preview_length] + "..." if output and len(output) > preview_length else output + output_preview = ( + output[:preview_length] + "..." if output and len(output) > preview_length + else output + ) message = ( f"Command {command_str} returned non-zero exit status {returncode}. \n" From cb7cf68ee79f20b060dd904bac7623c1f1db8a20 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Fri, 2 May 2025 22:44:16 -0400 Subject: [PATCH 03/13] Reformat --- src/bioontologies/robot.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 7f06098..d083403 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -422,6 +422,7 @@ def _path_context(path: None | str | Path, name: str = "output.json"): with tempfile.TemporaryDirectory() as directory: yield Path(directory).joinpath(name) + class RobotError(Exception): """Custom error for Robot command failures that includes output preview.""" @@ -430,7 +431,7 @@ def __init__( command: list[str], returncode: int, output: str | None = None, - preview_length: int = 500 + preview_length: int = 500, ): self.command = command self.returncode = returncode @@ -440,8 +441,7 @@ def __init__( # Create the error message command_str = str(command) output_preview = ( - output[:preview_length] + "..." if output and len(output) > preview_length - else output + output[:preview_length] + "..." if output and len(output) > preview_length else output ) message = ( @@ -452,7 +452,6 @@ def __init__( super().__init__(message) - def convert( input_path: str | Path, output_path: str | Path, @@ -540,11 +539,7 @@ def convert( cwd=os.path.dirname(__file__), ) except subprocess.CalledProcessError as e: - raise RobotError( - command=e.cmd, - returncode=e.returncode, - output=e.output.decode() - ) from None + raise RobotError(command=e.cmd, returncode=e.returncode, output=e.output.decode()) from None return ret.decode() From 6356e92246f9643b2abaf467c3b9170a7cf8a39c Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Fri, 2 May 2025 22:46:21 -0400 Subject: [PATCH 04/13] Add docstrings --- src/bioontologies/robot.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index d083403..2c672b5 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -433,6 +433,17 @@ def __init__( output: str | None = None, preview_length: int = 500, ): + """Initialize RobotError + + :param command: The command that was executed and failed + :param returncode: The exit code returned by the command + :param output: The stdout/stderr output from the command execution + :param preview_length: Maximum number of characters to include in the + error message preview. Default is 500 characters. + + The error message will contain the command, return code, and a preview + of the output truncated to preview_length characters. + """ self.command = command self.returncode = returncode self.output = output From 1823facb4f744e9429f6d1625c2b9ba0eefb737c Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Fri, 2 May 2025 22:47:13 -0400 Subject: [PATCH 05/13] More linting --- src/bioontologies/robot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 2c672b5..09c9db7 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -433,7 +433,7 @@ def __init__( output: str | None = None, preview_length: int = 500, ): - """Initialize RobotError + """Initialize RobotError. :param command: The command that was executed and failed :param returncode: The exit code returned by the command From e2e61417c700f5d6dd94b344f5b12cbdd5806fcf Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Thu, 5 Jun 2025 10:18:33 -0400 Subject: [PATCH 06/13] Style adjust --- src/bioontologies/robot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 922fe37..49cab47 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -96,7 +96,6 @@ def call_robot(args: list[str]) -> str: return ret.decode() - @dataclass class ParseResults: """A dataclass containing an OBO Graph JSON and text output from ROBOT.""" From a66ff872df72b560b923340240fdf2d6294b02d2 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 15 Jun 2025 15:51:10 +0200 Subject: [PATCH 07/13] Update robot.py --- src/bioontologies/robot.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 49cab47..298bed0 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -24,6 +24,7 @@ __all__ = [ "ParseResults", + "ROBOTError", "convert", "convert_to_obograph", "convert_to_obograph_local", @@ -92,7 +93,9 @@ def call_robot(args: list[str]) -> str: cwd=os.path.dirname(__file__), ) except subprocess.CalledProcessError as e: - raise RobotError(command=e.cmd, returncode=e.returncode, output=e.output.decode()) from None + raise ROBOTError( + command=e.cmd, return_code=e.returncode, output=e.output.decode() + ) from None return ret.decode() @@ -443,41 +446,40 @@ def _path_context(path: None | str | Path, name: str = "output.json"): yield Path(directory).joinpath(name) -class RobotError(Exception): - """Custom error for Robot command failures that includes output preview.""" +class ROBOTError(Exception): + """Custom error for ROBOT command failures that includes output preview.""" def __init__( self, command: list[str], - returncode: int, + return_code: int, output: str | None = None, preview_length: int = 500, - ): - """Initialize RobotError. + ) -> None: + """Initialize a wrapper around a ROBOT exception. :param command: The command that was executed and failed - :param returncode: The exit code returned by the command + :param return_code: The exit code returned by the command :param output: The stdout/stderr output from the command execution - :param preview_length: Maximum number of characters to include in the + :param preview_length: + Maximum number of characters to include in the error message preview. Default is 500 characters. The error message will contain the command, return code, and a preview of the output truncated to preview_length characters. """ self.command = command - self.returncode = returncode + self.return_code = return_code self.output = output self.preview_length = preview_length # Create the error message - command_str = str(command) - output_preview = ( - output[:preview_length] + "..." if output and len(output) > preview_length else output - ) + command_str = " ".join(command) + output_preview = textwrap.indent(textwrap.shorten(output, preview_length), " ") message = ( - f"Command {command_str} returned non-zero exit status {returncode}. \n" - f"Output: {output_preview}" + f"Command `{command_str}` returned non-zero exit status {return_code}.\n\n" + f"Output:\n\n{output_preview}" ) super().__init__(message) From 3fdeb1f4c880c0f1a2d53bc0803df999cc65375c Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:21:36 +0200 Subject: [PATCH 08/13] Update from_bioregistry.py --- scripts/from_bioregistry.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/scripts/from_bioregistry.py b/scripts/from_bioregistry.py index bbb6b9a..abbda4e 100644 --- a/scripts/from_bioregistry.py +++ b/scripts/from_bioregistry.py @@ -18,6 +18,7 @@ from tqdm.contrib.logging import logging_redirect_tqdm import bioontologies +from bioontologies.robot import ROBOTError @click.command() @@ -27,19 +28,29 @@ def main() -> None: resource.prefix for resource in bioregistry.resources() if resource.get_download_owl() or resource.get_download_obograph() - ][282:] + ] for prefix in tqdm(prefixes, desc="Parsing OWL ontologies", unit="ontology"): - tqdm.write(click.style("\n" + prefix, fg="green")) + tqdm.write(click.style(f"\n[{prefix}]", fg="green")) with logging_redirect_tqdm(): - document = bioontologies.get_obograph_by_prefix(prefix, cache=False) try: - graph = document.squeeze(standardize=True, prefix=prefix) - except ValueError: - tqdm.write(f"[{prefix}] failed to parse") - else: - tqdm.write( - click.style(f"[{prefix}] parsed {graph.title} - v{graph.version}", fg="red") + document = bioontologies.get_obograph_by_prefix(prefix, cache=False, reason=False) + except ROBOTError as e: + tqdm.write(click.style(f"[{prefix}] {e}", fg="red")) + continue + + try: + graph = document.squeeze( + standardize=True, prefix=prefix, tqdm_kwargs={"leave": False} ) + except ValueError as e: + tqdm.write(click.style(f"[{prefix}] failed to parse\n\n{e}", fg="red")) + else: + if graph.version: + msg = f"[{prefix}] parsed {graph.title} - v{graph.version}" + else: + msg = f"[{prefix}] parsed {graph.title}" + + tqdm.write(click.style(msg, fg="green")) if __name__ == "__main__": From b562159593c2a76b07e0e3f1ad18c202a658c462 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:24:14 +0200 Subject: [PATCH 09/13] Update robot.py --- src/bioontologies/robot.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 298bed0..93ea404 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -60,7 +60,7 @@ def is_available() -> bool: return False try: - check_output(["java", "--help"]) # noqa:S607,S603 + check_output(["java", "--help"]) # noqa: S607 except Exception: logger.error( "java --help failed - this means the java runtime environment (JRE) " @@ -475,7 +475,10 @@ def __init__( # Create the error message command_str = " ".join(command) - output_preview = textwrap.indent(textwrap.shorten(output, preview_length), " ") + if output is None: + output_preview = "" + else: + output_preview = textwrap.indent(textwrap.shorten(output, preview_length), " ") message = ( f"Command `{command_str}` returned non-zero exit status {return_code}.\n\n" From f9a7138dd1e6ebafa7590f77a5326a14bda781d9 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:26:26 +0200 Subject: [PATCH 10/13] Update robot.py --- src/bioontologies/robot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 93ea404..380cd5b 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -94,8 +94,11 @@ def call_robot(args: list[str]) -> str: ) except subprocess.CalledProcessError as e: raise ROBOTError( - command=e.cmd, return_code=e.returncode, output=e.output.decode() + command=e.cmd, + return_code=e.returncode, + output=e.output.decode() if e.output is not None else None, ) from None + return ret.decode() From 36f33085f74ebab543817aacaccfec9900e5b860 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:27:14 +0200 Subject: [PATCH 11/13] Update robot.py --- src/bioontologies/robot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 380cd5b..9aff11d 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -571,6 +571,7 @@ def convert( args.extend(("--format", fmt)) if debug: args.append("-vvv") + return call_robot(args) From 08848d6417e70560771959f5a1bec37c83568265 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:28:23 +0200 Subject: [PATCH 12/13] Update robot.py --- src/bioontologies/robot.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index 9aff11d..aa0d67b 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -473,15 +473,12 @@ def __init__( """ self.command = command self.return_code = return_code - self.output = output + self.output = output or "" self.preview_length = preview_length # Create the error message command_str = " ".join(command) - if output is None: - output_preview = "" - else: - output_preview = textwrap.indent(textwrap.shorten(output, preview_length), " ") + output_preview = textwrap.indent(textwrap.shorten(self.output, preview_length), " ") message = ( f"Command `{command_str}` returned non-zero exit status {return_code}.\n\n" From c0beb7e1342ed4078e00fb2e47e70bc5488532ee Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 6 Jul 2025 11:29:30 +0200 Subject: [PATCH 13/13] Update robot.py --- src/bioontologies/robot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bioontologies/robot.py b/src/bioontologies/robot.py index aa0d67b..92ca942 100644 --- a/src/bioontologies/robot.py +++ b/src/bioontologies/robot.py @@ -9,6 +9,7 @@ import os import subprocess import tempfile +import textwrap from contextlib import contextmanager from dataclasses import dataclass from pathlib import Path