Skip to content

Commit

Permalink
Added loop support
Browse files Browse the repository at this point in the history
  • Loading branch information
GlassOfWhiskey committed Dec 26, 2022
1 parent b84ec22 commit e62e5b3
Show file tree
Hide file tree
Showing 23 changed files with 881 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# JetBrains
.idea/
112 changes: 107 additions & 5 deletions Workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,56 @@ $graph:
to connect the output value to downstream parameters.
- name: LoopInput
type: record
extends: [Identified, Sink]
fields:
- name: source
doc: |
Specifies one or more of the step output parameters that will
provide input to the loop iterations after the first one (inputs
of the first iteration are the step input parameters).
jsonldPredicate:
"_id": "cwl:source"
"_type": "@id"
refScope: 2
type:
- string?
- string[]?
- name: default
type: ["null", File, Directory, Any]
doc: |
The default value for this parameter to use if either there is no
`source` field, or the value produced by the `source` is `null`. The
default must be applied prior to scattering or evaluating `valueFrom`.
jsonldPredicate:
_id: "sld:default"
noLinkCheck: true
- name: valueFrom
type:
- "null"
- string
- Expression
jsonldPredicate: "cwl:valueFrom"
doc: |
To use valueFrom, [StepInputExpressionRequirement](#StepInputExpressionRequirement) must
be specified in the workflow or workflow step requirements.
If `valueFrom` is a constant string value, use this as the value for
this input parameter.
If `valueFrom` is a parameter reference or expression, it must be
evaluated to yield the actual value to be assigned to the input field.
The `self` value in the parameter reference or expression must be
`null` if there is no `source` field, or the value of the
parameter(s) specified in the `source` field.
The value of `inputs` in the parameter reference or expression must be
the input object to the previous iteration of the workflow step (or the initial
inputs for the first iteration).

- name: ScatterMethod
type: enum
docParent: "#WorkflowStep"
Expand All @@ -455,6 +505,15 @@ $graph:
- flat_crossproduct


- name: LoopOutputMethod
type: enum
docParent: "#WorkflowStep"
doc: The loop output method, as described in [workflow step loop](#WorkflowStep).
symbols:
- last
- all


- name: WorkflowStep
type: record
extends: [Identified, Labeled, sld:Documented]
Expand Down Expand Up @@ -507,7 +566,7 @@ $graph:
output arrays must be flattened to a single level, but otherwise listed in the
order that the input arrays are listed in the `scatter` field.
# Conditional execution (Optional)
# Conditional and iterative execution (Optional)
Conditional execution makes execution of a step conditional on an
expression. A step that is not executed is "skipped". A skipped
Expand All @@ -524,12 +583,37 @@ $graph:
input object (or individual scatter job), and returns a boolean
value. It is an error if this expression returns a value other
than `true` or `false`.
Conditionals in CWL are an optional feature and are not required
to be implemented by all consumers of CWL documents. An
implementation that does not support conditionals must return a
The `loop` field controls iterative execution. It defines the input
parameters of the loop iterations after the first one (inputs of the
first iteration are the step input parameters, as usual). If no
`loop` rule is specified for a given step `in` field, the initial
value is kept constant among all iterations.
When a `loop` field is present, the `when` field is mandatory. It is
evaluated before each loop iteration and acts as a termination condition:
as soon as the `when` expression evaluates to `false`, the loop terminates
and the step outputs are propagated to the subsequent workflow steps.
The `outputMethod` field describes how to deal with loop outputs after
termination:
* **last** specifies that only the last computed element for each output
parameter should be propagated to the subsequenct steps. This is the
default value.
* **all** specifies that a single ordered array with all output values
computed at the end of each loop iteration should be propagated to the
subsequent steps.
Conditionals and iterative execution in CWL are an optional features
and are not required to be implemented by all consumers of CWL documents.
An implementation that does not support conditionals must return a
fatal error when attempting to execute a workflow that uses
conditional constructs the implementation does not support.
At this time, the `loop` field is not compatible with the `scatter` field.
Combining the two in the same step will produce an error.
# Subworkflows
Expand Down Expand Up @@ -621,6 +705,24 @@ $graph:
jsonldPredicate:
"_id": "cwl:scatterMethod"
"_type": "@vocab"
- name: loop
doc: |
Defines the input parameters of the loop iterations after the first one
(inputs of the first iteration are the step input parameters). If no
`loop` rule is specified for a given step `in` field, the initial value
is kept constant among all iterations.
type: LoopInput[]?
jsonldPredicate:
_id: "cwl:loop"
mapSubject: id
mapPredicate: source
- name: outputMethod
doc: |
Required if `loop` is defined.
type: LoopOutputMethod?
jsonldPredicate:
"_id": "cwl:outputMethod"
"_type": "@vocab"


- name: Workflow
Expand Down
2 changes: 2 additions & 0 deletions conformance_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3392,3 +3392,5 @@
}
}
tags: [ required, command_line_tool ]

