Skip to content

Commit 24e2dc2

Browse files
authored
Merge pull request #3276 from mashehu/fix-create-params
handle new schema structure in `nf-core pipelines create-params-file`
2 parents 27659c5 + e57a096 commit 24e2dc2

File tree

5 files changed

+75
-66
lines changed

5 files changed

+75
-66
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- Update GitHub Actions ([#3237](https://github.com/nf-core/tools/pull/3237))
3636
- add `--dir/-d` option to schema commands ([#3247](https://github.com/nf-core/tools/pull/3247))
3737
- Update pre-commit hook astral-sh/ruff-pre-commit to v0.7.1 ([#3250](https://github.com/nf-core/tools/pull/3250))
38+
- handle new schema structure in `nf-core pipelines create-params-file` ([#3276](https://github.com/nf-core/tools/pull/3276))
3839
- Update Gitpod image to use Miniforge instead of Miniconda([#3274](https://github.com/nf-core/tools/pull/3274))
3940
- Update pre-commit hook astral-sh/ruff-pre-commit to v0.7.3 ([#3275](https://github.com/nf-core/tools/pull/3275))
4041

nf_core/pipelines/params_file.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import json
44
import logging
5-
import os
65
import textwrap
7-
from typing import Literal, Optional
6+
from pathlib import Path
7+
from typing import Dict, List, Literal, Optional
88

99
import questionary
1010

@@ -27,7 +27,7 @@
2727
ModeLiteral = Literal["both", "start", "end", "none"]
2828

2929

30-
def _print_wrapped(text, fill_char="-", mode="both", width=80, indent=0, drop_whitespace=True):
30+
def _print_wrapped(text, fill_char="-", mode="both", width=80, indent=0, drop_whitespace=True) -> str:
3131
"""Helper function to format text for the params-file template.
3232
3333
Args:
@@ -100,7 +100,7 @@ def __init__(
100100
self.wfs = nf_core.pipelines.list.Workflows()
101101
self.wfs.get_remote_workflows()
102102

103-
def get_pipeline(self):
103+
def get_pipeline(self) -> Optional[bool]:
104104
"""
105105
Prompt the user for a pipeline name and get the schema
106106
"""
@@ -124,11 +124,14 @@ def get_pipeline(self):
124124
).unsafe_ask()
125125

126126
# Get the schema
127-
self.schema_obj = nf_core.pipelines.schema.PipelineSchema()
127+
self.schema_obj = PipelineSchema()
128+
if self.schema_obj is None:
129+
return False
128130
self.schema_obj.get_schema_path(self.pipeline, local_only=False, revision=self.pipeline_revision)
129131
self.schema_obj.get_wf_params()
132+
return True
130133

131-
def format_group(self, definition, show_hidden=False):
134+
def format_group(self, definition, show_hidden=False) -> str:
132135
"""Format a group of parameters of the schema as commented YAML.
133136
134137
Args:
@@ -167,7 +170,9 @@ def format_group(self, definition, show_hidden=False):
167170

168171
return out
169172

170-
def format_param(self, name, properties, required_properties=(), show_hidden=False):
173+
def format_param(
174+
self, name: str, properties: Dict, required_properties: List[str] = [], show_hidden: bool = False
175+
) -> Optional[str]:
171176
"""
172177
Format a single parameter of the schema as commented YAML
173178
@@ -188,6 +193,9 @@ def format_param(self, name, properties, required_properties=(), show_hidden=Fal
188193
return None
189194

190195
description = properties.get("description", "")
196+
if self.schema_obj is None:
197+
log.error("No schema object found")
198+
return ""
191199
self.schema_obj.get_schema_defaults()
192200
default = properties.get("default")
193201
type = properties.get("type")
@@ -209,7 +217,7 @@ def format_param(self, name, properties, required_properties=(), show_hidden=Fal
209217

210218
return out
211219

212-
def generate_params_file(self, show_hidden=False):
220+
def generate_params_file(self, show_hidden: bool = False) -> str:
213221
"""Generate the contents of a parameter template file.
214222
215223
Assumes the pipeline has been fetched (if remote) and the schema loaded.
@@ -220,6 +228,10 @@ def generate_params_file(self, show_hidden=False):
220228
Returns:
221229
str: Formatted output for the pipeline schema
222230
"""
231+
if self.schema_obj is None:
232+
log.error("No schema object found")
233+
return ""
234+
223235
schema = self.schema_obj.schema
224236
pipeline_name = self.schema_obj.pipeline_manifest.get("name", self.pipeline)
225237
pipeline_version = self.schema_obj.pipeline_manifest.get("version", "0.0.0")
@@ -234,13 +246,13 @@ def generate_params_file(self, show_hidden=False):
234246
out += "\n"
235247

236248
# Add all parameter groups
237-
for definition in schema.get("definitions", {}).values():
249+
for definition in schema.get("definitions", schema.get("$defs", {})).values():
238250
out += self.format_group(definition, show_hidden=show_hidden)
239251
out += "\n"
240252

241253
return out
242254

243-
def write_params_file(self, output_fn="nf-params.yaml", show_hidden=False, force=False):
255+
def write_params_file(self, output_fn: Path = Path("nf-params.yaml"), show_hidden=False, force=False) -> bool:
244256
"""Build a template file for the pipeline schema.
245257
246258
Args:
@@ -254,7 +266,9 @@ def write_params_file(self, output_fn="nf-params.yaml", show_hidden=False, force
254266
"""
255267

256268
self.get_pipeline()
257-
269+
if self.schema_obj is None:
270+
log.error("No schema object found")
271+
return False
258272
try:
259273
self.schema_obj.load_schema()
260274
self.schema_obj.validate_schema()
@@ -265,11 +279,10 @@ def write_params_file(self, output_fn="nf-params.yaml", show_hidden=False, force
265279

266280
schema_out = self.generate_params_file(show_hidden=show_hidden)
267281

268-
if os.path.exists(output_fn) and not force:
282+
if output_fn.exists() and not force:
269283
log.error(f"File '{output_fn}' exists! Please delete first, or use '--force'")
270284
return False
271-
with open(output_fn, "w") as fh:
272-
fh.write(schema_out)
273-
log.info(f"Parameter file written to '{output_fn}'")
285+
output_fn.write_text(schema_out)
286+
log.info(f"Parameter file written to '{output_fn}'")
274287

275288
return True

nf_core/pipelines/schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def get_schema_path(
124124
# Supplied path exists - assume a local pipeline directory or schema
125125
if path.exists():
126126
log.debug(f"Path exists: {path}. Assuming local pipeline directory or schema")
127+
local_only = True
127128
if revision is not None:
128129
log.warning(f"Local workflow supplied, ignoring revision '{revision}'")
129130
if path.is_dir():
Lines changed: 39 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,67 @@
11
import json
2-
import os
3-
import shutil
4-
import tempfile
52
from pathlib import Path
63

7-
import nf_core.pipelines.create.create
8-
import nf_core.pipelines.schema
94
from nf_core.pipelines.params_file import ParamsFileBuilder
105

6+
from ..test_pipelines import TestPipelines
117

12-
class TestParamsFileBuilder:
8+
9+
class TestParamsFileBuilder(TestPipelines):
1310
"""Class for schema tests"""
1411

15-
@classmethod
16-
def setup_class(cls):
12+
def setUp(self):
1713
"""Create a new PipelineSchema object"""
18-
cls.schema_obj = nf_core.pipelines.schema.PipelineSchema()
19-
cls.root_repo_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
20-
21-
# Create a test pipeline in temp directory
22-
cls.tmp_dir = tempfile.mkdtemp()
23-
cls.template_dir = Path(cls.tmp_dir, "wf")
24-
create_obj = nf_core.pipelines.create.create.PipelineCreate(
25-
"testpipeline", "a description", "Me", outdir=cls.template_dir, no_git=True
26-
)
27-
create_obj.init_pipeline()
28-
29-
cls.template_schema = Path(cls.template_dir, "nextflow_schema.json")
30-
cls.params_template_builder = ParamsFileBuilder(cls.template_dir)
31-
cls.invalid_template_schema = Path(cls.template_dir, "nextflow_schema_invalid.json")
32-
33-
# Remove the allOf section to make the schema invalid
34-
with open(cls.template_schema) as fh:
35-
o = json.load(fh)
36-
del o["allOf"]
37-
38-
with open(cls.invalid_template_schema, "w") as fh:
39-
json.dump(o, fh)
40-
41-
@classmethod
42-
def teardown_class(cls):
43-
if Path(cls.tmp_dir).exists():
44-
shutil.rmtree(cls.tmp_dir)
14+
super().setUp()
15+
16+
self.template_schema = Path(self.pipeline_dir, "nextflow_schema.json")
17+
self.params_template_builder = ParamsFileBuilder(self.pipeline_dir)
18+
self.outfile = Path(self.pipeline_dir, "params-file.yml")
4519

4620
def test_build_template(self):
47-
outfile = Path(self.tmp_dir, "params-file.yml")
48-
self.params_template_builder.write_params_file(str(outfile))
21+
self.params_template_builder.write_params_file(self.outfile)
4922

50-
assert outfile.exists()
23+
assert self.outfile.exists()
5124

52-
with open(outfile) as fh:
25+
with open(self.outfile) as fh:
5326
out = fh.read()
5427

5528
assert "nf-core/testpipeline" in out
5629

57-
def test_build_template_invalid_schema(self, caplog):
30+
def test_build_template_invalid_schema(self):
5831
"""Build a schema from a template"""
59-
outfile = Path(self.tmp_dir, "params-file-invalid.yml")
60-
builder = ParamsFileBuilder(self.invalid_template_schema)
61-
res = builder.write_params_file(str(outfile))
32+
schema = {}
33+
with open(self.template_schema) as fh:
34+
schema = json.load(fh)
35+
del schema["allOf"]
36+
37+
with open(self.template_schema, "w") as fh:
38+
json.dump(schema, fh)
39+
40+
builder = ParamsFileBuilder(self.template_schema)
41+
res = builder.write_params_file(self.outfile)
6242

6343
assert res is False
64-
assert "Pipeline schema file is invalid" in caplog.text
44+
assert "Pipeline schema file is invalid" in self.caplog.text
6545

66-
def test_build_template_file_exists(self, caplog):
46+
def test_build_template_file_exists(self):
6747
"""Build a schema from a template"""
6848

6949
# Creates a new empty file
70-
outfile = Path(self.tmp_dir) / "params-file.yml"
71-
with open(outfile, "w"):
72-
pass
50+
self.outfile.touch()
7351

74-
res = self.params_template_builder.write_params_file(outfile)
52+
res = self.params_template_builder.write_params_file(self.outfile)
7553

7654
assert res is False
77-
assert f"File '{outfile}' exists!" in caplog.text
55+
assert f"File '{self.outfile}' exists!" in self.caplog.text
56+
57+
self.outfile.unlink()
7858

79-
outfile.unlink()
59+
def test_build_template_content(self):
60+
"""Test that the content of the params file is correct"""
61+
self.params_template_builder.write_params_file(self.outfile)
62+
63+
with open(self.outfile) as fh:
64+
out = fh.read()
65+
66+
assert "nf-core/testpipeline" in out
67+
assert "# input = null" in out

tests/test_pipelines.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import shutil
22
from unittest import TestCase
33

4+
import pytest
5+
46
from nf_core.utils import Pipeline
57

68
from .utils import create_tmp_pipeline
@@ -24,3 +26,7 @@ def _make_pipeline_copy(self):
2426
new_pipeline = self.tmp_dir / "nf-core-testpipeline-copy"
2527
shutil.copytree(self.pipeline_dir, new_pipeline)
2628
return new_pipeline
29+
30+
@pytest.fixture(autouse=True)
31+
def _use_caplog(self, caplog):
32+
self.caplog = caplog

0 commit comments

Comments
 (0)