Skip to content

Commit 79e1c57

Browse files
committed
Workflow params
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent bfa67ca commit 79e1c57

40 files changed

Lines changed: 882 additions & 187 deletions

docs/migrations/25-10.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ This page summarizes the upcoming changes in Nextflow 25.10, which will be relea
88
This page is a work in progress and will be updated as features are finalized. It should not be considered complete until the 25.10 release.
99
:::
1010

11+
## New features
12+
13+
<h3>Workflow params</h3>
14+
15+
The `params` block is a new way to declare pipeline parameters in a Nextflow script:
16+
17+
```nextflow
18+
params {
19+
// Path to input data.
20+
input: Path
21+
22+
// Whether to save intermediate files.
23+
save_intermeds: Boolean = false
24+
}
25+
26+
workflow {
27+
println "params.input = ${params.input}"
28+
println "params.save_intermeds = ${params.save_intermeds}"
29+
}
30+
```
31+
32+
This syntax allows you to declare all parameters in one place with explicit type annotations, and it allows Nextflow to validate parameters at runtime.
33+
34+
See {ref}`workflow-params-def` for details.
35+
1136
## Enhancements
1237

1338
<h3>New syntax for workflow handlers</h3>
@@ -35,4 +60,6 @@ This syntax is simpler and easier to use with the {ref}`strict syntax <strict-sy
3560

3661
## Deprecations
3762

63+
- The legacy type detection of CLI parameters is disabled when using the strict syntax (`NXF_SYNTAX_PARSER=v2`). {ref}`Legacy parameters <workflow-params-legacy>` in the strict syntax should not rely on legacy type detection. Alternatively, use the new `params` block to convert CLI parameters based on their type annotations. Legacy type detection can be disabled globally by setting the environment variable `NXF_DISABLE_PARAMS_TYPE_DETECTION=true`.
64+
3865
- The use of workflow handlers in the configuration file has been deprecated. You should define workflow handlers in the pipeline script or a plugin instead. See {ref}`config-workflow-handlers` for details.

docs/reference/syntax.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ A Nextflow script may contain the following top-level declarations:
2828
- Shebang
2929
- Feature flags
3030
- Include declarations
31-
- Parameter declarations
31+
- Params block
32+
- Parameter declarations (legacy)
3233
- Workflow definitions
3334
- Process definitions
3435
- Function definitions
@@ -107,9 +108,22 @@ The following definitions can be included:
107108
- Processes
108109
- Named workflows
109110

110-
### Parameter
111+
### Params block
111112

112-
A parameter declaration is an assignment. The target should be a pipeline parameter and the source should be an expression:
113+
The params block consists of one or more *parameter declarations*. A parameter declaration consists of a name and an optional default value:
114+
115+
```nextflow
116+
params {
117+
input: Path
118+
save_intermeds: Boolean = false
119+
}
120+
```
121+
122+
Only one params block may be defined in a script.
123+
124+
### Parameter (legacy)
125+
126+
A legacy parameter declaration is an assignment. The target should be a pipeline parameter and the source should be an expression:
113127

114128
```nextflow
115129
params.message = 'Hello world!'

docs/vscode.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The language server parses scripts and config files according to the {ref}`Nextf
2626

2727
When you hover over certain source code elements, such as variable names and function calls, the extension provides a tooltip with related information, such as the definition and/or documentation for the element.
2828

29-
If a [Javadoc](https://en.wikipedia.org/wiki/Javadoc) comment is defined above a workflow, process, or function, the extension will include the contents of the comment in hover hints. The following is an example Javadoc comment:
29+
If a [Javadoc](https://en.wikipedia.org/wiki/Javadoc) comment is defined above a workflow, process, function, or parameter in a `params` block, the extension will include the contents of the comment in hover hints. The following is an example Javadoc comment:
3030

3131
```nextflow
3232
/**

docs/workflow.md

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,60 @@ workflow {
2222
}
2323
```
2424

25-
### Parameters
25+
(workflow-params-def)=
26+
27+
## Parameters
28+
29+
Parameters can be declared in a Nextflow script with the `params` block or with *legacy* parameter declarations.
30+
31+
### Params block
32+
33+
:::{versionadded} 25.10.0
34+
:::
35+
36+
:::{note}
37+
This feature requires the {ref}`strict syntax <strict-syntax-page>` to be enabled (`NXF_SYNTAX_PARSER=v2`).
38+
:::
39+
40+
A script can declare parameters using the `params` block:
41+
42+
```nextflow
43+
params {
44+
// Path to input data.
45+
input: Path
46+
47+
// Whether to save intermediate files.
48+
save_intermeds: Boolean = false
49+
}
50+
```
51+
52+
The following types can be used for parameters:
53+
54+
- {ref}`stdlib-types-boolean`
55+
- {ref}`stdlib-types-float`
56+
- {ref}`stdlib-types-integer`
57+
- {ref}`stdlib-types-path`
58+
- {ref}`stdlib-types-string`
59+
60+
Parameters can be used in the entry workflow:
61+
62+
```nextflow
63+
workflow {
64+
analyze(params.input, params.save_intermeds)
65+
}
66+
```
67+
68+
:::{note}
69+
As a best practice, params should be used only in the entry workflow and passed to workflows and processes as explicit inputs.
70+
:::
71+
72+
The default value can be overridden by the command line, params file, or config file. Parameters from multiple sources are resolved in the order described in {ref}`cli-params`. Parameters specified on the command line are converted to the appropriate type based on the corresponding type annotation.
73+
74+
A parameter that doesn't specify a default value is a *required* param. If a required param is not given a value at runtime, the run will fail.
75+
76+
(workflow-params-legacy)=
77+
78+
### Legacy parameters
2679

2780
Parameters can be declared by assigning a `params` property to a default value:
2881

@@ -38,10 +91,6 @@ workflow {
3891
}
3992
```
4093

41-
:::{note}
42-
As a best practice, params should be used only in the entry workflow and passed to workflows and processes as explicit inputs.
43-
:::
44-
4594
The default value can be overridden by the command line, params file, or config file. Parameters from multiple sources are resolved in the order described in {ref}`cli-params`.
4695

4796
## Named workflows

modules/nextflow/src/main/groovy/nextflow/Session.groovy

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ class Session implements ISession {
120120
*/
121121
ScriptBinding binding
122122

