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
12 changes: 4 additions & 8 deletions bionetgen/modelapi/bngfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@
import tempfile
from typing import NoReturn

from bionetgen.main import BioNetGen
from bionetgen.main import get_default_bng_path
from bionetgen.core.exc import BNGFileError
from bionetgen.core.utils.logging import BNGLogger
from bionetgen.core.utils.utils import find_BNG_path, run_command, ActionList

# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]


class BNGFile:
"""
Expand Down Expand Up @@ -45,14 +39,16 @@ class BNGFile:
"""

def __init__(
self, path, BNGPATH=def_bng_path, generate_network=False, suppress=True
self, path, BNGPATH=None, generate_network=False, suppress=True
) -> None:
self.path = path
self.logger = BNGLogger()
self.generate_network = generate_network
self.suppress = suppress
AList = ActionList()
self._action_list = [i + "(" for i in AList.possible_types]
if BNGPATH is None:
BNGPATH = get_default_bng_path()
BNGPATH, bngexec = find_BNG_path(BNGPATH)
self.BNGPATH = BNGPATH
self.bngexec = bngexec
Expand Down
13 changes: 4 additions & 9 deletions bionetgen/modelapi/bngparser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import xmltodict

from bionetgen.main import BioNetGen
from bionetgen.core.exc import BNGFileError, BNGParseError, BNGModelError
from tempfile import TemporaryFile

Expand All @@ -11,12 +10,6 @@
from .blocks import ActionBlock, ProtocolBlock
from bionetgen.core.utils.utils import ActionList

# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]


def _normalize_action_text(action: str) -> str:
"""Strip BNGL comments and unquoted whitespace, keep quoted spans intact.
Expand Down Expand Up @@ -188,13 +181,15 @@ class BNGParser:
def __init__(
self,
path,
BNGPATH=def_bng_path,
BNGPATH=None,
parse_actions=True,
generate_network=False,
suppress=True,
) -> None:
self.to_parse_actions = parse_actions
self.bngfile = BNGFile(path, generate_network=generate_network, suppress=True)
self.bngfile = BNGFile(
path, BNGPATH=BNGPATH, generate_network=generate_network, suppress=suppress
)
self.alist = ActionList()
self.alist.define_parser()

Expand Down
16 changes: 5 additions & 11 deletions bionetgen/modelapi/model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import copy, tempfile, shutil

from bionetgen.main import BioNetGen
from bionetgen.core.exc import BNGFileError, BNGModelError

from .bngparser import BNGParser
Expand All @@ -18,12 +17,6 @@
PopulationMapBlock,
)

# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]


###### CORE OBJECT AND PARSING FRONT-END ######
class bngmodel:
Expand Down Expand Up @@ -73,9 +66,7 @@ class bngmodel:
"population_maps", "rules", "reaction_rules", "actions".
"""

