diff --git a/packages/python/openproblems/CHANGELOG.md b/packages/python/openproblems/CHANGELOG.md index ffdddac..60a2071 100644 --- a/packages/python/openproblems/CHANGELOG.md +++ b/packages/python/openproblems/CHANGELOG.md @@ -22,6 +22,8 @@ * Improve diagnostic print messages in `check_config` and `run_and_check_output` to be more descriptive. +* `check_config`: Skip the Nextflow resource label check for components whose script is itself a Nextflow workflow. Viash renders those as a workflow rather than a process, so the labels would have no effect. + # openproblems core Python v0.1.1 ## NEW FUNCTIONALITY diff --git a/packages/python/openproblems/src/openproblems/project/component_tests/check_config.py b/packages/python/openproblems/src/openproblems/project/component_tests/check_config.py index d3dacc1..7bae60a 100644 --- a/packages/python/openproblems/src/openproblems/project/component_tests/check_config.py +++ b/packages/python/openproblems/src/openproblems/project/component_tests/check_config.py @@ -167,27 +167,37 @@ def check_config(config: dict) -> None: runners = config.get("runners", []) - print("Check that a Nextflow runner with time, mem, and cpu labels is defined", flush=True) + print("Check that a Nextflow runner is defined", flush=True) nextflow_runner = next( (runner for runner in runners if runner["type"] == "nextflow"), None, ) assert nextflow_runner, ".runners does not contain a nextflow runner" - assert nextflow_runner.get( - "directives" - ), "directives not a field in nextflow runner" - nextflow_labels = nextflow_runner["directives"].get("label") - assert nextflow_labels, "label not a field in nextflow runner directives" - - assert [ - label for label in nextflow_labels if label in TIME_LABELS - ], "time label not filled in" - assert [ - label for label in nextflow_labels if label in MEM_LABELS - ], "mem label not filled in" - assert [ - label for label in nextflow_labels if label in CPU_LABELS - ], "cpu label not filled in" + + # a component whose script is itself a Nextflow workflow is rendered as a + # workflow instead of a process, so resource directives have no effect on it + is_nextflow_workflow = any( + resource.get("type") == "nextflow_script" + for resource in config.get("resources") or [] + ) + + if not is_nextflow_workflow: + print("Check that the Nextflow runner has time, mem, and cpu labels", flush=True) + assert nextflow_runner.get( + "directives" + ), "directives not a field in nextflow runner" + nextflow_labels = nextflow_runner["directives"].get("label") + assert nextflow_labels, "label not a field in nextflow runner directives" + + assert [ + label for label in nextflow_labels if label in TIME_LABELS + ], "time label not filled in" + assert [ + label for label in nextflow_labels if label in MEM_LABELS + ], "mem label not filled in" + assert [ + label for label in nextflow_labels if label in CPU_LABELS + ], "cpu label not filled in" print("All checks succeeded!", flush=True) diff --git a/packages/python/openproblems/tests/test_project_check_config.py b/packages/python/openproblems/tests/test_project_check_config.py new file mode 100644 index 0000000..67da399 --- /dev/null +++ b/packages/python/openproblems/tests/test_project_check_config.py @@ -0,0 +1,67 @@ +from typing import Any, Dict + +import pytest + +from openproblems.project import check_config + + +def _config(**kwargs: Any) -> Dict[str, Any]: + # a control method without links or references, so that check_config + # does not need network access to validate the metadata + config: Dict[str, Any] = { + "name": "foo", + "label": "Foo", + "summary": "A foo control method.", + "description": "A foo control method.", + "namespace": "control_methods", + "info": {"type": "control_method"}, + "runners": [ + { + "type": "nextflow", + "directives": {"label": ["lowtime", "lowmem", "lowcpu"]}, + } + ], + } + config.update(kwargs) + return config + + +def test_check_config_accepts_resource_labels(): + check_config(_config()) + + +def test_check_config_requires_a_nextflow_runner(): + with pytest.raises(AssertionError, match="does not contain a nextflow runner"): + check_config(_config(runners=[{"type": "executable"}])) + + +def test_check_config_requires_resource_labels(): + with pytest.raises(AssertionError, match="directives not a field"): + check_config(_config(runners=[{"type": "nextflow"}])) + + runners = [{"type": "nextflow", "directives": {"tag": "$id"}}] + with pytest.raises(AssertionError, match="label not a field"): + check_config(_config(runners=runners)) + + +def test_check_config_requires_a_label_of_each_kind(): + for labels, message in [ + (["lowmem", "lowcpu"], "time label not filled in"), + (["lowtime", "lowcpu"], "mem label not filled in"), + (["lowtime", "lowmem"], "cpu label not filled in"), + ]: + runners = [{"type": "nextflow", "directives": {"label": labels}}] + with pytest.raises(AssertionError, match=message): + check_config(_config(runners=runners)) + + +def test_check_config_skips_resource_labels_for_nextflow_workflows(): + # viash renders a nextflow_script component as a workflow rather than a + # process, so it has nothing to attach resource labels to + resources = [{"type": "nextflow_script", "path": "main.nf", "entrypoint": "run_wf"}] + check_config( + _config( + resources=resources, + runners=[{"type": "nextflow"}], + ) + )