Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- TEMPLATE: ignore nf-core components during prettier linting ([#3858](https://github.com/nf-core/tools/pull/3858))
- update json schema store URL ([#3877](https://github.com/nf-core/tools/pull/3877))
- add word boundary for input, output and topic linting ([#3894](https://github.com/nf-core/tools/pull/3894))
- Add linting of topics ([#3902](https://github.com/nf-core/tools/pull/3902))

### Modules

Expand Down
10 changes: 4 additions & 6 deletions nf_core/components/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def generate_meta_yml_file(self) -> None:
{"${task.process}": {"type": "string", "description": "The name of the process"}},
{f"{self.component}": {"type": "string", "description": "The name of the tool"}},
{
f"{self.component} --version": {"type": "string", "description": "The version of the tool"},
f"{self.component} --version": {"type": "eval", "description": "The version of the tool"},
},
]
]
Expand All @@ -543,12 +543,10 @@ def generate_meta_yml_file(self) -> None:
versions_topic: dict[str, list | dict] = {
"versions": [
[
{"process": {"type": "string", "description": "The process the versions were collected from"}},
{
"tool": {"type": "string", "description": "The tool name the version was collected for"},
},
{"${task.process}": {"type": "string", "description": "The name of the process"}},
{f"{self.component}": {"type": "string", "description": "The name of the tool"}},
{
"version": {"type": "string", "description": "The version of the tool"},
f"{self.component} --version": {"type": "eval", "description": "The version of the tool"},
},
]
]
Expand Down
17 changes: 11 additions & 6 deletions nf_core/components/nfcore_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __init__(
self.failed: list[tuple[str, str, str, Path]] = []
self.inputs: list[list[dict[str, dict[str, str]]]] = []
self.outputs: list[str] = []
self.topics: dict[str, list[dict[str, dict] | list[dict[str, dict[str, str]]]]]
self.has_meta: bool = False
self.git_sha: str | None = None
self.is_patched: bool = False
Expand Down Expand Up @@ -322,14 +323,18 @@ def get_topics_from_main_nf(self) -> None:
if topic_name in topics:
continue
topics[match_topic.group(1)] = []
for count, match_element in enumerate(matches_elements, start=1):
output_val = None
for _, match_element in enumerate(matches_elements, start=1):
topic_val = None
if match_element.group(3):
output_val = match_element.group(3)
topic_val = match_element.group(3)
elif match_element.group(4):
output_val = match_element.group(4)
if output_val:
channel_elements.append({f"value{count}": {}})
topic_val = match_element.group(4)
if topic_val:
topic_val = re.split(r',(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)', topic_val)[
0
] # Takes only first part, avoid commas in quotes
topic_val = topic_val.strip().strip("'").strip('"') # remove quotes and whitespaces
channel_elements.append({topic_val: {}})
if len(channel_elements) == 1:
topics[match_topic.group(1)].append(channel_elements[0])
elif len(channel_elements) > 1:
Expand Down
18 changes: 9 additions & 9 deletions nf_core/module-template/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ output:
type: string
description: The name of the tool
- "{{ component }} --version":
type: string
description: The version of the tool
type: eval
description: The expression to obtain the version of the tool

topics:
versions:
- - process:
type: string
description: The process the versions were collected from
- tool:
- - "${task.process}":
type: string
description: The tool name the version was collected for
- version:
description: The name of the process
- "{{ component }}":
type: string
description: The version of the tool
description: The name of the tool
- "{{ component }} --version":
type: eval
description: The expression to obtain the version of the tool

authors:
- "{{ author }}"
Expand Down
23 changes: 22 additions & 1 deletion nf_core/modules/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

from .environment_yml import environment_yml
from .main_nf import main_nf
from .meta_yml import meta_yml, obtain_inputs, obtain_outputs, read_meta_yml
from .meta_yml import meta_yml, obtain_inputs, obtain_outputs, obtain_topics, read_meta_yml
from .module_changes import module_changes
from .module_deprecations import module_deprecations
from .module_patch import module_patch
Expand All @@ -48,6 +48,7 @@ class ModuleLint(ComponentLint):
meta_yml = meta_yml
obtain_inputs = obtain_inputs
obtain_outputs = obtain_outputs
obtain_topics = obtain_topics
read_meta_yml = read_meta_yml
module_changes = module_changes
module_deprecations = module_deprecations
Expand Down Expand Up @@ -302,6 +303,8 @@ def update_meta_yml_file(self, mod):
if "output" in meta_yml:
correct_outputs = self.obtain_outputs(mod.outputs)
meta_outputs = self.obtain_outputs(meta_yml["output"])
correct_topics = self.obtain_topics(mod.topics)
meta_topics = self.obtain_topics(meta_yml["topics"])

def _find_meta_info(meta_yml, element_name, is_output=False) -> dict:
"""Find the information specified in the meta.yml file to update the corrected meta.yml content"""
Expand Down Expand Up @@ -367,6 +370,24 @@ def _find_meta_info(meta_yml, element_name, is_output=False) -> dict:
corrected_meta_yml["output"][ch_name][i][element_name] = _find_meta_info(
meta_yml["output"], element_name, is_output=True
)
elif "topics" in meta_yml and correct_topics != meta_topics:
log.debug(
f"Correct topics: '{correct_topics}' differ from current topics: '{meta_topics}' in '{mod.meta_yml}'"
)
corrected_meta_yml["topics"] = mod.topics.copy()
for t_name in corrected_meta_yml["topics"].keys():
for i, t_content in enumerate(corrected_meta_yml["topics"][t_name]):
if isinstance(t_content, list):
for j, element in enumerate(t_content):
element_name = list(element.keys())[0]
corrected_meta_yml["topics"][t_name][i][j][element_name] = _find_meta_info(
meta_yml["topics"], element_name, is_output=True
)
elif isinstance(t_content, dict):
element_name = list(t_content.keys())[0]
corrected_meta_yml["topics"][t_name][i][element_name] = _find_meta_info(
meta_yml["topics"], element_name, is_output=True
)

def _add_edam_ontologies(section, edam_formats, desc):
expected_ontologies = []
Expand Down
49 changes: 49 additions & 0 deletions nf_core/modules/lint/meta_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,29 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
module.meta_yml,
)
)
# Check that all topics are correctly specified
if "topics" in meta_yaml:
correct_topics = obtain_topics(module_lint_object, module.topics)
meta_topics = obtain_topics(module_lint_object, meta_yaml["topics"])

if correct_topics == meta_topics:
module.passed.append(
(
"meta_yml",
"correct_meta_topics",
"Correct topics specified in module `meta.yml`",
module.meta_yml,
)
)
else:
module.failed.append(
(
"meta_yml",
"correct_meta_topics",
f"Module `meta.yml` does not match `main.nf`. Topics should contain: {correct_topics}\nRun `nf-core modules lint --fix` to update the `meta.yml` file.",
module.meta_yml,
)
)


def read_meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> dict | None:
Expand Down Expand Up @@ -301,3 +324,29 @@ def obtain_outputs(_, outputs: dict | list) -> dict | list:
return [{k: v} for k, v in formatted_outputs.items()]
else:
return formatted_outputs


def obtain_topics(_, topics: dict) -> dict:
"""
Obtain the dictionary of topics and elements of each topic.

Args:
topics (dict): The dictionary of topics from main.nf or meta.yml files.

Returns:
formatted_topics (dict): A dictionary containing the topics and their elements obtained from main.nf or meta.yml files.
"""
formatted_topics: dict = {}
for name in topics.keys():
content = topics[name]
t_elements: list = []
for element in content:
if isinstance(element, list):
t_elements.append([])
for e in element:
t_elements[-1].append(list(e.keys())[0])
else:
t_elements.append(list(element.keys())[0])
formatted_topics[name] = t_elements

return formatted_topics
Loading