wdl icon indicating copy to clipboard operation
wdl copied to clipboard

runtime struct initialization from select_first()

Open Melkaz opened this issue 3 years ago • 6 comments

Hello,

I'd like to use a boolean that states if I need to run Task2 or not. The problem is when I attempt to catch the optional output of this task, or fallback to another variable.

Error reported by miniwdl:

2022-01-24 14:07:54.828 wdl.w:Workflow workflow Workflow (iftest.wdl Ln 40 Col 1) failed :: dir: "[redacted]", error: "EvalError", message: "unusable runtime struct initializer (member type mismatch, lacking required member, or extra member)", node: "output-updatedStruct", pos: {"source": "[redacted]", "line": 56, "column": 34}
2022-01-24 14:07:54.829 wdl.w:Workflow aborting workflow
2022-01-24 14:07:54.829 miniwdl-run unusable runtime struct initializer (member type mismatch, lacking required member, or extra member) :: error: "EvalError", node: "output-updatedStruct", pos: {"source": "[redacted]", "line": 56, "column": 34}, dir: "[redacted]"

And the reproductible example:

version 1.1

struct MyStruct {
    String prop1
    Boolean prop2
}

task Task1 {
    input {}

    command <<<
        echo "Hello"
    >>>

    output {
        MyStruct myUpdatedStruct = object { 
            "prop1": read_string(stdout()),
            "prop2": true
        }
    }
}

task Task2 {
    input {
        MyStruct myStruct
    }

    command <<<
        echo ~{myStruct.prop1} + " world !"
    >>>

    output {
        MyStruct myUpdatedStruct = object { 
            "prop1": read_string(stdout()), 
            "prop2": false
        }
    }
}

workflow Workflow {
    input {
        Boolean runTask2 = true
    }

    call Task1 as task1 {
    }

    if (runTask2) {
        call Task2 as task2 {
            input:
                myStruct = task1.myUpdatedStruct
        }
    }

    output {
        MyStruct updatedStruct = select_first([task2.myUpdatedStruct, task1.myUpdatedStruct])
    }
}

Am I doing something wrong ?

Thanks for your help :)

Melkaz avatar Jan 24 '22 14:01 Melkaz

Not sure, but WDL 1.1 deprecated object in favor of a specific struct type. I would try using the newer form instead. (It should be supported as it's deprecated in 1.1, not removed, but maybe the 1.1 support has a bug.)

    output {
        MyStruct myUpdatedStruct = MyStruct { 
            "prop1": read_string(stdout()),
            "prop2": true
        }
    }

pshapiro4broad avatar Jan 24 '22 14:01 pshapiro4broad

Thank you, I made the modification, that's way more elegant !

Unfortunately it didn't solve my problem as the error remains. Do you personally use another type of pattern than select_first to handle conditional tasks ?

Updated example:

version 1.1

struct MyStruct {
    String prop1
    Boolean prop2
}

task Task1 {
    input {}

    command <<<
        echo "Hello"
    >>>

    output {
        MyStruct myUpdatedStruct = MyStruct { 
            "prop1": read_string(stdout()),
            "prop2": true
        }
    }
}

task Task2 {
    input {
        MyStruct myStruct
    }

    command <<<
        echo ~{myStruct.prop1} + " world !"
    >>>

    output {
        MyStruct myUpdatedStruct = MyStruct { 
            "prop1": read_string(stdout()), 
            "prop2": false
        }
    }
}

workflow Workflow {
    input {
        Boolean runTask2 = true
    }

    call Task1 as task1 {
    }

    if (runTask2) {
        call Task2 as task2 {
            input:
                myStruct = task1.myUpdatedStruct
        }
    }

    output {
        MyStruct updatedStruct = select_first([task2.myUpdatedStruct, task1.myUpdatedStruct])
    }
}

Melkaz avatar Jan 24 '22 14:01 Melkaz

Object keys are not quoted. Change "prop1" to prop1.

jdidion avatar Jan 24 '22 15:01 jdidion

Ah, good to know !

Not fixing the issue though 😜

Updated example:

version 1.1

struct MyStruct {
    String prop1
    Boolean prop2
}

task Task1 {
    input {}

    command <<<
        echo "Hello"
    >>>

    output {
        MyStruct myUpdatedStruct = MyStruct { 
            prop1: read_string(stdout()),
            prop2: true
        }
    }
}

task Task2 {
    input {
        MyStruct myStruct
    }

    command <<<
        echo ~{myStruct.prop1} + " world !"
    >>>

    output {
        MyStruct myUpdatedStruct = MyStruct { 
            prop1: read_string(stdout()), 
            prop2: false
        }
    }
}

workflow Workflow {
    input {
        Boolean runTask2 = true
    }

    call Task1 as task1 {
    }

    if (runTask2) {
        call Task2 as task2 {
            input:
                myStruct = task1.myUpdatedStruct
        }
    }

    output {
        MyStruct updatedStruct = select_first([task2.myUpdatedStruct, task1.myUpdatedStruct])
    }
}

Melkaz avatar Jan 24 '22 15:01 Melkaz

I don't believe that Cromwell yet supports the new struct literal syntax. So do:

MyStruct myUpdatedStruct = object { 
            prop1: read_string(stdout()),
            prop2: true
        }

jdidion avatar Jan 24 '22 17:01 jdidion

@Melkaz It is entirely possible that this is a bug in miniwdl? @mlin WDYT?

patmagee avatar Jan 28 '22 17:01 patmagee

I think the issue here is probably that task2 is undefined if runTask2 is false. So task2.myUpdate is essentially None.myUpdate, which results in an error.

Instead, I think you can do select_first([task1, task2]).myUpdatedStruct.

jdidion avatar Feb 06 '24 22:02 jdidion