From 0f3491b278c023191d1e6068baea36e09b033333 Mon Sep 17 00:00:00 2001 From: Peter Van Dyken Date: Fri, 26 Jul 2024 11:01:03 -0400 Subject: [PATCH] Deduplicate wildcards when parsing components Specifying a wildcard multiple times leads to an unintuitive KeyError. This includes re-specifying a wildcard defined in the config by the developer. Fix by using a set to deduplicate wildcards before parsing. Resolves #422 --- snakebids/core/input_generation.py | 2 +- snakebids/tests/test_generate_inputs.py | 29 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/snakebids/core/input_generation.py b/snakebids/core/input_generation.py index 2d8ee21f..c3f8f60f 100644 --- a/snakebids/core/input_generation.py +++ b/snakebids/core/input_generation.py @@ -599,7 +599,7 @@ def _get_component( for img in matching_files: wildcards: list[str] = [ wildcard - for wildcard in component.get("wildcards", []) + for wildcard in set(component.get("wildcards", [])) if wildcard in img.entities ] _logger.debug("Wildcards %s found entities for %s", wildcards, img.path) diff --git a/snakebids/tests/test_generate_inputs.py b/snakebids/tests/test_generate_inputs.py index 73963292..2f989a71 100644 --- a/snakebids/tests/test_generate_inputs.py +++ b/snakebids/tests/test_generate_inputs.py @@ -840,6 +840,35 @@ def test_filter_with_invalid_method_raises_error(self, tmpdir: Path, method: str generate_inputs(tmpdir, pybids_inputs) +@settings( + deadline=800, + suppress_health_check=[ + HealthCheck.function_scoped_fixture, + HealthCheck.too_slow, + ], + max_examples=1, +) +@given(dataset=sb_st.datasets_one_comp(unique=True)) +def test_duplicate_wildcards_does_not_create_error( + dataset: BidsDataset, bids_fs: Path, fakefs_tmpdir: Path +): + root = tempfile.mkdtemp(dir=fakefs_tmpdir) + rooted = BidsDataset.from_iterable( + attrs.evolve(comp, path=os.path.join(root, comp.path)) + for comp in dataset.values() + ) + create_dataset(Path("/"), rooted) + config = create_snakebids_config(dataset) + wildcards = itx.first(config.values()).get("wildcards", []) + wildcards.append(wildcards[0]) + reindexed = generate_inputs( + root, + config, + ) + assert reindexed == rooted + assert reindexed.layout is not None + + class TestAbsentConfigEntries: def get_entities(self, root: Path): # Generate directory