|
35 | 35 | aslist,
|
36 | 36 | )
|
37 | 37 | from .workflow_job import WorkflowJob
|
| 38 | +from cwl_utils import expression |
38 | 39 |
|
39 | 40 |
|
40 | 41 | def default_make_tool(
|
@@ -156,6 +157,73 @@ def job(
|
156 | 157 | output_callbacks: Optional[OutputCallbackType],
|
157 | 158 | runtimeContext: RuntimeContext,
|
158 | 159 | ) -> JobsGeneratorType:
|
| 160 | + # TODO: This is not very efficient and could be improved. |
| 161 | + # |
| 162 | + # See issue #1330. We needed to evaluate the requirements |
| 163 | + # at the workflow level, so that it was not re-evaluated at |
| 164 | + # each step level (since a command-line tool, for instance, |
| 165 | + # won't have the inputs from args, but instead will have the |
| 166 | + # empty/default inputs of a workflow step). |
| 167 | + # |
| 168 | + # The solution below evaluates the requirements and hints for |
| 169 | + # the workflow (parent), keeping track of the name of the |
| 170 | + # requirements and hints. For each step of the workflow and of |
| 171 | + # the embedded tool (command-line or expression tools) it will |
| 172 | + # then evaluate the requirements or hints that have the same |
| 173 | + # name - even though they may be re-evaluated at the step |
| 174 | + # level (e.g. a workflow defines a requirement resource that |
| 175 | + # uses inputs.threads_max, and a command-line tool of the same |
| 176 | + # workflow also defines a requirement with the same name, but |
| 177 | + # using the command-line tool input values). |
| 178 | + # |
| 179 | + # This prevents evaluation at the step level (i.e. the values |
| 180 | + # were already loaded earlier). |
| 181 | + def _fix_hints_and_requirements( |
| 182 | + hints_or_requirements: List[CWLObjectType], |
| 183 | + requirements_or_hints_to_evaluate: List[str], |
| 184 | + ) -> None: |
| 185 | + """Fix hints and requirements of a workflow. |
| 186 | +
|
| 187 | + Internal function to iterate the hints or requirements |
| 188 | + of steps provided and evaluate the ones that exist in |
| 189 | + the parent process. |
| 190 | + """ |
| 191 | + for hint_or_requirement in hints_or_requirements: |
| 192 | + for key, value in hint_or_requirement.items(): |
| 193 | + if key in requirements_or_hints_to_evaluate: |
| 194 | + hint_or_requirement[key] = expression.do_eval( |
| 195 | + ex=value, |
| 196 | + jobinput=job_order, |
| 197 | + requirements=self.requirements, |
| 198 | + outdir=runtimeContext.outdir, |
| 199 | + tmpdir=runtimeContext.tmpdir, |
| 200 | + resources={}, |
| 201 | + context=None, |
| 202 | + timeout=runtimeContext.eval_timeout, |
| 203 | + ) |
| 204 | + |
| 205 | + for attr_key in ["hints", "requirements"]: |
| 206 | + parent_entries = [] |
| 207 | + for hint_or_requirement in getattr(self, attr_key): |
| 208 | + for key, value in hint_or_requirement.items(): |
| 209 | + hint_or_requirement[key] = expression.do_eval( |
| 210 | + ex=value, |
| 211 | + jobinput=job_order, |
| 212 | + requirements=self.requirements, |
| 213 | + outdir=runtimeContext.outdir, |
| 214 | + tmpdir=runtimeContext.tmpdir, |
| 215 | + resources={}, |
| 216 | + context=None, |
| 217 | + timeout=runtimeContext.eval_timeout, |
| 218 | + ) |
| 219 | + parent_entries.append(key) |
| 220 | + |
| 221 | + for step in self.steps: |
| 222 | + _fix_hints_and_requirements(getattr(step, attr_key), parent_entries) |
| 223 | + _fix_hints_and_requirements( |
| 224 | + getattr(step.embedded_tool, attr_key), parent_entries |
| 225 | + ) |
| 226 | + |
159 | 227 | builder = self._init_job(job_order, runtimeContext)
|
160 | 228 |
|
161 | 229 | if runtimeContext.research_obj is not None:
|
|
0 commit comments