- $import: tests/loop/test-index.yaml
28 changes: 28 additions & 0 deletions tests/loop/all-output-loop-no-iteration.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
inputs:
i1: int
outputs:
o1:
type: int[]
outputSource: subworkflow/o1
steps:
subworkflow:
when: $(inputs.i1 < 1)
loop:
i1: o1
outputMethod: all
run:
class: ExpressionTool
inputs:
i1: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + 1};}
in:
i1: i1
out: [o1]
28 changes: 28 additions & 0 deletions tests/loop/all-output-loop.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
inputs:
i1: int
outputs:
o1:
type: int[]
outputSource: subworkflow/o1
steps:
subworkflow:
when: $(inputs.i1 < 10)
loop:
i1: o1
outputMethod: all
run:
class: ExpressionTool
inputs:
i1: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + 1};}
in:
i1: i1
out: [o1]
47 changes: 47 additions & 0 deletions tests/loop/default-value-loop.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
ScatterFeatureRequirement: {}
SubworkflowFeatureRequirement: {}
inputs:
i1: int
outputs:
o1:
type: int[]
outputSource: loop/o1
pickValue: all_non_null
steps:
loop:
when: $(inputs.i1 < 20)
loop:
i1:
source: o1
default: 5
outputMethod: all
run:
class: Workflow
inputs:
i1: int
outputs:
o1:
type: int?
outputSource: big_values/o1
steps:
big_values:
when: $(inputs.i1 >= 5)
run:
class: ExpressionTool
inputs:
i1: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + 3};}
in:
i1: i1
out: [ o1 ]
in:
i1: i1
out: [ o1 ]
34 changes: 34 additions & 0 deletions tests/loop/invalid-loop-scatter.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
ScatterFeatureRequirement: {}
SubworkflowFeatureRequirement: {}
inputs:
i1: int[]
i2: int
outputs:
o1:
type: int[]
outputSource: subworkflow/o1
steps:
subworkflow:
when: $(inputs.i1 < 10)
loop:
i1: o1
outputMethod: last
run:
class: ExpressionTool
inputs:
i1: int
i2: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + inputs.i2};}
in:
i1: i1
i2: i2
scatter: i1
out: [o1]
64 changes: 64 additions & 0 deletions tests/loop/invalid-multi-source-loop-no-requirement.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
ScatterFeatureRequirement: {}
SubworkflowFeatureRequirement: {}
inputs:
i1: int
outputs:
o1:
type: int[]
outputSource: [loop/osmall, loop/obig]
linkMerge: merge_flattened
pickValue: all_non_null
steps:
loop:
when: $(inputs.i1 < 20)
loop:
i1:
source: [ osmall, obig ]
pickValue: the_only_non_null
outputMethod: all
run:
class: Workflow
inputs:
i1: int
outputs:
osmall:
type: int?
outputSource: small_values/o1
obig:
type: int?
outputSource: big_values/o1
steps:
small_values:
when: $(inputs.i1 < 5)
run:
class: ExpressionTool
inputs:
i1: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + 1};}
in:
i1: i1
out: [o1]
big_values:
when: $(inputs.i1 >= 5)
run:
class: ExpressionTool
inputs:
i1: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + 3};}
in:
i1: i1
out: [ o1 ]
in:
i1: i1
out: [osmall, obig]
32 changes: 32 additions & 0 deletions tests/loop/invalid-no-when.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env cwl-runner
cwlVersion: v1.3.0-dev1
class: Workflow
requirements:
InlineJavascriptRequirement: {}
ScatterFeatureRequirement: {}
SubworkflowFeatureRequirement: {}
inputs:
i1: int
i2: int
outputs:
o1:
type: int
outputSource: subworkflow/o1
steps:
subworkflow:
loop:
i1: o1
outputMethod: last
run:
class: ExpressionTool
inputs:
i1: int
i2: int
outputs:
o1: int
expression: >
${return {'o1': inputs.i1 + inputs.i2};}
in:
i1: i1
i2: i2
out: [o1]
Loading

0 comments on commit e62e5b3

Please sign in to comment.