def __init__(
self, bngl_model, BNGPATH=def_bng_path, generate_network=False, suppress=True
):
def __init__(self, bngl_model, BNGPATH=None, generate_network=False, suppress=True):
self.active_blocks = []
# We want blocks to be printed in the same order every time
self._block_order = [
Expand All @@ -94,7 +85,10 @@ def __init__(
self.model_name = ""
self.model_path = bngl_model
self.bngparser = BNGParser(
bngl_model, generate_network=generate_network, suppress=True
bngl_model,
BNGPATH=BNGPATH,
generate_network=generate_network,
suppress=suppress,
)
self.bngparser.parse_model(self)
for block in self._block_order:
Expand Down
9 changes: 1 addition & 8 deletions bionetgen/network/network.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from bionetgen.main import BioNetGen
from bionetgen.network.networkparser import BNGNetworkParser
from bionetgen.network.blocks import (
NetworkGroupBlock,
Expand All @@ -11,12 +10,6 @@
NetworkPopulationMapBlock,
)

# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]


###### CORE OBJECT AND PARSING FRONT-END ######
class Network:
Expand Down Expand Up @@ -46,7 +39,7 @@ class Network:
type of simulator is libRR for libRoadRunner simulator.
"""

def __init__(self, bngl_model, BNGPATH=def_bng_path):
def __init__(self, bngl_model, BNGPATH=None):
self.active_blocks = []
# We want blocks to be printed in the same order every time
self.block_order = [
Expand Down
6 changes: 0 additions & 6 deletions bionetgen/network/networkparser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import re, os
from bionetgen.core.exc import BNGParseError
from bionetgen.core.utils.logging import BNGLogger
from bionetgen.main import BioNetGen
from bionetgen.network.blocks import (
NetworkGroupBlock,
NetworkParameterBlock,
Expand All @@ -13,11 +12,6 @@
NetworkPopulationMapBlock,
)

# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]
logger = BNGLogger()


Expand Down
8 changes: 2 additions & 6 deletions bionetgen/simulator/csimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import numpy as np

from .bngsimulator import BNGSimulator
from bionetgen.main import BioNetGen
from bionetgen.main import get_conf
from bionetgen.core.exc import BNGCompileError, BNGFormatError, BNGSimError
from bionetgen.core.utils.logging import BNGLogger

Expand All @@ -27,11 +27,6 @@ def _new_ccompiler():
return ccompiler.new_compiler()


# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]
logger = BNGLogger()


Expand Down Expand Up @@ -161,6 +156,7 @@ class CSimulator(BNGSimulator):
"""

def __init__(self, model_file, generate_network=False):
conf = get_conf()
# check cvode library paths
if (conf.get("cvode_include") is None) or (conf.get("cvode_lib") is None):
logger.warning(
Expand Down
16 changes: 12 additions & 4 deletions tests/test_csimulator_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ def test_csimulator_init_logs_missing_cvode_paths():
fake_model.species = {}
fake_compiler = mock.MagicMock()
mock_conf_get = mock.MagicMock(side_effect=lambda key: None)
fake_conf = mock.MagicMock()
fake_conf.get = mock_conf_get

def fake_compile(self):
self.lib_file = "/tmp/fake/libcsim.so"

with mock.patch.object(csim_module.conf, "get", mock_conf_get), mock.patch.object(
with mock.patch.object(
csim_module, "get_conf", return_value=fake_conf
) as mock_get_conf, mock.patch.object(
csim_module, "logger"
) as mock_logger, mock.patch.object(
csim_module.bionetgen, "bngmodel", return_value=fake_model
Expand All @@ -29,6 +33,7 @@ def fake_compile(self):
) as mock_wrapper:
csim_module.CSimulator("/fake/model.bngl")

mock_get_conf.assert_called_once_with()
mock_logger.warning.assert_called_once()
warning_args, warning_kwargs = mock_logger.warning.call_args
assert "CVODE include and library paths are not set" in warning_args[0]
Expand All @@ -55,16 +60,19 @@ def test_csimulator_init_invalid_model_type_raises_bng_format_error():
"cvode_lib": "/tmp/lib",
}[key]
)
fake_conf = mock.MagicMock()
fake_conf.get = mock_conf_get

with mock.patch.object(csim_module.conf, "get", mock_conf_get), mock.patch.object(
csim_module, "logger"
) as mock_logger:
with mock.patch.object(
csim_module, "get_conf", return_value=fake_conf
) as mock_get_conf, mock.patch.object(csim_module, "logger") as mock_logger:
with pytest.raises(
BNGFormatError,
match="CSimulator model input must be a BNGL path or bngmodel instance",
):
csim_module.CSimulator(123)

mock_get_conf.assert_called_once_with()
mock_logger.error.assert_called_once()
error_args, error_kwargs = mock_logger.error.call_args
assert "got int" in error_args[0]
Expand Down
71 changes: 71 additions & 0 deletions tests/test_deferred_app_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import subprocess
import sys
import textwrap
from pathlib import Path
from unittest.mock import patch

REPO_ROOT = Path(__file__).resolve().parents[1]


def test_library_imports_do_not_setup_default_app():
script = textwrap.dedent("""
import importlib
from unittest.mock import patch

modules = [
"bionetgen.modelapi.bngfile",
"bionetgen.modelapi.bngparser",
"bionetgen.modelapi.model",
"bionetgen.network.network",
"bionetgen.network.networkparser",
"bionetgen.simulator.csimulator",
]

with patch("cement.core.foundation.App.setup") as setup:
for module in modules:
importlib.import_module(module)

if setup.called:
raise SystemExit(f"App.setup() called during import: {setup.call_args!r}")
""")
result = subprocess.run(
[sys.executable, "-c", script],
cwd=REPO_ROOT,
capture_output=True,
text=True,
check=False,
)

assert result.returncode == 0, result.stdout + result.stderr


def test_bngfile_resolves_default_bngpath_lazily():
from bionetgen.modelapi import bngfile as bngfile_module

with patch.object(
bngfile_module, "get_default_bng_path", return_value="/default/bng"
) as mock_default, patch.object(
bngfile_module,
"find_BNG_path",
return_value=("/resolved/bng", "/resolved/bng/BNG2.pl"),
) as mock_find:
bngfile_module.BNGFile("/some/model.bngl")

mock_default.assert_called_once_with()
mock_find.assert_called_once_with("/default/bng")


def test_bngfile_explicit_bngpath_skips_default_lookup():
from bionetgen.modelapi import bngfile as bngfile_module

with patch.object(
bngfile_module, "get_default_bng_path"
) as mock_default, patch.object(
bngfile_module,
"find_BNG_path",
return_value=("/custom/bng", "/custom/bng/BNG2.pl"),
) as mock_find:
bngfile_module.BNGFile("/some/model.bngl", BNGPATH="/custom/bng")

mock_default.assert_not_called()
mock_find.assert_called_once_with("/custom/bng")
42 changes: 42 additions & 0 deletions tests/test_modelapi_export_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,48 @@ def test_parse_model_xml_generation_failure_wraps_bngfile_error(mock_bngfile_cls
parser.parse_model(MagicMock())


@patch("bionetgen.modelapi.bngparser.BNGFile")
def test_bngparser_passes_path_options_to_bngfile(mock_bngfile_cls):
from bionetgen.modelapi.bngparser import BNGParser

BNGParser(
"/some/model.bngl",
BNGPATH="/custom/bng",
generate_network=True,
suppress=False,
)

mock_bngfile_cls.assert_called_once_with(
"/some/model.bngl",
BNGPATH="/custom/bng",
generate_network=True,
suppress=False,
)


@patch("bionetgen.modelapi.model.BNGParser")
def test_bngmodel_passes_path_options_to_bngparser(mock_parser_cls):
from bionetgen.modelapi.model import bngmodel

parser = MagicMock()
mock_parser_cls.return_value = parser

bngmodel(
"/some/model.bngl",
BNGPATH="/custom/bng",
generate_network=True,
suppress=False,
)

mock_parser_cls.assert_called_once_with(
"/some/model.bngl",
BNGPATH="/custom/bng",
generate_network=True,
suppress=False,
)
parser.parse_model.assert_called_once()


def test_setup_simulator_write_xml_failure_raises_bngmodel_error_and_restores_actions(
tmp_path, monkeypatch
):
Expand Down
Loading