Repository: common-workflow-language/cwltool

Issue:
Resource requirements fail if user does division (float vs int)

## Expected Behavior

Resource allocation should accept all numeric values, including floats.

## Actual Behavior

User code (from requirements stanza):
```
tmpdirMin: $((2 * inputs.input_bam.size) / 1048576)
```
produces a floating number.  Which causes the error:
```
ERROR [step sort_markdup_bam] Cannot make job: Got incorrect return type <class 'float'> from resource expression evaluation of $((2 * inputs.input_bam.size) / 1048576).
```
This is probably returned from the function at https://github.com/common-workflow-language/cwltool/blob/main/cwltool/process.py#L524

## Workflow Code

https://github.com/NCI-GDC/gdc-dnaseq-cwl/blob/master/tools/samtools_sort.cwl

## Full Traceback
```pytb
Traceback (most recent call last):
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/workflow_job.py", line 821, in job
    for newjob in step.iterable:
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/workflow_job.py", line 751, in try_make_job
    yield from jobs
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/workflow_job.py", line 77, in job
    yield from self.step.job(joborder, output_callback, runtimeContext)
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/workflow.py", line 458, in job
    yield from self.embedded_tool.job(
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/command_line_tool.py", line 959, in job
    builder = self._init_job(job_order, runtimeContext)
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/process.py", line 940, in _init_job
    builder.resources = self.evalResources(builder, runtime_context)
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/process.py", line 984, in evalResources
    eval_resource(builder, cast(Union[str, int, float], rsc[a + "Min"])),
  File "/home/users/ellrott/miniconda3/envs/cwl/lib/python3.9/site-packages/cwltool/process.py", line 523, in eval_resource
    raise WorkflowException(
cwltool.errors.WorkflowException: Got incorrect return type <class 'float'> from resource expression evaluation of $((2 * inputs.input_bam.size) / 1048576).
INFO [workflow conditional_markduplicates] completed permanentFail
```

## Your Environment
* cwltool version: 
Check using ``cwltool --version``

3.1.20230302145532


Discussion:
Hello @kellrott ; `tmpdirMin` can be a float only starting with CWL v1.2

See https://www.commonwl.org/v1.2/CommandLineTool.html#ResourceRequirement vs https://www.commonwl.org/v1.1/CommandLineTool.html#ResourceRequirement

That being said, that error will still trigger in that circumstance. 

Resolving patch:
diff --git a/cwltool/process.py b/cwltool/process.py
--- a/cwltool/process.py
+++ b/cwltool/process.py
@@ -58,7 +58,7 @@
 from .pathmapper import MapperEnt, PathMapper
 from .secrets import SecretStore
 from .stdfsaccess import StdFsAccess
