Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document ext.args in meta yml #3451

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions nf_core/components/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ def generate_params_table(self, type) -> Table:
table.add_column("Structure", justify="right", style="green")
return table

def generate_args_table(self) -> Table:
table = Table(expand=True, show_lines=True, box=box.MINIMAL_HEAVY_HEAD, padding=0)
table.add_column(":hammer_and_wrench: Extra Args")
table.add_column("Description")
return table

def get_channel_structure(self, structure: dict) -> str:
"Get the structure of a channel"
structure_str = ""
Expand Down Expand Up @@ -341,6 +347,17 @@ def generate_component_info_help(self):

renderables.append(outputs_table)

# args
if self.meta.get("args"):
args_table = self.generate_args_table()
for argname, arginfo in self.meta["args"].items():
args_table.add_row(
f"[orange1 on black] {argname} [/][dim i]",
Markdown(arginfo["description"] if arginfo["description"] else ""),
)

renderables.append(args_table)

# Installation command
if self.remote_location and not self.local:
cmd_base = f"nf-core {self.component_type}"
Expand Down
20 changes: 20 additions & 0 deletions nf_core/components/nfcore_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,23 @@ def get_outputs_from_main_nf(self):
pass
log.debug(f"Found {len(outputs)} outputs in {self.main_nf}")
self.outputs = outputs

def get_ext_args_from_main_nf(self) -> List[str]:
"""Collect all ext.args from the main.nf file."""
extargs: set[str] = set()
with open(self.main_nf) as f:
data = f.read()
if self.component_type == "modules":
awgymer marked this conversation as resolved.
Show resolved Hide resolved
section_identifier = "script:"
if "script:" not in data:
section_identifier = "exec:"
if "exec:" not in data:
log.debug(f"Could not find 'script' or 'exec' section in {self.main_nf}")
return []
input_data = data.split(section_identifier)[1].split("stub:")[0]
regex = r"ext.(args[0-9]*) "
matches = re.finditer(regex, input_data)
for m in matches:
extargs.add(m.group(1))
log.debug(f"Found {len(extargs)} extra args in {self.main_nf}")
return sorted(extargs)
7 changes: 7 additions & 0 deletions nf_core/module-template/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ tools:
licence: {{ tool_licence }}
identifier: {{ tool_identifier }}

{% if not_empty_template -%}
## TODO nf-core: Add a description of all of the args used in the pipeline
{% endif -%}
args:
- args:
description: "{{ component }}"

{% if not_empty_template -%}
## TODO nf-core: Add a description of all of the variables used as input
{% endif -%}
Expand Down
50 changes: 37 additions & 13 deletions nf_core/modules/lint/meta_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
return
raise LintExceptionError("Module does not have a `meta.yml` file")
# Check if we have a patch file, get original file in that case
meta_yaml = read_meta_yml(module_lint_object, module)
meta_yaml_content = read_meta_yml(module_lint_object, module)
if module.is_patched and module_lint_object.modules_repo.repo_path is not None:
lines = ComponentsDiffer.try_apply_patch(
module.component_type,
Expand All @@ -62,8 +62,8 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
).get("meta.yml")
if lines is not None:
yaml = ruamel.yaml.YAML()
meta_yaml = yaml.load("".join(lines))
if meta_yaml is None:
meta_yaml_content = yaml.load("".join(lines))
if meta_yaml_content is None:
module.failed.append(("meta_yml_exists", "Module `meta.yml` does not exist", module.meta_yml))
return
else:
Expand All @@ -74,7 +74,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
try:
with open(Path(module_lint_object.modules_repo.local_repo_dir, "modules/meta-schema.json")) as fh:
schema = json.load(fh)
validators.validate(instance=meta_yaml, schema=schema)
validators.validate(instance=meta_yaml_content, schema=schema)
module.passed.append(("meta_yml_valid", "Module `meta.yml` is valid", module.meta_yml))
valid_meta_yml = True
except exceptions.ValidationError as e:
Expand All @@ -85,7 +85,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
hint = f"\nCheck that the child entries of {str(e.path[0]) + '.' + str(e.path[2])} are indented correctly."
if e.schema and isinstance(e.schema, dict) and "message" in e.schema:
e.message = e.schema["message"]
incorrect_value = meta_yaml
incorrect_value = meta_yaml_content
for key in e.path:
incorrect_value = incorrect_value[key]

Expand All @@ -101,7 +101,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
# Confirm that all input and output channels are correctly specified
if valid_meta_yml:
# confirm that the name matches the process name in main.nf
if meta_yaml["name"].upper() == module.process_name:
if meta_yaml_content["name"].upper() == module.process_name:
module.passed.append(
(
"meta_name",
Expand All @@ -113,12 +113,12 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
module.failed.append(
(
"meta_name",
f"Conflicting `process` name between meta.yml (`{meta_yaml['name']}`) and main.nf (`{module.process_name}`)",
f"Conflicting `process` name between meta.yml (`{meta_yaml_content['name']}`) and main.nf (`{module.process_name}`)",
module.meta_yml,
)
)
# Check that inputs are specified in meta.yml
if len(module.inputs) > 0 and "input" not in meta_yaml:
if len(module.inputs) > 0 and "input" not in meta_yaml_content:
module.failed.append(
(
"meta_input",
Expand All @@ -137,8 +137,10 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
else:
log.debug(f"No inputs specified in module `main.nf`: {module.component_name}")
# Check that all inputs are correctly specified
if "input" in meta_yaml:
correct_inputs, meta_inputs = obtain_correct_and_specified_inputs(module_lint_object, module, meta_yaml)
if "input" in meta_yaml_content:
correct_inputs, meta_inputs = obtain_correct_and_specified_inputs(
module_lint_object, module, meta_yaml_content
)

if correct_inputs == meta_inputs:
module.passed.append(
Expand All @@ -158,7 +160,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
)

# Check that outputs are specified in meta.yml
if len(module.outputs) > 0 and "output" not in meta_yaml:
if len(module.outputs) > 0 and "output" not in meta_yaml_content:
module.failed.append(
(
"meta_output",
Expand All @@ -175,8 +177,10 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
)
)
# Check that all outputs are correctly specified
if "output" in meta_yaml:
correct_outputs, meta_outputs = obtain_correct_and_specified_outputs(module_lint_object, module, meta_yaml)
if "output" in meta_yaml_content:
correct_outputs, meta_outputs = obtain_correct_and_specified_outputs(
module_lint_object, module, meta_yaml_content
)

if correct_outputs == meta_outputs:
module.passed.append(
Expand All @@ -195,6 +199,26 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
)
)

# Check that ext.args are listed in the `extra_args` section
expected_extra_args = module.get_ext_args_from_main_nf()
extra_args_yml = sorted(meta_yaml_content.get("extra_args", {}).keys())
awgymer marked this conversation as resolved.
Show resolved Hide resolved
if expected_extra_args == extra_args_yml:
module.passed.append(
(
"correct_extra_args",
"Correct extra args specified in `meta.yml`",
module.meta_yml,
)
)
else:
module.warned.append(
(
"correct_extra_args",
f"Module `meta.yml` does not match `main.nf`. Extra args should contain: {extra_args_yml}\n",
module.meta_yml,
)
)


def read_meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> Union[dict, None]:
"""
Expand Down
Loading