common-workflow-language / cwltool

Common Workflow Language reference implementation
https://cwltool.readthedocs.io/
Apache License 2.0
332 stars 230 forks source link

workflow level ResourceRequirements unable to access workflow level inputs #1330

Open mr-c opened 4 years ago

mr-c commented 4 years ago
#!/usr/bin/env cwl-runner
cwlVersion: v1.0
class: Workflow

requirements:
  InlineJavascriptRequirement: {}  # shouldn't need this, but makes the error more interesting

hints:
  ResourceRequirement:
    coresMax: $(inputs.threads_max)

inputs:
  threads_max:
    type: int
    default: 4

steps:
  one:
    in: []
    run:
      class: CommandLineTool
      inputs: []
      baseCommand: echo
      arguments: [ $(runtime.cores) ]
      outputs: []
    out: []

outputs: []
$ cwltool workflow-dyn-res.cwl
INFO /home/michael/cwltool/env3.8/bin/cwltool 3.0.20200724083110
INFO Resolved 'workflow-dyn-res.cwl' to 'file:///home/michael/cwltool/workflow-dyn-res.cwl'
INFO [workflow ] start
INFO [workflow ] starting step one
INFO [step one] start
ERROR Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {};
03 var self = null;
04 var runtime = {
05     "tmpdir": "/tmp/tmpcb9aom_8",
06     "outdir": "/tmp/nx2jmuji"
07 };
08 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

