diff --git a/python/activator/config.py b/python/activator/config.py index 0eb17e73..f39ec44f 100644 --- a/python/activator/config.py +++ b/python/activator/config.py @@ -45,7 +45,7 @@ class PipelinesConfig: A sequence of mappings ("nodes"), each with the following keys: ``"survey"`` - The survey that triggers the pipelines (`str`). + The survey that triggers the pipelines (`str`, optional). ``"pipelines"`` A list of zero or more pipelines (sequence [`str`] or `None`). Each pipeline path may contain environment variables, and the list of @@ -53,7 +53,8 @@ class PipelinesConfig: be run. Nodes are arranged by precedence, i.e., the first node that matches a - visit is used. + visit is used. A node containing only ``pipelines`` is unconstrained + and matches *any* visit. Examples -------- @@ -112,8 +113,8 @@ def __init__(self, config: collections.abc.Mapping[str, typing.Any]): raise ValueError(f"Pipelines spec must be list or None, got {pipelines_value}") self._check_pipelines(self._filenames) - survey_value = specs.pop('survey') - if isinstance(survey_value, str): + survey_value = specs.pop('survey', None) + if isinstance(survey_value, str) or survey_value is None: self._survey = survey_value else: raise ValueError(f"{survey_value} is not a valid survey name.") @@ -158,7 +159,9 @@ def matches(self, visit: FannedOutVisit) -> bool: matches : `bool` `True` if the visit meets all conditions, `False` otherwise. """ - return self._survey == visit.survey + if self._survey is not None and self._survey != visit.survey: + return False + return True @property def pipeline_files(self) -> collections.abc.Sequence[str]: diff --git a/tests/test_config.py b/tests/test_config.py index 72e650d5..c831ea13 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -152,6 +152,12 @@ def test_duplicates(self): PipelinesConfig([{"survey": "TestSurvey", "pipelines": ["/etc/pipelines/ApPipe.yaml", "/etc/pipelines/ApPipe.yaml#isr"]}]) + def test_needpipeline(self): + with self.assertRaises(ValueError): + PipelinesConfig([{"survey": "TestSurvey"}]) + with self.assertRaises(ValueError): + PipelinesConfig([{}]) + def test_order(self): config = PipelinesConfig([{"survey": "TestSurvey", "pipelines": ["/etc/pipelines/SingleFrame.yaml"], @@ -165,3 +171,25 @@ def test_order(self): config.get_pipeline_files(self.visit), [os.path.normpath(os.path.join("/etc", "pipelines", "SingleFrame.yaml"))] ) + + def test_unconstrained(self): + config = PipelinesConfig([{"survey": "TestSurvey", + "pipelines": ["/etc/pipelines/SingleFrame.yaml"], + }, + {"pipelines": ["${AP_PIPE_DIR}/pipelines/Isr.yaml"]}, + {"survey": "", "pipelines": ["Default.yaml"]}, + ]) + self.assertEqual( + config.get_pipeline_files(self.visit), + [os.path.normpath(os.path.join("/etc", "pipelines", "SingleFrame.yaml"))] + ) + self.assertEqual( + # Matches the second node, which has no constraints + config.get_pipeline_files(dataclasses.replace(self.visit, survey="CameraTest")), + [os.path.normpath(os.path.join(getPackageDir("ap_pipe"), "pipelines", "Isr.yaml"))] + ) + self.assertEqual( + # Matches the second node, which has no constraints + config.get_pipeline_files(dataclasses.replace(self.visit, survey="")), + [os.path.normpath(os.path.join(getPackageDir("ap_pipe"), "pipelines", "Isr.yaml"))] + )