Skip to content

Commit 3048627

Browse files
authored
Merge branch 'dev' into renovate/astral-sh-ruff-pre-commit-0.x
2 parents 52e8d4b + bc3417a commit 3048627

File tree

21 files changed

+337
-95
lines changed

21 files changed

+337
-95
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010

1111
### Modules
1212

13+
- Modules created in pipelines "local" dir now use the full template ([#3256](https://github.com/nf-core/tools/pull/3256))
14+
1315
### Subworkflows
1416

17+
- Subworkflows created in pipelines "local" dir now use the full template ([#3256](https://github.com/nf-core/tools/pull/3256))
18+
1519
### General
1620

1721
- Fix `process.shell` in `nextflow.config` ([#3416](https://github.com/nf-core/tools/pull/3416))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# modules_structure
2+
3+
```{eval-rst}
4+
.. automethod:: nf_core.pipelines.lint.PipelineLint.local_component_structure
5+
```

nf_core/components/components_command.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ def get_local_components(self) -> List[str]:
7171
"""
7272
local_component_dir = Path(self.directory, self.component_type, "local")
7373
return [
74+
str(Path(directory).relative_to(local_component_dir))
75+
for directory, _, files in os.walk(local_component_dir)
76+
if "main.nf" in files
77+
] + [
7478
str(path.relative_to(local_component_dir)) for path in local_component_dir.iterdir() if path.suffix == ".nf"
7579
]
7680

nf_core/components/create.py

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ def create(self) -> bool:
7575
e.g bam_sort or bam_sort_samtools, respectively.
7676
7777
If <directory> is a pipeline, this function creates a file called:
78-
'<directory>/modules/local/tool.nf'
78+
'<directory>/modules/local/tool/main.nf'
7979
OR
80-
'<directory>/modules/local/tool_subtool.nf'
80+
'<directory>/modules/local/tool/subtool/main.nf'
8181
OR for subworkflows
82-
'<directory>/subworkflows/local/subworkflow_name.nf'
82+
'<directory>/subworkflows/local/subworkflow_name/main.nf'
8383
8484
If <directory> is a clone of nf-core/modules, it creates or modifies the following files:
8585
@@ -355,70 +355,46 @@ def _get_component_dirs(self) -> Dict[str, Path]:
355355
"""
356356
file_paths = {}
357357
if self.repo_type == "pipeline":
358-
local_component_dir = Path(self.directory, self.component_type, "local")
359-
# Check whether component file already exists
360-
component_file = local_component_dir / f"{self.component_name}.nf"
361-
if component_file.exists() and not self.force_overwrite:
362-
raise UserWarning(
363-
f"{self.component_type[:-1].title()} file exists already: '{component_file}'. Use '--force' to overwrite"
364-
)
365-
366-
if self.component_type == "modules":
367-
# If a subtool, check if there is a module called the base tool name already
368-
if self.subtool and (local_component_dir / f"{self.component}.nf").exists():
369-
raise UserWarning(
370-
f"Module '{self.component}' exists already, cannot make subtool '{self.component_name}'"
371-
)
372-
373-
# If no subtool, check that there isn't already a tool/subtool
374-
tool_glob = glob.glob(f"{local_component_dir}/{self.component}_*.nf")
375-
if not self.subtool and tool_glob:
376-
raise UserWarning(
377-
f"Module subtool '{tool_glob[0]}' exists already, cannot make tool '{self.component_name}'"
378-
)
379-
380-
# Set file paths
381-
file_paths["main.nf"] = component_file
358+
component_dir = Path(self.directory, self.component_type, "local", self.component_dir)
382359

383360
elif self.repo_type == "modules":
384361
component_dir = Path(self.directory, self.component_type, self.org, self.component_dir)
385-
# Check if module/subworkflow directories exist already
386-
if component_dir.exists() and not self.force_overwrite and not self.migrate_pytest:
387-
raise UserWarning(
388-
f"{self.component_type[:-1]} directory exists: '{component_dir}'. Use '--force' to overwrite"
389-
)
362+
else:
363+
raise ValueError("`repo_type` not set correctly")
390364

391-
if self.component_type == "modules":
392-
# If a subtool, check if there is a module called the base tool name already
393-
parent_tool_main_nf = Path(
394-
self.directory,
395-
self.component_type,
396-
self.org,
397-
self.component,
398-
"main.nf",
365+
# Check if module/subworkflow directories exist already
366+
if component_dir.exists() and not self.force_overwrite and not self.migrate_pytest:
367+
raise UserWarning(
368+
f"{self.component_type[:-1]} directory exists: '{component_dir}'. Use '--force' to overwrite"
369+
)
370+
371+
if self.component_type == "modules":
372+
# If a subtool, check if there is a module called the base tool name already
373+
parent_tool_main_nf = Path(
374+
self.directory,
375+
self.component_type,
376+
self.org,
377+
self.component,
378+
"main.nf",
379+
)
380+
if self.subtool and parent_tool_main_nf.exists() and not self.migrate_pytest:
381+
raise UserWarning(
382+
f"Module '{parent_tool_main_nf}' exists already, cannot make subtool '{self.component_name}'"
399383
)
400-
if self.subtool and parent_tool_main_nf.exists() and not self.migrate_pytest:
401-
raise UserWarning(
402-
f"Module '{parent_tool_main_nf}' exists already, cannot make subtool '{self.component_name}'"
403-
)
404384

405-
# If no subtool, check that there isn't already a tool/subtool
406-
tool_glob = glob.glob(
407-
f"{Path(self.directory, self.component_type, self.org, self.component)}/*/main.nf"
385+
# If no subtool, check that there isn't already a tool/subtool
386+
tool_glob = glob.glob(f"{Path(self.directory, self.component_type, self.org, self.component)}/*/main.nf")
387+
if not self.subtool and tool_glob and not self.migrate_pytest:
388+
raise UserWarning(
389+
f"Module subtool '{tool_glob[0]}' exists already, cannot make tool '{self.component_name}'"
408390
)
409-
if not self.subtool and tool_glob and not self.migrate_pytest:
410-
raise UserWarning(
411-
f"Module subtool '{tool_glob[0]}' exists already, cannot make tool '{self.component_name}'"
412-
)
413-
# Set file paths
414-
# For modules - can be tool/ or tool/subtool/ so can't do in template directory structure
415-
file_paths["main.nf"] = component_dir / "main.nf"
416-
file_paths["meta.yml"] = component_dir / "meta.yml"
417-
if self.component_type == "modules":
418-
file_paths["environment.yml"] = component_dir / "environment.yml"
419-
file_paths["tests/main.nf.test.j2"] = component_dir / "tests" / "main.nf.test"
420-
else:
421-
raise ValueError("`repo_type` not set correctly")
391+
# Set file paths
392+
# For modules - can be tool/ or tool/subtool/ so can't do in template directory structure
393+
file_paths["main.nf"] = component_dir / "main.nf"
394+
file_paths["meta.yml"] = component_dir / "meta.yml"
395+
if self.component_type == "modules":
396+
file_paths["environment.yml"] = component_dir / "environment.yml"
397+
file_paths["tests/main.nf.test.j2"] = component_dir / "tests" / "main.nf.test"
422398

423399
return file_paths
424400

nf_core/components/lint/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ def _set_registry(self, registry) -> None:
162162
self.registry = registry
163163
log.debug(f"Registry set to {self.registry}")
164164

165+
@property
166+
def local_module_exclude_tests(self):
167+
return ["module_version", "module_changes", "modules_patch"]
168+
165169
@staticmethod
166170
def get_all_module_lint_tests(is_pipeline):
167171
if is_pipeline:

nf_core/components/nfcore_component.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ def __init__(
7474
repo_dir = self.component_dir.parts[:name_index][-1]
7575

7676
self.org = repo_dir
77-
self.nftest_testdir = Path(self.component_dir, "tests")
78-
self.nftest_main_nf = Path(self.nftest_testdir, "main.nf.test")
77+
self.nftest_testdir: Optional[Path] = Path(self.component_dir, "tests")
78+
self.nftest_main_nf: Optional[Path] = Path(self.nftest_testdir, "main.nf.test")
7979

8080
if self.repo_type == "pipeline":
8181
patch_fn = f"{self.component_name.replace('/', '-')}.diff"
@@ -85,15 +85,23 @@ def __init__(
8585
self.patch_path = patch_path
8686
else:
8787
# The main file is just the local module
88-
self.main_nf = self.component_dir
89-
self.component_name = self.component_dir.stem
90-
# These attributes are only used by nf-core modules
91-
# so just initialize them to None
92-
self.meta_yml = None
93-
self.environment_yml = None
94-
self.test_dir = None
95-
self.test_yml = None
96-
self.test_main_nf = None
88+
if self.component_dir.is_dir():
89+
self.main_nf = Path(self.component_dir, "main.nf")
90+
self.component_name = self.component_dir.stem
91+
# These attributes are only required by nf-core modules
92+
# so just set them to None if they don't exist
93+
self.meta_yml = p if (p := Path(self.component_dir, "meta.yml")).exists() else None
94+
self.environment_yml = p if (p := Path(self.component_dir, "environment.yml")).exists() else None
95+
self.nftest_testdir = p if (p := Path(self.component_dir, "tests")).exists() else None
96+
if self.nftest_testdir is not None:
97+
self.nftest_main_nf = p if (p := Path(self.nftest_testdir, "main.nf.test")).exists() else None
98+
else:
99+
self.main_nf = self.component_dir
100+
self.component_dir = self.component_dir.parent
101+
self.meta_yml = None
102+
self.environment_yml = None
103+
self.nftest_testdir = None
104+
self.nftest_main_nf = None
97105

98106
self.process_name: str = self._get_process_name()
99107

nf_core/modules/lint/__init__.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def lint(
117117
"""
118118
# TODO: consider unifying modules and subworkflows lint() function and add it to the ComponentLint class
119119
# Prompt for module or all
120-
if module is None and not all_modules and len(self.all_remote_components) > 0:
120+
if module is None and not (local or all_modules) and len(self.all_remote_components) > 0:
121121
questions = [
122122
{
123123
"type": "list",
@@ -170,7 +170,7 @@ def lint(
170170
self.lint_modules(local_modules, registry=registry, local=True, fix_version=fix_version)
171171

172172
# Lint nf-core modules
173-
if len(remote_modules) > 0:
173+
if not local and len(remote_modules) > 0:
174174
self.lint_modules(remote_modules, registry=registry, local=False, fix_version=fix_version)
175175

176176
if print_results:
@@ -234,7 +234,23 @@ def lint_module(
234234
# TODO: consider unifying modules and subworkflows lint_module() function and add it to the ComponentLint class
235235
# Only check the main script in case of a local module
236236
if local:
237-
self.main_nf(mod, fix_version, self.registry, progress_bar)
237+
mod.get_inputs_from_main_nf()
238+
mod.get_outputs_from_main_nf()
239+
# Update meta.yml file if requested
240+
if self.fix and mod.meta_yml is not None:
241+
self.update_meta_yml_file(mod)
242+
243+
for test_name in self.lint_tests:
244+
if test_name in self.local_module_exclude_tests:
245+
continue
246+
if test_name == "main_nf":
247+
getattr(self, test_name)(mod, fix_version, self.registry, progress_bar)
248+
elif test_name in ["meta_yml", "environment_yml"]:
249+
# Allow files to be missing for local
250+
getattr(self, test_name)(mod, allow_missing=True)
251+
else:
252+
getattr(self, test_name)(mod)
253+
238254
self.passed += [LintResult(mod, *m) for m in mod.passed]
239255
warned = [LintResult(mod, *m) for m in (mod.warned + mod.failed)]
240256
if not self.fail_warned:

nf_core/modules/lint/environment_yml.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
log = logging.getLogger(__name__)
1313

1414

15-
def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> None:
15+
def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_missing: bool = False) -> None:
1616
"""
1717
Lint an ``environment.yml`` file.
1818
@@ -23,6 +23,15 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent)
2323
env_yml = None
2424
# load the environment.yml file
2525
if module.environment_yml is None:
26+
if allow_missing:
27+
module.warned.append(
28+
(
29+
"environment_yml_exists",
30+
"Module's `environment.yml` does not exist",
31+
Path(module.component_dir, "environment.yml"),
32+
),
33+
)
34+
return
2635
raise LintExceptionError("Module does not have an `environment.yml` file")
2736
try:
2837
with open(module.environment_yml) as fh:

nf_core/modules/lint/meta_yml.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
log = logging.getLogger(__name__)
1414

1515

16-
def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> None:
16+
def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_missing: bool = False) -> None:
1717
"""
1818
Lint a ``meta.yml`` file
1919
@@ -42,7 +42,13 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> None
4242
module (NFCoreComponent): The module to lint
4343
4444
"""
45-
45+
if module.meta_yml is None:
46+
if allow_missing:
47+
module.warned.append(
48+
("meta_yml_exists", "Module `meta.yml` does not exist", Path(module.component_dir, "meta.yml"))
49+
)
50+
return
51+
raise LintExceptionError("Module does not have a `meta.yml` file")
4652
# Check if we have a patch file, get original file in that case
4753
meta_yaml = read_meta_yml(module_lint_object, module)
4854
if module.is_patched and module_lint_object.modules_repo.repo_path is not None:
@@ -57,8 +63,6 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent) -> None
5763
if lines is not None:
5864
yaml = ruamel.yaml.YAML()
5965
meta_yaml = yaml.safe_load("".join(lines))
60-
if module.meta_yml is None:
61-
raise LintExceptionError("Module does not have a `meta.yml` file")
6266
if meta_yaml is None:
6367
module.failed.append(("meta_yml_exists", "Module `meta.yml` does not exist", module.meta_yml))
6468
return

nf_core/modules/lint/module_tests.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,44 @@
99

1010
import yaml
1111

12+
from nf_core.components.lint import LintExceptionError
1213
from nf_core.components.nfcore_component import NFCoreComponent
1314

1415
log = logging.getLogger(__name__)
1516

1617

17-
def module_tests(_, module: NFCoreComponent):
18+
def module_tests(_, module: NFCoreComponent, allow_missing: bool = False):
1819
"""
1920
Lint the tests of a module in ``nf-core/modules``
2021
2122
It verifies that the test directory exists
2223
and contains a ``main.nf.test`` and a ``main.nf.test.snap``
2324
2425
"""
26+
if module.nftest_testdir is None:
27+
if allow_missing:
28+
module.warned.append(
29+
(
30+
"test_dir_exists",
31+
"nf-test directory is missing",
32+
Path(module.component_dir, "tests"),
33+
)
34+
)
35+
return
36+
raise LintExceptionError("Module does not have a `tests` dir")
37+
38+
if module.nftest_main_nf is None:
39+
if allow_missing:
40+
module.warned.append(
41+
(
42+
"test_main_nf_exists",
43+
"test `main.nf.test` does not exist",
44+
Path(module.component_dir, "tests", "main.nf.test"),
45+
)
46+
)
47+
return
48+
raise LintExceptionError("Module does not have a `tests` dir")
49+
2550
repo_dir = module.component_dir.parts[: module.component_dir.parts.index(module.component_name.split("/")[0])][-1]
2651
test_dir = Path(module.base_dir, "tests", "modules", repo_dir, module.component_name)
2752
pytest_main_nf = Path(test_dir, "main.nf")

0 commit comments

Comments
 (0)