From 82b24610fdd782ac271701e02da2ceb98728b151 Mon Sep 17 00:00:00 2001 From: James Kent Date: Fri, 20 Mar 2026 15:49:31 -0500 Subject: [PATCH 1/5] bump nimare and simplify run.py --- compose_runner/run.py | 25 ++++--- compose_runner/tests/test_run.py | 115 +++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 128 insertions(+), 14 deletions(-) diff --git a/compose_runner/run.py b/compose_runner/run.py index 0b9964c..b943753 100644 --- a/compose_runner/run.py +++ b/compose_runner/run.py @@ -80,8 +80,8 @@ def __init__( self.cached_studyset = None self.cached_annotation = None self.cached_specification = None - self.first_dataset = None - self.second_dataset = None + self.first_studyset = None + self.second_studyset = None self.estimator = None self.corrector = None @@ -292,13 +292,9 @@ def process_bundle(self, n_cores=None): studyset = Studyset(self.cached_studyset) annotation = Annotation(self.cached_annotation, studyset) first_studyset, second_studyset = self.apply_filter(studyset, annotation) - first_dataset = first_studyset.to_dataset() - second_dataset = ( - second_studyset.to_dataset() if second_studyset is not None else None - ) estimator, corrector = self.load_specification(n_cores=n_cores) - self.first_dataset = first_dataset - self.second_dataset = second_dataset + self.first_studyset = first_studyset + self.second_studyset = second_studyset self.estimator = estimator self.corrector = corrector @@ -323,25 +319,28 @@ def create_result_object(self): raise ValueError(f"Could not create result for {self.meta_analysis_id}") def run_meta_analysis(self): - if self.second_dataset and isinstance(self.estimator, PairwiseCBMAEstimator): + if self.second_studyset and isinstance(self.estimator, PairwiseCBMAEstimator): workflow = PairwiseCBMAWorkflow( estimator=self.estimator, corrector=self.corrector, diagnostics="focuscounter", output_dir=self.result_dir, ) - self.meta_results = workflow.fit(self.first_dataset, self.second_dataset) - elif self.second_dataset is None and isinstance(self.estimator, CBMAEstimator): + self.meta_results = workflow.fit( + self.first_studyset, + self.second_studyset, + ) + elif self.second_studyset is None and isinstance(self.estimator, CBMAEstimator): workflow = CBMAWorkflow( estimator=self.estimator, corrector=self.corrector, diagnostics="focuscounter", output_dir=self.result_dir, ) - self.meta_results = workflow.fit(self.first_dataset, self.second_dataset) + self.meta_results = workflow.fit(self.first_studyset) else: raise ValueError( - f"Estimator {self.estimator} and datasets {self.first_dataset} and {self.second_dataset} are not compatible." + f"Estimator {self.estimator} and studysets {self.first_studyset} and {self.second_studyset} are not compatible." ) self._persist_meta_results() diff --git a/compose_runner/tests/test_run.py b/compose_runner/tests/test_run.py index 6ea4e34..87a6586 100644 --- a/compose_runner/tests/test_run.py +++ b/compose_runner/tests/test_run.py @@ -1,6 +1,7 @@ import pytest from requests.exceptions import HTTPError +import compose_runner.run as run_module from compose_runner.run import Runner @@ -62,6 +63,120 @@ def test_run_string_group_comparison_workflow(): ) runner.run_workflow() + +def test_process_bundle_keeps_studysets(monkeypatch): + first_studyset = object() + second_studyset = object() + estimator = object() + corrector = object() + + class FakeStudyset: + def __init__(self, source): + self.source = source + + class FakeAnnotation: + def __init__(self, source, studyset): + self.source = source + self.studyset = studyset + + def fake_apply_filter(self, studyset, annotation): + assert isinstance(studyset, FakeStudyset) + assert isinstance(annotation, FakeAnnotation) + return first_studyset, second_studyset + + def fake_load_specification(self, n_cores=None): + assert n_cores == 3 + return estimator, corrector + + monkeypatch.setattr(run_module, "Studyset", FakeStudyset) + monkeypatch.setattr(run_module, "Annotation", FakeAnnotation) + monkeypatch.setattr(Runner, "apply_filter", fake_apply_filter) + monkeypatch.setattr(Runner, "load_specification", fake_load_specification) + + runner = Runner(meta_analysis_id="made_up_id", environment="staging") + runner.cached_studyset = {"id": "studyset", "studies": []} + runner.cached_annotation = {"note_keys": {}} + + runner.process_bundle(n_cores=3) + + assert runner.first_studyset is first_studyset + assert runner.second_studyset is second_studyset + assert runner.estimator is estimator + assert runner.corrector is corrector + + +def test_run_meta_analysis_single_studyset_uses_cbma_workflow(monkeypatch, tmp_path): + calls = {} + + class FakeCBMAEstimator: + pass + + class FakeWorkflow: + def __init__(self, estimator, corrector, diagnostics, output_dir): + calls["init"] = { + "estimator": estimator, + "corrector": corrector, + "diagnostics": diagnostics, + "output_dir": output_dir, + } + + def fit(self, dataset): + calls["fit"] = {"dataset": dataset} + return "meta-results" + + monkeypatch.setattr(run_module, "CBMAEstimator", FakeCBMAEstimator) + monkeypatch.setattr(run_module, "CBMAWorkflow", FakeWorkflow) + + runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner.first_studyset = object() + runner.second_studyset = None + runner.estimator = FakeCBMAEstimator() + runner.corrector = object() + runner._persist_meta_results = lambda: None + + runner.run_meta_analysis() + + assert calls["fit"] == {"dataset": runner.first_studyset} + assert runner.meta_results == "meta-results" + + +def test_run_meta_analysis_pairwise_uses_pairwise_workflow(monkeypatch, tmp_path): + calls = {} + + class FakePairwiseEstimator: + pass + + class FakeWorkflow: + def __init__(self, estimator, corrector, diagnostics, output_dir): + calls["init"] = { + "estimator": estimator, + "corrector": corrector, + "diagnostics": diagnostics, + "output_dir": output_dir, + } + + def fit(self, dataset1, dataset2): + calls["fit"] = {"dataset1": dataset1, "dataset2": dataset2} + return "pairwise-results" + + monkeypatch.setattr(run_module, "PairwiseCBMAEstimator", FakePairwiseEstimator) + monkeypatch.setattr(run_module, "PairwiseCBMAWorkflow", FakeWorkflow) + + runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner.first_studyset = object() + runner.second_studyset = object() + runner.estimator = FakePairwiseEstimator() + runner.corrector = object() + runner._persist_meta_results = lambda: None + + runner.run_meta_analysis() + + assert calls["fit"] == { + "dataset1": runner.first_studyset, + "dataset2": runner.second_studyset, + } + assert runner.meta_results == "pairwise-results" + # def test_yifan_workflow(): # runner = Runner( # meta_analysis_id="4WELjap2yCJm", diff --git a/pyproject.toml b/pyproject.toml index 2ab3e57..e1c44a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ classifiers = [ "Programming Language :: Python :: 3", ] dynamic = ["version"] -dependencies = ["nimare>=0.11.0", "click", "sentry-sdk", "numpy"] +dependencies = ["nimare>=0.11.1", "click", "sentry-sdk", "numpy"] [project.urls] Repository = "https://github.com/neurostuff/compose-runner" From be3920fe0765726b418c3915a64350943a392c4c Mon Sep 17 00:00:00 2001 From: James Kent Date: Fri, 20 Mar 2026 16:11:47 -0500 Subject: [PATCH 2/5] do not depend on staging being up --- compose_runner/tests/test_cli.py | 46 +++++++++++++++++++++++++++----- compose_runner/tests/test_run.py | 27 +++++++++---------- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/compose_runner/tests/test_cli.py b/compose_runner/tests/test_cli.py index 09749e6..57561d2 100644 --- a/compose_runner/tests/test_cli.py +++ b/compose_runner/tests/test_cli.py @@ -1,13 +1,47 @@ from click.testing import CliRunner +import compose_runner.cli as cli_module from compose_runner.cli import cli -def test_cli(): +def test_cli(monkeypatch): + calls = {} + + def fake_run(meta_analysis_id, environment, result_dir, nsc_key, nv_key, no_upload, n_cores): + calls["args"] = { + "meta_analysis_id": meta_analysis_id, + "environment": environment, + "result_dir": result_dir, + "nsc_key": nsc_key, + "nv_key": nv_key, + "no_upload": no_upload, + "n_cores": n_cores, + } + return "https://example.org/result", None + + monkeypatch.setattr(cli_module, "run", fake_run) + runner = CliRunner() - result = runner.invoke(cli, [ - "4nBwrGsqVWtt", - '--environment', "staging", - "--n-cores", 1, - "--no-upload"]) + result = runner.invoke( + cli, + [ + "3opENJpHxRsH", + "--environment", + "staging", + "--n-cores", + 1, + "--no-upload", + ], + ) + assert result.exit_code == 0 + assert calls["args"] == { + "meta_analysis_id": "3opENJpHxRsH", + "environment": "staging", + "result_dir": None, + "nsc_key": None, + "nv_key": None, + "no_upload": True, + "n_cores": 1, + } + assert "https://example.org/result" in result.output diff --git a/compose_runner/tests/test_run.py b/compose_runner/tests/test_run.py index 87a6586..f937f88 100644 --- a/compose_runner/tests/test_run.py +++ b/compose_runner/tests/test_run.py @@ -2,12 +2,11 @@ from requests.exceptions import HTTPError import compose_runner.run as run_module -from compose_runner.run import Runner @pytest.mark.vcr(record_mode="once") def test_incorrect_id(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="made_up_id", environment="staging", ) @@ -18,7 +17,7 @@ def test_incorrect_id(): @pytest.mark.vcr(record_mode="once") def test_download_bundle(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="3opENJpHxRsH", environment="staging", ) @@ -30,7 +29,7 @@ def test_download_bundle(): @pytest.mark.vcr(record_mode="once") def test_run_workflow(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="3opENJpHxRsH", environment="staging", ) @@ -39,7 +38,7 @@ def test_run_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_database_workflow(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="dRFtnAo9bhp3", environment="staging", ) @@ -48,7 +47,7 @@ def test_run_database_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_group_comparison_workflow(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="4CGQSSyaoWN3", environment="staging", ) @@ -57,7 +56,7 @@ def test_run_group_comparison_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_string_group_comparison_workflow(): - runner = Runner( + runner = run_module.Runner( meta_analysis_id="7joU2Siajs5X", environment="staging", ) @@ -90,10 +89,10 @@ def fake_load_specification(self, n_cores=None): monkeypatch.setattr(run_module, "Studyset", FakeStudyset) monkeypatch.setattr(run_module, "Annotation", FakeAnnotation) - monkeypatch.setattr(Runner, "apply_filter", fake_apply_filter) - monkeypatch.setattr(Runner, "load_specification", fake_load_specification) + monkeypatch.setattr(run_module.Runner, "apply_filter", fake_apply_filter) + monkeypatch.setattr(run_module.Runner, "load_specification", fake_load_specification) - runner = Runner(meta_analysis_id="made_up_id", environment="staging") + runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging") runner.cached_studyset = {"id": "studyset", "studies": []} runner.cached_annotation = {"note_keys": {}} @@ -127,7 +126,7 @@ def fit(self, dataset): monkeypatch.setattr(run_module, "CBMAEstimator", FakeCBMAEstimator) monkeypatch.setattr(run_module, "CBMAWorkflow", FakeWorkflow) - runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) runner.first_studyset = object() runner.second_studyset = None runner.estimator = FakeCBMAEstimator() @@ -162,7 +161,7 @@ def fit(self, dataset1, dataset2): monkeypatch.setattr(run_module, "PairwiseCBMAEstimator", FakePairwiseEstimator) monkeypatch.setattr(run_module, "PairwiseCBMAWorkflow", FakeWorkflow) - runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) runner.first_studyset = object() runner.second_studyset = object() runner.estimator = FakePairwiseEstimator() @@ -178,7 +177,7 @@ def fit(self, dataset1, dataset2): assert runner.meta_results == "pairwise-results" # def test_yifan_workflow(): -# runner = Runner( +# runner = run_module.Runner( # meta_analysis_id="4WELjap2yCJm", # ) # runner.run_workflow() @@ -186,7 +185,7 @@ def fit(self, dataset1, dataset2): # @pytest.mark.vcr(record_mode="once") # def test_mkdachis_comparison_workflow(): -# runner = Runner( +# runner = run_module.Runner( # meta_analysis_id="6Grzwzs3t7YB", # ) # runner.run_workflow() From 36a6ba84b8bf017f077177c123141235dd25c4c8 Mon Sep 17 00:00:00 2001 From: James Kent Date: Fri, 20 Mar 2026 16:14:55 -0500 Subject: [PATCH 3/5] fix the imports --- compose_runner/tests/test_cli.py | 3 +-- compose_runner/tests/test_run.py | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/compose_runner/tests/test_cli.py b/compose_runner/tests/test_cli.py index 57561d2..4dbe36a 100644 --- a/compose_runner/tests/test_cli.py +++ b/compose_runner/tests/test_cli.py @@ -1,6 +1,5 @@ from click.testing import CliRunner -import compose_runner.cli as cli_module from compose_runner.cli import cli @@ -19,7 +18,7 @@ def fake_run(meta_analysis_id, environment, result_dir, nsc_key, nv_key, no_uplo } return "https://example.org/result", None - monkeypatch.setattr(cli_module, "run", fake_run) + monkeypatch.setattr(cli, "run", fake_run) runner = CliRunner() result = runner.invoke( diff --git a/compose_runner/tests/test_run.py b/compose_runner/tests/test_run.py index f937f88..7210750 100644 --- a/compose_runner/tests/test_run.py +++ b/compose_runner/tests/test_run.py @@ -1,12 +1,11 @@ import pytest from requests.exceptions import HTTPError -import compose_runner.run as run_module - +from compose_runner.run import Runner @pytest.mark.vcr(record_mode="once") def test_incorrect_id(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="made_up_id", environment="staging", ) @@ -17,7 +16,7 @@ def test_incorrect_id(): @pytest.mark.vcr(record_mode="once") def test_download_bundle(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="3opENJpHxRsH", environment="staging", ) @@ -29,7 +28,7 @@ def test_download_bundle(): @pytest.mark.vcr(record_mode="once") def test_run_workflow(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="3opENJpHxRsH", environment="staging", ) @@ -38,7 +37,7 @@ def test_run_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_database_workflow(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="dRFtnAo9bhp3", environment="staging", ) @@ -47,7 +46,7 @@ def test_run_database_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_group_comparison_workflow(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="4CGQSSyaoWN3", environment="staging", ) @@ -56,7 +55,7 @@ def test_run_group_comparison_workflow(): @pytest.mark.vcr(record_mode="once") def test_run_string_group_comparison_workflow(): - runner = run_module.Runner( + runner = Runner( meta_analysis_id="7joU2Siajs5X", environment="staging", ) @@ -89,10 +88,10 @@ def fake_load_specification(self, n_cores=None): monkeypatch.setattr(run_module, "Studyset", FakeStudyset) monkeypatch.setattr(run_module, "Annotation", FakeAnnotation) - monkeypatch.setattr(run_module.Runner, "apply_filter", fake_apply_filter) - monkeypatch.setattr(run_module.Runner, "load_specification", fake_load_specification) + monkeypatch.setattr(Runner, "apply_filter", fake_apply_filter) + monkeypatch.setattr(Runner, "load_specification", fake_load_specification) - runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging") + runner = Runner(meta_analysis_id="made_up_id", environment="staging") runner.cached_studyset = {"id": "studyset", "studies": []} runner.cached_annotation = {"note_keys": {}} @@ -126,7 +125,7 @@ def fit(self, dataset): monkeypatch.setattr(run_module, "CBMAEstimator", FakeCBMAEstimator) monkeypatch.setattr(run_module, "CBMAWorkflow", FakeWorkflow) - runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) runner.first_studyset = object() runner.second_studyset = None runner.estimator = FakeCBMAEstimator() @@ -161,7 +160,7 @@ def fit(self, dataset1, dataset2): monkeypatch.setattr(run_module, "PairwiseCBMAEstimator", FakePairwiseEstimator) monkeypatch.setattr(run_module, "PairwiseCBMAWorkflow", FakeWorkflow) - runner = run_module.Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) + runner = Runner(meta_analysis_id="made_up_id", environment="staging", result_dir=tmp_path) runner.first_studyset = object() runner.second_studyset = object() runner.estimator = FakePairwiseEstimator() @@ -177,7 +176,7 @@ def fit(self, dataset1, dataset2): assert runner.meta_results == "pairwise-results" # def test_yifan_workflow(): -# runner = run_module.Runner( +# runner = Runner( # meta_analysis_id="4WELjap2yCJm", # ) # runner.run_workflow() @@ -185,7 +184,7 @@ def fit(self, dataset1, dataset2): # @pytest.mark.vcr(record_mode="once") # def test_mkdachis_comparison_workflow(): -# runner = run_module.Runner( +# runner = Runner( # meta_analysis_id="6Grzwzs3t7YB", # ) # runner.run_workflow() From dcbb64360402370c0687d907a58ae9e3490d2e3d Mon Sep 17 00:00:00 2001 From: James Kent Date: Fri, 20 Mar 2026 16:18:33 -0500 Subject: [PATCH 4/5] monkeypatch correctly --- compose_runner/tests/test_run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compose_runner/tests/test_run.py b/compose_runner/tests/test_run.py index 7210750..dcf685f 100644 --- a/compose_runner/tests/test_run.py +++ b/compose_runner/tests/test_run.py @@ -1,6 +1,7 @@ import pytest from requests.exceptions import HTTPError +from compose_runner import run as run_module from compose_runner.run import Runner @pytest.mark.vcr(record_mode="once") From 0b04293b4062591fcf198f3c7c82695b9320025d Mon Sep 17 00:00:00 2001 From: James Kent Date: Fri, 20 Mar 2026 16:21:39 -0500 Subject: [PATCH 5/5] use monkeypatch correctly --- compose_runner/tests/test_cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compose_runner/tests/test_cli.py b/compose_runner/tests/test_cli.py index 4dbe36a..af52626 100644 --- a/compose_runner/tests/test_cli.py +++ b/compose_runner/tests/test_cli.py @@ -1,5 +1,6 @@ from click.testing import CliRunner +from compose_runner import cli as cli_module from compose_runner.cli import cli @@ -18,7 +19,7 @@ def fake_run(meta_analysis_id, environment, result_dir, nsc_key, nv_key, no_uplo } return "https://example.org/result", None - monkeypatch.setattr(cli, "run", fake_run) + monkeypatch.setattr(cli_module, "run", fake_run) runner = CliRunner() result = runner.invoke(