Traceback (most recent call last):
  File "/home/michael/cwltool/cwltool/sandboxjs.py", line 411, in execjs
    return cast(CWLOutputType, json.loads(stdout))
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "cwltool/expression.py", line 377, in do_eval
    return interpolate(
  File "cwltool/expression.py", line 293, in interpolate
    e = evaluator(
  File "cwltool/expression.py", line 250, in evaluator
    return execjs(
  File "/home/michael/cwltool/cwltool/sandboxjs.py", line 413, in execjs
    raise JavascriptException(
cwltool.sandboxjs.JavascriptException: Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {};
03 var self = null;
04 var runtime = {
05     "tmpdir": "/tmp/tmpcb9aom_8",
06     "outdir": "/tmp/nx2jmuji"
07 };
08 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

ERROR Exception on step 'one'
ERROR [step one] Cannot make job: Expression evaluation error:
Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {};
03 var self = null;
04 var runtime = {
05     "tmpdir": "/tmp/tmpcb9aom_8",
06     "outdir": "/tmp/nx2jmuji"
07 };
08 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

INFO [workflow ] completed permanentFail
{}
mr-c commented 4 years ago

Full traceback

Traceback (most recent call last):
  File "/home/michael/cwltool/cwltool/workflow_job.py", line 852, in job
    for newjob in step.iterable:
  File "/home/michael/cwltool/cwltool/workflow_job.py", line 771, in try_make_job
    for j in jobs:
  File "/home/michael/cwltool/cwltool/workflow_job.py", line 78, in job
    for j in self.step.job(joborder, output_callback, runtimeContext):
  File "cwltool/workflow.py", line 439, in job
    for tool in self.embedded_tool.job(
  File "cwltool/command_line_tool.py", line 788, in job
  File "cwltool/process.py", line 952, in _init_job
    if self.tool["class"] != "Workflow":
  File "cwltool/process.py", line 990, in evalResources
    Union[int, float],
  File "cwltool/process.py", line 549, in eval_resource
    if isinstance(resource_req, str) and expression.needs_parsing(resource_req):
  File "cwltool/builder.py", line 639, in do_eval
    return expression.do_eval(
  File "cwltool/expression.py", line 402, in do_eval
    raise WorkflowException("Expression evaluation error:\n%s" % str(e)) from e
mr-c commented 4 years ago

When I modify the example to have an input for the CommandLineTool it shows that the wrong inputs object is being used to evaluated the Workflow level dynamic ResourceRequirement

#!/usr/bin/env cwl-runner
cwlVersion: v1.0
class: Workflow

requirements:
  InlineJavascriptRequirement: {}

hints:
  ResourceRequirement:
    coresMax: $(inputs.threads_max)

inputs:
  threads_max:
    type: int
    default: 4

steps:
  one:
    in: []
    run:
      class: CommandLineTool
      inputs:
        other_input:
          type: int
          default: 8
      baseCommand: echo
      arguments: [ $(runtime.cores) ]
      outputs: []
    out: []

outputs: []
$ cwltool workflow-dyn-res.cwl 
INFO /home/michael/cwltool/env3.8/bin/cwltool 3.0.20200724083110
INFO Resolved 'workflow-dyn-res.cwl' to 'file:///home/michael/cwltool/workflow-dyn-res.cwl'
INFO [workflow ] start
INFO [workflow ] starting step one
INFO [step one] start
ERROR Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {
03     "other_input": 8
04 };
05 var self = null;
06 var runtime = {
07     "tmpdir": "/tmp/tmpneb1x4qe",
08     "outdir": "/tmp/vts9yg_3"
09 };
10 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

Traceback (most recent call last):
  File "/home/michael/cwltool/cwltool/sandboxjs.py", line 411, in execjs
    return cast(CWLOutputType, json.loads(stdout))
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "cwltool/expression.py", line 377, in do_eval
    return interpolate(
  File "cwltool/expression.py", line 293, in interpolate
    e = evaluator(
  File "cwltool/expression.py", line 250, in evaluator
    return execjs(
  File "/home/michael/cwltool/cwltool/sandboxjs.py", line 413, in execjs
    raise JavascriptException(
cwltool.sandboxjs.JavascriptException: Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {
03     "other_input": 8
04 };
05 var self = null;
06 var runtime = {
07     "tmpdir": "/tmp/tmpneb1x4qe",
08     "outdir": "/tmp/vts9yg_3"
09 };
10 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

ERROR Exception on step 'one'
ERROR [step one] Cannot make job: Expression evaluation error:
Expecting value: line 1 column 1 (char 0)
script was:
01 "use strict";
02 var inputs = {
03     "other_input": 8
04 };
05 var self = null;
06 var runtime = {
07     "tmpdir": "/tmp/tmpneb1x4qe",
08     "outdir": "/tmp/vts9yg_3"
09 };
10 (function(){return ((inputs.threads_max));})()
stdout was: 'undefined'
stderr was: ''

INFO [workflow ] completed permanentFail
{}
WARNING Final process status is permanentFail
mr-c commented 4 years ago

FYI, the expected is

$ cwltool workflow-dyn-res.cwl 
INFO /home/michael/cwltool/env3.8/bin/cwltool 3.0.20200724083110
INFO Resolved 'workflow-dyn-res.cwl' to 'file:///home/michael/cwltool/workflow-dyn-res.cwl'
INFO [workflow ] start
INFO [workflow ] starting step one
INFO [step one] start
INFO [job one] /tmp/ywftv8yg$ echo \
    4
4
INFO [job one] completed success
INFO [step one] completed success
INFO [workflow ] completed success
{}
INFO Final process status is success
kinow commented 2 years ago

Here's a workaround that works for the example workflow above, with the downside that it is annoying to have to duplicate inputs.

#!/usr/bin/env cwl-runner
cwlVersion: v1.0
class: Workflow

hints:
  ResourceRequirement:
    coresMax: $(inputs.threads_max)

inputs:
  threads_max:
    type: int
    default: 4

steps:
  one:
    in:
      threads_max: threads_max
    run:
      class: CommandLineTool
      inputs:
        threads_max:
          type: int
      baseCommand: echo
      arguments: [ $(runtime.cores) ]
      outputs: []
    out: []

outputs: []
mr-c commented 2 years ago

@kinow Yep, but this is a major bug that the wrong inputs object is being used to evaluate expressions in workflow level hints/reqs