From 010f8cd42a97ba869abed521025d12c05b7d2b29 Mon Sep 17 00:00:00 2001 From: Bill Hlavacek Date: Mon, 11 May 2026 14:23:37 -0600 Subject: [PATCH 1/2] Add cached app/config accessors in bionetgen.main --- bionetgen/main.py | 20 ++++++++++++++++ tests/test_main_accessors.py | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 tests/test_main_accessors.py diff --git a/bionetgen/main.py b/bionetgen/main.py index 2d8be05d..56fbbeee 100644 --- a/bionetgen/main.py +++ b/bionetgen/main.py @@ -1,3 +1,5 @@ +import functools + import cement import bionetgen as bng from cement.core.exc import CaughtSignal @@ -625,6 +627,24 @@ class Meta: label = "bionetgen" +@functools.cache +def get_default_app(): + """Return a configured BioNetGen cement app, initialized once per process.""" + app = BioNetGen() + app.setup() + return app + + +def get_conf(): + """Return the bionetgen config section from the shared app.""" + return get_default_app().config["bionetgen"] + + +def get_default_bng_path(): + """Return the default BNG2.pl path from config.""" + return get_conf()["bngpath"] + + def main(): with BioNetGen() as app: try: diff --git a/tests/test_main_accessors.py b/tests/test_main_accessors.py new file mode 100644 index 00000000..73aa17f8 --- /dev/null +++ b/tests/test_main_accessors.py @@ -0,0 +1,45 @@ +from unittest.mock import MagicMock, patch + + +def test_get_default_app_is_cached(): + from bionetgen import main as main_module + + main_module.get_default_app.cache_clear() + fake_app = MagicMock() + + with patch.object(main_module, "BioNetGen", return_value=fake_app) as mock_app_cls: + app1 = main_module.get_default_app() + app2 = main_module.get_default_app() + + assert app1 is fake_app + assert app2 is fake_app + mock_app_cls.assert_called_once_with() + fake_app.setup.assert_called_once_with() + main_module.get_default_app.cache_clear() + + +def test_get_conf_uses_cached_default_app(): + from bionetgen import main as main_module + + main_module.get_default_app.cache_clear() + fake_app = MagicMock() + fake_app.config = {"bionetgen": {"bngpath": "/fake/BNG2.pl", "other": "value"}} + + with patch.object(main_module, "BioNetGen", return_value=fake_app) as mock_app_cls: + conf1 = main_module.get_conf() + conf2 = main_module.get_conf() + + assert conf1 == {"bngpath": "/fake/BNG2.pl", "other": "value"} + assert conf2 is conf1 + mock_app_cls.assert_called_once_with() + fake_app.setup.assert_called_once_with() + main_module.get_default_app.cache_clear() + + +def test_get_default_bng_path_reads_bngpath_from_config(): + from bionetgen import main as main_module + + with patch.object( + main_module, "get_conf", return_value={"bngpath": "/tmp/BNG2.pl"} + ): + assert main_module.get_default_bng_path() == "/tmp/BNG2.pl" From cdf979ef0950fb10dd44b346e302d40ed5f1219c Mon Sep 17 00:00:00 2001 From: Bill Hlavacek Date: Mon, 11 May 2026 15:26:51 -0600 Subject: [PATCH 2/2] Use lru_cache for Python 3.8 compatibility --- bionetgen/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bionetgen/main.py b/bionetgen/main.py index 56fbbeee..51c58f07 100644 --- a/bionetgen/main.py +++ b/bionetgen/main.py @@ -627,7 +627,7 @@ class Meta: label = "bionetgen" -@functools.cache +@functools.lru_cache(maxsize=None) def get_default_app(): """Return a configured BioNetGen cement app, initialized once per process.""" app = BioNetGen()