-from .update import INTERNAL_VERSION, ORIGINAL_CWLVERSION
+from .update import INTERNAL_VERSION, ORDERED_VERSIONS, ORIGINAL_CWLVERSION
 from .utils import (
     CWLObjectType,
     CWLOutputAtomType,
@@ -518,12 +518,17 @@ def eval_resource(
 ) -> Optional[Union[str, int, float]]:
     if isinstance(resource_req, str) and expression.needs_parsing(resource_req):
         result = builder.do_eval(resource_req)
+        if isinstance(result, float):
+            if ORDERED_VERSIONS.index(builder.cwlVersion) >= ORDERED_VERSIONS.index("v1.2.0-dev4"):
+                return result
+            raise WorkflowException(
+                "Floats are not valid in resource requirement expressions prior "
+                f"to CWL v1.2: {resource_req} returned {result}."
+            )
         if isinstance(result, (str, int)) or result is None:
             return result
         raise WorkflowException(
-            "Got incorrect return type {} from resource expression evaluation of {}.".format(
-                type(result), resource_req
-            )
+            f"Got incorrect return type {type(result)} from resource expression evaluation of {resource_req}."
         )
     return resource_req
 
@@ -979,15 +984,17 @@ def evalResources(
                 continue
             mn = mx = None  # type: Optional[Union[int, float]]
             if rsc.get(a + "Min"):
-                mn = cast(
-                    Union[int, float],
-                    eval_resource(builder, cast(Union[str, int, float], rsc[a + "Min"])),
-                )
+                with SourceLine(rsc, f"{a}Min", WorkflowException, runtimeContext.debug):
+                    mn = cast(
+                        Union[int, float],
+                        eval_resource(builder, cast(Union[str, int, float], rsc[a + "Min"])),
+                    )
             if rsc.get(a + "Max"):
-                mx = cast(
-                    Union[int, float],
-                    eval_resource(builder, cast(Union[str, int, float], rsc[a + "Max"])),
-                )
+                with SourceLine(rsc, f"{a}Max", WorkflowException, runtimeContext.debug):
+                    mx = cast(
+                        Union[int, float],
+                        eval_resource(builder, cast(Union[str, int, float], rsc[a + "Max"])),
+                    )
             if mn is None:
                 mn = mx
             elif mx is None:


Test patch:
diff --git a/tests/test_examples.py b/tests/test_examples.py
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -1799,3 +1799,33 @@ def test_validate_optional_src_with_mandatory_sink() -> None:
     stderr = re.sub(r"\s\s+", " ", stderr)
     assert 'Source \'opt_file\' of type ["null", "File"] may be incompatible' in stderr
     assert "with sink 'r' of type \"File\"" in stderr
+
+
+def test_res_req_expr_float_1_0() -> None:
+    """Confirm expected error when returning a float value from a ResourceRequirement expr in CWL v1.0."""
+    exit_code, stdout, stderr = get_main_output(
+        [
+            get_data("tests/wf/resreq_expr_float_v1_0.cwl"),
+            "--input_bam",
+            get_data("tests/wf/whale.txt"),
+        ]
+    )
+    assert exit_code == 1
+    stderr = re.sub(r"\s\s+", " ", stderr)
+    assert "Floats are not valid in resource requirement expressions" in stderr
+    assert "prior to CWL v1.2" in stderr
+    assert "$((2 * inputs.input_bam.size) / 3.14159) returned" in stderr
+
+
+def test_res_req_expr_float_1_2() -> None:
+    """Confirm no error when returning a float value from a ResourceRequirement expr in CWL v1.0."""
+    exit_code, stdout, stderr = get_main_output(
+        [
+            get_data("tests/wf/resreq_expr_float_v1_2.cwl"),
+            "--input_bam",
+            get_data("tests/wf/whale.txt"),
+        ]
+    )
+    assert exit_code == 0, stderr
+    assert json.loads(stdout)["result"]["outdirSize"] >= 708
+    assert json.loads(stdout)["result"]["tmpdirSize"] >= 708
diff --git a/tests/wf/resreq_expr_float_v1_0.cwl b/tests/wf/resreq_expr_float_v1_0.cwl
new file mode 100644
--- /dev/null
+++ b/tests/wf/resreq_expr_float_v1_0.cwl
@@ -0,0 +1,21 @@
+cwlVersion: v1.0
+class: CommandLineTool
+requirements:
+  - class: InlineJavascriptRequirement
+  - class: ResourceRequirement
+    tmpdirMin: $((2 * inputs.input_bam.size) / 3.14159)
+    outdirMin: $((2 * inputs.input_bam.size) / 3.14159)
+
+inputs:
+  input_bam: File
+
+arguments:
+ - |
+   {"result": $(runtime) }
+
+stdout: cwl.output.json
+
+outputs:
+  result: Any
+
+baseCommand: [echo]
diff --git a/tests/wf/resreq_expr_float_v1_2.cwl b/tests/wf/resreq_expr_float_v1_2.cwl
new file mode 100644
--- /dev/null
+++ b/tests/wf/resreq_expr_float_v1_2.cwl
@@ -0,0 +1,21 @@
+cwlVersion: v1.2
+class: CommandLineTool
+requirements:
+  - class: InlineJavascriptRequirement
+  - class: ResourceRequirement
+    tmpdirMin: $((2 * inputs.input_bam.size) / 3.14159)
+    outdirMin: $((2 * inputs.input_bam.size) / 3.14159)
+
+inputs:
+  input_bam: File
+
+arguments:
+ - |
+   {"result": $(runtime) }
+
+stdout: cwl.output.json
+
+outputs:
+  result: Any
+
+baseCommand: [echo]