Skip to content

Commit faadb21

Browse files
mashehunf-core-bot
andauthored
use same logic for super-tool selection in modules lint and bump-version (#3823)
Co-authored-by: nf-core-bot <[email protected]>
1 parent 3afb111 commit faadb21

File tree

5 files changed

+86
-8
lines changed

5 files changed

+86
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
- Support modules with `exec:` blocks ([#3633](https://github.com/nf-core/tools/pull/3633))
3232
- feat: nf-core modules bump-version supports specifying the toolkit ([#3608](https://github.com/nf-core/tools/pull/3608))
33+
- use same logic for super-tool selection in modules lint and bump-version ([#3823](https://github.com/nf-core/tools/pull/3823))
3334
- Override example keywords in modules test ([#3801](https://github.com/nf-core/tools/pull/3801))
3435
- update test assertions in modules template to current recommendations and remove `single_end` from example meta value ([#3815](https://github.com/nf-core/tools/pull/3815))
3536

nf_core/modules/bump_versions.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,7 @@ def bump_versions(
116116
raise nf_core.modules.modules_utils.ModuleExceptionError(
117117
"You cannot specify a tool and request all tools to be bumped."
118118
)
119-
# First try to find an exact match
120-
exact_matches = [m for m in nfcore_modules if m.component_name == module]
121-
if exact_matches:
122-
nfcore_modules = exact_matches
123-
else:
124-
# If no exact match, look for modules that start with the given name (subtools)
125-
nfcore_modules = [m for m in nfcore_modules if m.component_name.startswith(module + "/")]
119+
nfcore_modules = nf_core.modules.modules_utils.filter_modules_by_name(nfcore_modules, module)
126120

127121
if len(nfcore_modules) == 0:
128122
raise nf_core.modules.modules_utils.ModuleExceptionError(

nf_core/modules/lint/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def lint(
141141
if all_modules:
142142
raise LintExceptionError("You cannot specify a tool and request all tools to be linted.")
143143
local_modules = []
144-
remote_modules = [m for m in self.all_remote_components if m.component_name == module]
144+
remote_modules = nf_core.modules.modules_utils.filter_modules_by_name(self.all_remote_components, module)
145145
if len(remote_modules) == 0:
146146
raise LintExceptionError(f"Could not find the specified module: '{module}'")
147147
else:

nf_core/modules/modules_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,22 @@ def load_edam():
115115
if extension not in edam_formats:
116116
edam_formats[extension] = (fields[0], fields[1]) # URL, name
117117
return edam_formats
118+
119+
120+
def filter_modules_by_name(modules: list[NFCoreComponent], module_name: str) -> list[NFCoreComponent]:
121+
"""
122+
Filter modules by name, supporting exact matches and tool family matching.
123+
124+
Args:
125+
modules (list[NFCoreComponent]): List of modules to filter
126+
module_name (str): The module name or prefix to match
127+
128+
Returns:
129+
list[NFCoreComponent]: List of matching modules
130+
"""
131+
# First try to find an exact match
132+
exact_matches = [m for m in modules if m.component_name == module_name]
133+
if exact_matches:
134+
return exact_matches
135+
# If no exact match, look for modules that start with the given name (subtools)
136+
return [m for m in modules if m.component_name.startswith(module_name)]

tests/modules/test_modules_utils.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,67 @@ def test_get_installed_modules_with_files(self):
1818

1919
_, nfcore_modules = nf_core.modules.modules_utils.get_installed_modules(self.nfcore_modules)
2020
assert len(nfcore_modules) == 1
21+
22+
def test_filter_modules_by_name_exact_match(self):
23+
"""Test filtering modules by name with an exact match"""
24+
# install bpipe/test
25+
_, nfcore_modules = nf_core.modules.modules_utils.get_installed_modules(self.nfcore_modules)
26+
27+
# Test exact match
28+
filtered = nf_core.modules.modules_utils.filter_modules_by_name(nfcore_modules, "bpipe/test")
29+
assert len(filtered) == 1
30+
assert filtered[0].component_name == "bpipe/test"
31+
32+
def test_filter_modules_by_name_tool_family(self):
33+
"""Test filtering modules by name to get all subtools of a super-tool"""
34+
# Create some mock samtools subtools in the modules directory
35+
samtools_dir = self.nfcore_modules / "modules" / "nf-core" / "samtools"
36+
37+
for subtool in ["view", "sort", "index"]:
38+
subtool_dir = samtools_dir / subtool
39+
subtool_dir.mkdir(parents=True, exist_ok=True)
40+
(subtool_dir / "main.nf").touch()
41+
42+
# Get the modules
43+
_, nfcore_modules = nf_core.modules.modules_utils.get_installed_modules(self.nfcore_modules)
44+
45+
# Test filtering by tool family (super-tool)
46+
filtered = nf_core.modules.modules_utils.filter_modules_by_name(nfcore_modules, "samtools")
47+
48+
assert set(m.component_name for m in filtered) == {"samtools/view", "samtools/sort", "samtools/index"}
49+
50+
def test_filter_modules_by_name_exact_match_preferred(self):
51+
"""Test that exact matches are preferred over prefix matches"""
52+
# Create a samtools super-tool and its subtools
53+
samtools_dir = self.nfcore_modules / "modules" / "nf-core" / "samtools"
54+
samtools_dir.mkdir(parents=True, exist_ok=True)
55+
(samtools_dir / "main.nf").touch()
56+
57+
# Create subtools
58+
for subtool in ["view", "sort"]:
59+
subtool_dir = samtools_dir / subtool
60+
subtool_dir.mkdir(parents=True, exist_ok=True)
61+
(subtool_dir / "main.nf").touch()
62+
63+
# Get the modules
64+
_, nfcore_modules = nf_core.modules.modules_utils.get_installed_modules(self.nfcore_modules)
65+
66+
# Test that exact match is returned when it exists
67+
filtered = nf_core.modules.modules_utils.filter_modules_by_name(nfcore_modules, "samtools")
68+
assert len(filtered) == 1
69+
assert filtered[0].component_name == "samtools"
70+
71+
def test_filter_modules_by_name_no_match(self):
72+
"""Test filtering modules by name with no matches"""
73+
_, nfcore_modules = nf_core.modules.modules_utils.get_installed_modules(self.nfcore_modules)
74+
75+
# Test no match
76+
filtered = nf_core.modules.modules_utils.filter_modules_by_name(nfcore_modules, "nonexistent")
77+
assert len(filtered) == 0
78+
79+
def test_filter_modules_by_name_empty_list(self):
80+
"""Test filtering an empty list of modules"""
81+
modules = []
82+
83+
filtered = nf_core.modules.modules_utils.filter_modules_by_name(modules, "fastqc")
84+
assert len(filtered) == 0

0 commit comments

Comments
 (0)