Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 50 additions & 39 deletions bionetgen/core/tools/visualize.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os, bionetgen, glob
from tempfile import TemporaryDirectory
from typing import NoReturn, Optional

from bionetgen.core.exc import BNGError, BNGFileError
from bionetgen.core.utils.logging import BNGLogger


Expand Down Expand Up @@ -135,7 +137,7 @@ def run(self) -> VisResult:
self.logger.debug("Running", loc=f"{__file__} : BNGVisualize.run()")
return self._normal_mode()

def _normal_mode(self):
def _normal_mode(self) -> VisResult:
self.logger.debug(
f"Running on normal mode, loading model {self.input}",
loc=f"{__file__} : BNGVisualize._normal_mode()",
Expand Down Expand Up @@ -182,42 +184,51 @@ def _normal_mode(self):
with TemporaryDirectory() as out:
# instantiate a CLI object with the info
cli = BNGCLI(model, out, self.bngpath, suppress=self.suppress)
try:
cli.run()
# load vis
vis_res = VisResult(
os.path.abspath(os.getcwd()),
name=model.model_name,
vtype=self.vtype,
)
# go back
os.chdir(cur_dir)
# dump files
vis_res._dump_files(cur_dir)
return vis_res
except Exception as e:
os.chdir(cur_dir)
print("Couldn't run the simulation, see error.")
raise e
else:
# instantiate a CLI object with the info
cli = BNGCLI(model, self.output, self.bngpath, suppress=self.suppress)
try:
cli.run()
# load vis
vis_res = VisResult(
os.path.abspath(os.getcwd()),
name=model.model_name,
vtype=self.vtype,
)
# go back
os.chdir(cur_dir)
return vis_res
except Exception as e:
self.logger.error(
"Failed to run file",
loc=f"{__file__} : BNGVisualize._normal_mode()",
return self._run_and_collect_vis(
cli,
model_name=model.model_name,
cur_dir=cur_dir,
dump_dir=cur_dir,
)
os.chdir(cur_dir)
print("Couldn't run the simulation, see error.")
raise e

# instantiate a CLI object with the info
cli = BNGCLI(model, self.output, self.bngpath, suppress=self.suppress)
return self._run_and_collect_vis(
cli,
model_name=model.model_name,
cur_dir=cur_dir,
)

def _run_and_collect_vis(
self, cli, *, model_name: str, cur_dir: str, dump_dir: Optional[str] = None
) -> VisResult:
try:
cli.run()
vis_res = VisResult(
os.path.abspath(os.getcwd()),
name=model_name,
vtype=self.vtype,
)
if dump_dir is not None:
vis_res._dump_files(dump_dir)
return vis_res
except BNGError as exc:
self._log_visualization_failure(exc)
raise
except OSError as exc:
self._raise_visualization_file_error(exc)
finally:
os.chdir(cur_dir)

def _log_visualization_failure(self, exc: BaseException) -> None:
self.logger.error(
f"Failed to generate visualization files: {exc}",
loc=f"{__file__} : BNGVisualize._normal_mode()",
)

def _raise_visualization_file_error(self, exc: OSError) -> NoReturn:
self._log_visualization_failure(exc)
raise BNGFileError(
self.input,
message=f"Failed to generate visualization files: {exc}",
) from exc
66 changes: 66 additions & 0 deletions tests/test_visualize_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from unittest import mock

import pytest

from bionetgen.core.exc import BNGFileError, BNGRunError


@pytest.mark.parametrize("use_output", [False, True])
def test_normal_mode_logs_and_reraises_cli_failures(tmp_path, capsys, use_output):
from bionetgen.core.tools.visualize import BNGVisualize

fake_model = mock.MagicMock()
fake_model.model_name = "test_model"
output = str(tmp_path / "viz") if use_output else None
visualize = BNGVisualize("test.bngl", output=output)
visualize.logger = mock.MagicMock()

with mock.patch(
"bionetgen.core.tools.visualize.bionetgen.modelapi.bngmodel",
return_value=fake_model,
), mock.patch("bionetgen.core.main.BNGCLI") as mock_cli_cls:
mock_cli_cls.return_value.run.side_effect = BNGRunError(
["perl", "BNG2.pl", "test.bngl"],
message="boom",
)

with pytest.raises(BNGRunError, match="boom"):
visualize._normal_mode()

captured = capsys.readouterr()
assert captured.out == ""
visualize.logger.error.assert_called_once()
error_args, error_kwargs = visualize.logger.error.call_args
assert error_args[0].startswith("Failed to generate visualization files:")
assert "boom" in error_args[0]
assert "BNGVisualize._normal_mode()" in error_kwargs["loc"]


def test_normal_mode_wraps_dump_failures(capsys):
from bionetgen.core.tools.visualize import BNGVisualize

fake_model = mock.MagicMock()
fake_model.model_name = "test_model"
fake_vis_result = mock.MagicMock()
fake_vis_result._dump_files.side_effect = OSError("disk full")
visualize = BNGVisualize("test.bngl")
visualize.logger = mock.MagicMock()

with mock.patch(
"bionetgen.core.tools.visualize.bionetgen.modelapi.bngmodel",
return_value=fake_model,
), mock.patch("bionetgen.core.main.BNGCLI"), mock.patch(
"bionetgen.core.tools.visualize.VisResult",
return_value=fake_vis_result,
):
with pytest.raises(
BNGFileError, match="Failed to generate visualization files: disk full"
):
visualize._normal_mode()

captured = capsys.readouterr()
assert captured.out == ""
visualize.logger.error.assert_called_once()
error_args, error_kwargs = visualize.logger.error.call_args
assert "Failed to generate visualization files: disk full" in error_args[0]
assert "BNGVisualize._normal_mode()" in error_kwargs["loc"]
Loading