Skip to content

Commit 4f5b885

Browse files
GlassOfWhiskeymr-c
authored andcommitted
Added loop support
1 parent 9d929b6 commit 4f5b885

22 files changed

+879
-5
lines changed

Workflow.yml

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,56 @@ $graph:
443443
to connect the output value to downstream parameters.
444444
445445
446+
- name: LoopInput
447+
type: record
448+
extends: [Identified, Sink]
449+
fields:
450+
- name: source
451+
doc: |
452+
Specifies one or more of the step output parameters that will
453+
provide input to the loop iterations after the first one (inputs
454+
of the first iteration are the step input parameters).
455+
jsonldPredicate:
456+
"_id": "cwl:source"
457+
"_type": "@id"
458+
refScope: 1
459+
type:
460+
- string?
461+
- string[]?
462+
- name: default
463+
type: ["null", File, Directory, Any]
464+
doc: |
465+
The default value for this parameter to use if either there is no
466+
`source` field, or the value produced by the `source` is `null`. The
467+
default must be applied prior to scattering or evaluating `valueFrom`.
468+
jsonldPredicate:
469+
_id: "sld:default"
470+
noLinkCheck: true
471+
- name: valueFrom
472+
type:
473+
- "null"
474+
- string
475+
- Expression
476+
jsonldPredicate: "cwl:valueFrom"
477+
doc: |
478+
To use valueFrom, [StepInputExpressionRequirement](#StepInputExpressionRequirement) must
479+
be specified in the workflow or workflow step requirements.
480+
481+
If `valueFrom` is a constant string value, use this as the value for
482+
this input parameter.
483+
484+
If `valueFrom` is a parameter reference or expression, it must be
485+
evaluated to yield the actual value to be assigned to the input field.
486+
487+
The `self` value in the parameter reference or expression must be
488+
`null` if there is no `source` field, or the value of the
489+
parameter(s) specified in the `source` field.
490+
491+
The value of `inputs` in the parameter reference or expression must be
492+
the input object to the previous iteration of the workflow step (or the initial
493+
inputs for the first iteration).
494+
495+
446496
- name: ScatterMethod
447497
type: enum
448498
docParent: "#WorkflowStep"
@@ -453,6 +503,15 @@ $graph:
453503
- flat_crossproduct
454504

455505

506+
- name: LoopOutputMethod
507+
type: enum
508+
docParent: "#WorkflowStep"
509+
doc: The loop output method, as described in [workflow step loop](#WorkflowStep).
510+
symbols:
511+
- last
512+
- all
513+
514+
456515
- name: WorkflowStep
457516
type: record
458517
extends: [Identified, Labeled, sld:Documented]
@@ -505,7 +564,7 @@ $graph:
505564
output arrays must be flattened to a single level, but otherwise listed in the
506565
order that the input arrays are listed in the `scatter` field.
507566
508-
# Conditional execution (Optional)
567+
# Conditional and iterative execution (Optional)
509568
510569
Conditional execution makes execution of a step conditional on an
511570
expression. A step that is not executed is "skipped". A skipped
@@ -522,12 +581,37 @@ $graph:
522581
input object (or individual scatter job), and returns a boolean
523582
value. It is an error if this expression returns a value other
524583
than `true` or `false`.
525-
526-
Conditionals in CWL are an optional feature and are not required
527-
to be implemented by all consumers of CWL documents. An
528-
implementation that does not support conditionals must return a
584+
585+
The `loop` field controls iterative execution. It defines the input
586+
parameters of the loop iterations after the first one (inputs of the
587+
first iteration are the step input parameters, as usual). If no
588+
`loop` rule is specified for a given step `in` field, the initial
589+
value is kept constant among all iterations.
590+
591+
When a `loop` field is present, the `when` field is mandatory. It is
592+
evaluated before each loop iteration and acts as a termination condition:
593+
as soon as the `when` expression evaluates to `false`, the loop terminates
594+
and the step outputs are propagated to the subsequent workflow steps.
595+
596+
The `outputMethod` field describes how to deal with loop outputs after
597+
termination:
598+
599+
* **last** specifies that only the last computed element for each output
600+
parameter should be propagated to the subsequenct steps. This is the
601+
default value.
602+
603+
* **all** specifies that a single ordered array with all output values
604+
computed at the end of each loop iteration should be propagated to the
605+
subsequent steps.
606+
607+
Conditionals and iterative execution in CWL are an optional features
608+
and are not required to be implemented by all consumers of CWL documents.
609+
An implementation that does not support conditionals must return a
529610
fatal error when attempting to execute a workflow that uses
530611
conditional constructs the implementation does not support.
612+
613+
At this time, the `loop` field is not compatible with the `scatter` field.
614+
Combining the two in the same step will produce an error.
531615
532616
# Subworkflows
533617
@@ -619,6 +703,24 @@ $graph:
619703
jsonldPredicate:
620704
"_id": "cwl:scatterMethod"
621705
"_type": "@vocab"
706+
- name: loop
707+
doc: |
708+
Defines the input parameters of the loop iterations after the first one
709+
(inputs of the first iteration are the step input parameters). If no
710+
`loop` rule is specified for a given step `in` field, the initial value
711+
is kept constant among all iterations.
712+
type: LoopInput[]?
713+
jsonldPredicate:
714+
_id: "cwl:loop"
715+
mapSubject: id
716+
mapPredicate: source
717+
- name: outputMethod
718+
doc: |
719+
Required if `loop` is defined.
720+
type: LoopOutputMethod?
721+
jsonldPredicate:
722+
"_id": "cwl:outputMethod"
723+
"_type": "@vocab"
622724

623725

624726
- name: Workflow

conformance_tests.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,3 +3525,5 @@
35253525
an_array_of_mixed_booleans: [ false, true, false ]
35263526
an_array_of_trues: [ true, true, true ]
35273527
an_int: 42
3528+
3529+
- $import: tests/loop/test-index.yaml
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
inputs:
7+
i1: int
8+
outputs:
9+
o1:
10+
type: int[]
11+
outputSource: subworkflow/o1
12+
steps:
13+
subworkflow:
14+
when: $(inputs.i1 < 1)
15+
loop:
16+
i1: o1
17+
outputMethod: all
18+
run:
19+
class: ExpressionTool
20+
inputs:
21+
i1: int
22+
outputs:
23+
o1: int
24+
expression: >
25+
${return {'o1': inputs.i1 + 1};}
26+
in:
27+
i1: i1
28+
out: [o1]

tests/loop/all-output-loop.cwl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
inputs:
7+
i1: int
8+
outputs:
9+
o1:
10+
type: int[]
11+
outputSource: subworkflow/o1
12+
steps:
13+
subworkflow:
14+
when: $(inputs.i1 < 10)
15+
loop:
16+
i1: o1
17+
outputMethod: all
18+
run:
19+
class: ExpressionTool
20+
inputs:
21+
i1: int
22+
outputs:
23+
o1: int
24+
expression: >
25+
${return {'o1': inputs.i1 + 1};}
26+
in:
27+
i1: i1
28+
out: [o1]

tests/loop/default-value-loop.cwl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
ScatterFeatureRequirement: {}
7+
SubworkflowFeatureRequirement: {}
8+
inputs:
9+
i1: int
10+
outputs:
11+
o1:
12+
type: int[]
13+
outputSource: loop/o1
14+
pickValue: all_non_null
15+
steps:
16+
loop:
17+
when: $(inputs.i1 < 20)
18+
loop:
19+
i1:
20+
source: o1
21+
default: 5
22+
outputMethod: all
23+
run:
24+
class: Workflow
25+
inputs:
26+
i1: int
27+
outputs:
28+
o1:
29+
type: int?
30+
outputSource: big_values/o1
31+
steps:
32+
big_values:
33+
when: $(inputs.i1 >= 5)
34+
run:
35+
class: ExpressionTool
36+
inputs:
37+
i1: int
38+
outputs:
39+
o1: int
40+
expression: >
41+
${return {'o1': inputs.i1 + 3};}
42+
in:
43+
i1: i1
44+
out: [ o1 ]
45+
in:
46+
i1: i1
47+
out: [ o1 ]

tests/loop/invalid-loop-scatter.cwl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
ScatterFeatureRequirement: {}
7+
SubworkflowFeatureRequirement: {}
8+
inputs:
9+
i1: int[]
10+
i2: int
11+
outputs:
12+
o1:
13+
type: int[]
14+
outputSource: subworkflow/o1
15+
steps:
16+
subworkflow:
17+
when: $(inputs.i1 < 10)
18+
loop:
19+
i1: o1
20+
outputMethod: last
21+
run:
22+
class: ExpressionTool
23+
inputs:
24+
i1: int
25+
i2: int
26+
outputs:
27+
o1: int
28+
expression: >
29+
${return {'o1': inputs.i1 + inputs.i2};}
30+
in:
31+
i1: i1
32+
i2: i2
33+
scatter: i1
34+
out: [o1]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
ScatterFeatureRequirement: {}
7+
SubworkflowFeatureRequirement: {}
8+
inputs:
9+
i1: int
10+
outputs:
11+
o1:
12+
type: int[]
13+
outputSource: [loop/osmall, loop/obig]
14+
linkMerge: merge_flattened
15+
pickValue: all_non_null
16+
steps:
17+
loop:
18+
when: $(inputs.i1 < 20)
19+
loop:
20+
i1:
21+
source: [ osmall, obig ]
22+
pickValue: the_only_non_null
23+
outputMethod: all
24+
run:
25+
class: Workflow
26+
inputs:
27+
i1: int
28+
outputs:
29+
osmall:
30+
type: int?
31+
outputSource: small_values/o1
32+
obig:
33+
type: int?
34+
outputSource: big_values/o1
35+
steps:
36+
small_values:
37+
when: $(inputs.i1 < 5)
38+
run:
39+
class: ExpressionTool
40+
inputs:
41+
i1: int
42+
outputs:
43+
o1: int
44+
expression: >
45+
${return {'o1': inputs.i1 + 1};}
46+
in:
47+
i1: i1
48+
out: [o1]
49+
big_values:
50+
when: $(inputs.i1 >= 5)
51+
run:
52+
class: ExpressionTool
53+
inputs:
54+
i1: int
55+
outputs:
56+
o1: int
57+
expression: >
58+
${return {'o1': inputs.i1 + 3};}
59+
in:
60+
i1: i1
61+
out: [ o1 ]
62+
in:
63+
i1: i1
64+
out: [osmall, obig]

tests/loop/invalid-no-when.cwl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env cwl-runner
2+
cwlVersion: v1.3.0-dev1
3+
class: Workflow
4+
requirements:
5+
InlineJavascriptRequirement: {}
6+
ScatterFeatureRequirement: {}
7+
SubworkflowFeatureRequirement: {}
8+
inputs:
9+
i1: int
10+
i2: int
11+
outputs:
12+
o1:
13+
type: int
14+
outputSource: subworkflow/o1
15+
steps:
16+
subworkflow:
17+
loop:
18+
i1: o1
19+
outputMethod: last
20+
run:
21+
class: ExpressionTool
22+
inputs:
23+
i1: int
24+
i2: int
25+
outputs:
26+
o1: int
27+
expression: >
28+
${return {'o1': inputs.i1 + inputs.i2};}
29+
in:
30+
i1: i1
31+
i2: i2
32+
out: [o1]

0 commit comments

Comments
 (0)