Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
39 changes: 39 additions & 0 deletions docs/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,45 @@ In general, multiple input channels should be used to process *combinations* of

See also: {ref}`channel-types`.

(process-input-default-values)=

### Inputs with default values

:::{versionadded} 23.06.0-edge
:::

Process inputs can be defined with a default value, so that they don't have to be specified when calling the process. Default values are useful for process inputs that aren't always used. For example:

```groovy
process foo {
input:
val metadata
path ('star/*'), defaultValue: []
path ('hisat2/*'), defaultValue: []
path ('salmon/*'), defaultValue: []
output:
stdout
script:
"""
echo 'metadata: ${metadata}'
[[ -d star ]] && ls star || echo 'skipping star directory'
[[ -d hisat2 ]] && ls hisat2 || echo 'skipping hisat2 directory'
[[ -d salmon ]] && ls salmon || echo 'skipping salmon directory'
"""
}

workflow {
metadata = Channel.of('foo')
foo(metadata) | view
}
```

There are a few important caveats to keep in mind when using default values:

- Inputs with a default value must be declared after inputs without a default value.

- If you provide a value for an input, then you must also provide values for all inputs that precede it, even if those inputs have default values. In the example above, if you wanted to provide a value for the `salmon` input, you would have to provide values for the `star` and `hisat2` inputs as well, regardless of whether you use the default values for those inputs.

(process-output)=

## Outputs
Expand Down
14 changes: 12 additions & 2 deletions modules/nextflow/src/main/groovy/nextflow/script/ProcessDef.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,27 @@ class ProcessDef extends BindableDef implements IterableDef, ChainableDef {

// get params
final params = ChannelOut.spread(args)

// sanity check
if( params.size() != declaredInputs.size() )
if( params.size() > declaredInputs.size() )
throw new ScriptRuntimeException(missMatchErrMessage(processName, declaredInputs.size(), params.size()))

// set input channels
for( int i=0; i<params.size(); i++ ) {
final inParam = (declaredInputs[i] as BaseInParam)
final inParam = (BaseInParam)declaredInputs[i]
inParam.setFrom(params[i])
inParam.init()
}

for( int i=params.size(); i<declaredInputs.size(); i++ ) {
final inParam = (BaseInParam)declaredInputs[i]
if( inParam.defaultValue == null )
throw new ScriptRuntimeException(missMatchErrMessage(processName, declaredInputs.size(), params.size()))

inParam.setFrom(CH.value(inParam.defaultValue))
inParam.init()
}

// set output channels
// note: the result object must be an array instead of a List to allow process
// composition ie. to use the process output as the input in another process invocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ abstract class BaseInParam extends BaseParam implements InParam {

protected owner

protected defaultValue

/**
* The channel to which the input value is bound
*/
Expand Down Expand Up @@ -151,6 +153,18 @@ abstract class BaseInParam extends BaseParam implements InParam {
throw new IllegalArgumentException("Invalid process input definition")
}

/**
* @return The parameter default value
*/
Object getDefaultValue() {
return defaultValue
}

BaseInParam setDefaultValue(Object value) {
this.defaultValue = value
return this
}

BaseInParam bind( Object obj ) {
this.bindObject = obj
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class AwsS3Config {
this.debug = opts.debug as Boolean
this.endpoint = opts.endpoint ?: SysEnv.get('AWS_S3_ENDPOINT')
this.storageClass = parseStorageClass((opts.storageClass ?: opts.uploadStorageClass) as String) // 'uploadStorageClass' is kept for legacy purposes
this.storageEncryption = parseStorageEncryption(opts.storageEncryption as String)
this.storageKmsKeyId = opts.storageKmsKeyId
this.storageEncryption = parseStorageEncryption(opts.storageEncryption as String) ?: SysEnv.get('NXF_AWS_SSE_MODE')
this.storageKmsKeyId = opts.storageKmsKeyId ?: SysEnv.get('NXF_AWS_SSE_KMS_KEY_ID')
this.pathStyleAccess = opts.s3PathStyleAccess as Boolean
this.s3Acl = parseS3Acl(opts.s3Acl as String)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,18 @@ class AwsS3ConfigTest extends Specification {
SysEnv.pop()

}

def 'should set storage encryption via env variable' () {
given:
SysEnv.push([NXF_AWS_SSE_MODE: 'aws:kms', NXF_AWS_SSE_KMS_KEY_ID: 'xyz1'])

when:
def client = new AwsS3Config([:])
then:
client.storageKmsKeyId == 'xyz1'
client.storageEncryption == 'aws:kms'

cleanup:
SysEnv.pop()
}
}
23 changes: 23 additions & 0 deletions tests/process-input-default-value.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env nextflow

process foo {
input:
val metadata
path ('star/*'), defaultValue: []
path ('hisat2/*'), defaultValue: []
path ('salmon/*'), defaultValue: []
output:
stdout
script:
"""
echo 'metadata: ${metadata}'
[[ -d star ]] && ls star || echo 'skipping star directory'
[[ -d hisat2 ]] && ls hisat2 || echo 'skipping hisat2 directory'
[[ -d salmon ]] && ls salmon || echo 'skipping salmon directory'
"""
}

workflow {
metadata = Channel.of('foo')
foo(metadata) | view
}