123+
/**
124+
* Params that were specified on the command line.
125+
*/
126+
Map cliParams
127+
128+
/**
129+
* Params that were specified in the configuration.
130+
*/
131+
Map configParams
132+
123133
/**
124134
* Holds the configuration object
125135
*/
@@ -425,7 +435,7 @@ class Session implements ISession {
425435
/**
426436
* Initialize the session workDir, libDir, baseDir and scriptName variables
427437
*/
428-
Session init( ScriptFile scriptFile, List<String> args=null ) {
438+
Session init( ScriptFile scriptFile, List<String> args=null, Map<String,?> cliParams=null, Map<String,?> configParams=null ) {
429439

430440
if(!workDir.mkdirs()) throw new AbortOperationException("Cannot create work-dir: $workDir -- Make sure you have write permissions or specify a different directory by using the `-w` command line option")
431441
log.debug "Work-dir: ${workDir.toUriString()} [${FileHelper.getPathFsType(workDir)}]"
@@ -452,6 +462,8 @@ class Session implements ISession {
452462
this.workflowMetadata = new WorkflowMetadata(this, scriptFile)
453463

454464
// configure script params
465+
this.cliParams = cliParams
466+
this.configParams = configParams
455467
binding.setParams( (Map)config.params )
456468
binding.setArgs( new ScriptRunner.ArgsList(args) )
457469

modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,18 @@ class CmdRun extends CmdBase implements HubOptions {
325325
// -- specify the arguments
326326
final scriptFile = getScriptFile(pipeline)
327327

328+
// -- load command line params
329+
final baseDir = scriptFile.parent
330+
final cliParams = parsedParams(ConfigBuilder.getConfigVars(baseDir))
331+
328332
// create the config object
329333
final builder = new ConfigBuilder()
330334
.setOptions(launcher.options)
331335
.setCmdRun(this)
332-
.setBaseDir(scriptFile.parent)
333-
final config = builder .build()
336+
.setBaseDir(baseDir)
337+
.setCliParams(cliParams)
338+
final config = builder.build()
339+
final configParams = builder.getConfigParams()
334340

335341
// check DSL syntax in the config
336342
launchInfo(config, scriptFile)
@@ -376,7 +382,7 @@ class CmdRun extends CmdBase implements HubOptions {
376382
}
377383

378384
// -- run it!
379-
runner.execute(scriptArgs, this.entryName)
385+
runner.execute(scriptArgs, cliParams, configParams, this.entryName)
380386
}
381387

382388
protected void printBanner() {
@@ -698,7 +704,7 @@ class CmdRun extends CmdBase implements HubOptions {
698704
}
699705

700706
static protected parseParamValue(String str) {
701-
if ( SysEnv.get('NXF_DISABLE_PARAMS_TYPE_DETECTION') )
707+
if ( SysEnv.get('NXF_DISABLE_PARAMS_TYPE_DETECTION') || NF.isSyntaxParserV2() )
702708
return str
703709

704710
if ( str == null ) return null

modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class ConfigBuilder {
5858

5959
Path currentDir
6060

61+
Map<String,?> cliParams
62+
6163
boolean showAllProfiles
6264

6365
String profile = DEFAULT_PROFILE
@@ -78,9 +80,11 @@ class ConfigBuilder {
7880

7981
Map<String,String> env = new HashMap<>(SysEnv.get())
8082

81-
List<String> warnings = new ArrayList<>(10);
83+
List<String> warnings = new ArrayList<>(10)
84+
85+
Map<String,Object> declaredParams = [:]
8286

83-
{
87+
ConfigBuilder() {
8488
setHomeDir(Const.APP_HOME_DIR)
8589
setCurrentDir(Paths.get('.'))
8690
}
@@ -111,6 +115,11 @@ class ConfigBuilder {
111115
return this
112116
}
113117

118+
ConfigBuilder setCliParams( Map<String,?> cliParams ) {
119+
this.cliParams = cliParams
120+
return this
121+
}
122+
114123
ConfigBuilder setBaseDir( Path path ) {
115124
this.baseDir = path.complete()
116125
return this
@@ -159,6 +168,10 @@ class ConfigBuilder {
159168
return this
160169
}
161170

171+
Map<String,Object> getConfigParams() {
172+
return declaredParams
173+
}
174+
162175
static private wrapValue( value ) {
163176
if( !value )
164177
return ''
@@ -324,11 +337,11 @@ class ConfigBuilder {
324337
// this is needed to make sure to reuse the same
325338
// instance of the config vars across different instances of the ConfigBuilder
326339
// and prevent multiple parsing of the same params file (which can even be remote resource)
327-
return cacheableConfigVars(baseDir)
340+
return getConfigVars(baseDir)
328341
}
329342

330343
@Memoized
331-
static private Map cacheableConfigVars(Path base) {
344+
static Map getConfigVars(Path base) {
332345
final binding = new HashMap(10)
333346
binding.put('baseDir', base)
334347
binding.put('projectDir', base)
@@ -348,8 +361,8 @@ class ConfigBuilder {
348361
.setIgnoreIncludes(ignoreIncludes)
349362
ConfigObject result = new ConfigObject()
350363

351-
if( cmdRun && (cmdRun.hasParams()) )
352-
parser.setParams(cmdRun.parsedParams(configVars()))
364+
if( cliParams )
365+
parser.setParams(cliParams)
353366

354367
// add the user specified environment to the session env
355368
env.sort().each { name, value -> result.env.put(name,value) }
@@ -380,7 +393,7 @@ class ConfigBuilder {
380393
}
381394

382395
if( validateProfile ) {
383-
checkValidProfile(parser.getProfiles())
396+
checkValidProfile(parser.getDeclaredProfiles())
384397
}
385398

386399
}
@@ -414,6 +427,7 @@ class ConfigBuilder {
414427
final config = parse0(parser, entry)
415428
if( NF.getSyntaxParserVersion() == 'v1' )
416429
validate(config, entry)
430+
declaredParams.putAll(parser.getDeclaredParams())
417431
result.merge(config)
418432
}
419433

@@ -723,8 +737,8 @@ class ConfigBuilder {
723737
}
724738

725739
// -- add the command line parameters to the 'taskConfig' object
726-
if( cmdRun.hasParams() )
727-
config.params = mergeMaps( (Map)config.params, cmdRun.parsedParams(configVars()), NF.strictMode )
740+
if( cliParams )
741+
config.params = mergeMaps( (Map)config.params, cliParams, NF.strictMode )
728742

729743
if( cmdRun.withoutDocker && config.docker instanceof Map ) {
730744
// disable docker execution

modules/nextflow/src/main/groovy/nextflow/config/ConfigParser.groovy

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ interface ConfigParser {
6767
*/
6868
ConfigParser setParams(Map vars)
6969

70+
/**
71+
* Set the profiles that should be applied.
72+
*/
73+
ConfigParser setProfiles(List<String> profiles)
74+
7075
/**
7176
* Parse a config object from the given source.
7277
*/
@@ -75,13 +80,13 @@ interface ConfigParser {
7580
ConfigObject parse(Path path)
7681

7782
/**
78-
* Set the profiles that should be applied.
83+
* Get the set of declared profiles.
7984
*/
80-
ConfigParser setProfiles(List<String> profiles)
85+
Set<String> getDeclaredProfiles()
8186

8287
/**
83-
* Get the set of available profiles.
88+
* Get the map of declared params.
8489
*/
85-
Set<String> getProfiles()
90+
Map<String,Object> getDeclaredParams()
8691

8792
}

modules/nextflow/src/main/groovy/nextflow/config/parser/v1/ConfigParserV1.groovy

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,15 @@ class ConfigParserV1 implements ConfigParser {
123123
}
124124

125125
@Override
126-
Set<String> getProfiles() {
126+
Set<String> getDeclaredProfiles() {
127127
Collections.unmodifiableSet(conditionalNames)
128128
}
129129

130+
@Override
131+
Map<String,Object> getDeclaredParams() {
132+
[:]
133+
}
134+
130135
private Grengine getGrengine() {
131136
if( grengine ) {
132137
return grengine

0 commit comments

Comments
 (0)