Skip to content

Commit

Permalink
Nextflow runner: throw error when using a non-existent argument in a …
Browse files Browse the repository at this point in the history
…map passed to fromState (#793)
  • Loading branch information
DriesSchaumont authored Jan 7, 2025
1 parent 7ebb587 commit 557abe6
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 2 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Viash 0.x.x (yyyy-MM-dd): TODO Add title

TODO add summary
## NEW FEATURES

* `Nextflow` runner: specifying a non-existent argument as a hashmap key for `fromState` and `toState` now raises an error (PR #793).

# Viash 0.9.1 (2024-12-16): Enhanced nextflow support and Scala 3 update

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,16 @@ def _processFromState(fromState, key_, config_) {
assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings"
assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings"
def fromStateMap = fromState.clone()
def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName}
def allArgumentNames = config_.allArguments.collect{it.plainName}
def requiredInputNames = config_.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName}
// turn the map into a closure to be used later on
fromState = { it ->
def state = it[1]
assert state instanceof Map : "Error in module '$key_': the state is not a Map"
def data = fromStateMap.collectMany{newkey, origkey ->
if (!allArgumentNames.contains(newkey)) {
throw new Exception("Error processing fromState for '$key_': invalid argument '$newkey'")
}
// check whether newkey corresponds to a required argument
if (state.containsKey(origkey)) {
[[newkey, state[origkey]]]
Expand Down Expand Up @@ -161,6 +165,7 @@ def _processToState(toState, key_, config_) {
assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings"
assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings"
def toStateMap = toState.clone()
def allArgumentNames = config_.allArguments.collect{it.plainName}
def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName}
// turn the map into a closure to be used later on
toState = { it ->
Expand All @@ -169,6 +174,9 @@ def _processToState(toState, key_, config_) {
assert output instanceof Map : "Error in module '$key_': the output is not a Map"
assert state instanceof Map : "Error in module '$key_': the state is not a Map"
def extraEntries = toStateMap.collectMany{newkey, origkey ->
if (!allArgumentNames.contains(origkey)) {
throw new Exception("Error processing toState for '$key_': invalid argument '$origkey'")
}
// check whether newkey corresponds to a required argument
if (output.containsKey(origkey)) {
[[newkey, output[origkey]]]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: invalid_fromstate_argument
namespace: test_wfs
resources:
- type: nextflow_script
path: main.nf
entrypoint: base
# TODO: make absolute when the ns build uses the right CWD
- path: ../../../resources
dependencies:
- name: sub_workflow
platforms:
- type: nextflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
workflow base {
take: input_ch
main:

// generate list from 0 to 1000
ch = Channel.fromList(0..1000)
| map { num ->
// create temporary file
def file = tempFile()
file.write("num: $num")

["num$num", [ file: file ], ["num": num]]
}
| sub_workflow.run(
fromState: [
"file": "file",
"thisargumentdoesnotexist": "file", // this should raise
],
toState: {id, output, state ->
def newState = [
"step1_output": output.output,
"num": state.num,
"file": state.file,
"thisargumentdoesnotexist": "foo"
]
return newState
}
)


emit:
input_ch
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: invalid_tostate_argument
namespace: test_wfs
resources:
- type: nextflow_script
path: main.nf
entrypoint: base
# TODO: make absolute when the ns build uses the right CWD
- path: ../../../resources
dependencies:
- name: sub_workflow
platforms:
- type: nextflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
workflow base {
take: input_ch
main:

// generate list from 0 to 1000
ch = Channel.fromList(0..1000)
| map { num ->
// create temporary file
def file = tempFile()
file.write("num: $num")

["num$num", [ file: file ], ["num": num]]
}
| sub_workflow.run(
fromState: [
"file": "file",
],
toState: [
"step1_output": "output",
"file": "file",
"newkey": "thisargumentdoesnotexist" // This should raise
]
)


emit:
input_ch
}
27 changes: 27 additions & 0 deletions src/test/scala/io/viash/runners/nextflow/NextflowScriptTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,33 @@ class NextflowScriptTest extends AnyFunSuite with BeforeAndAfterAll {
}


test("Test invalid argument in fromState map", DockerTest, NextflowTest) {
val (exitCode, stdOut, stdErr) = NextflowTestHelper.run(
mainScript = "target/nextflow/test_wfs/invalid_fromstate_argument/main.nf",
args = List(
"--publish_dir", "output"
),
cwd = tempFolFile
)

assert(exitCode == 1, s"\nexit code was $exitCode\nStd output:\n$stdOut\nStd error:\n$stdErr")
assert(stdOut.contains("Error processing fromState for 'sub_workflow': invalid argument 'thisargumentdoesnotexist'"))
}

test("Test invalid argument in toState map", DockerTest, NextflowTest) {
val (exitCode, stdOut, stdErr) = NextflowTestHelper.run(
mainScript = "target/nextflow/test_wfs/invalid_tostate_argument/main.nf",
args = List(
"--publish_dir", "output"
),
cwd = tempFolFile
)

assert(exitCode == 1, s"\nexit code was $exitCode\nStd output:\n$stdOut\nStd error:\n$stdErr")
assert(stdOut.contains("Error processing toState for 'sub_workflow': invalid argument 'thisargumentdoesnotexist'"))
}


test("Run multiple output channels standalone", NextflowTest) {
val (exitCode, stdOut, stdErr) = NextflowTestHelper.run(
mainScript = "target/nextflow/multiple_emit_channels/main.nf",
Expand Down

0 comments on commit 557abe6

Please sign in to comment.