diff --git a/src/toil/wdl/wdltoil.py b/src/toil/wdl/wdltoil.py index 41a5af8d33..9b54f40407 100755 --- a/src/toil/wdl/wdltoil.py +++ b/src/toil/wdl/wdltoil.py @@ -333,6 +333,25 @@ def unpack_toil_uri(toil_uri: str) -> Tuple[FileID, str]: return file_id, file_basename +def evaluate_output_decls(output_decls: List[WDL.Tree.Decl], all_bindings: WDL.Env.Bindings[WDL.Value.Base], standard_library: WDL.StdLib.Base) -> WDL.Env.Bindings[WDL.Value.Base]: + """ + Evaluate output decls with a given bindings environment and standard library. + Creates a new bindings object that only contains the bindings from the given decls. + Guarantees that each decl in `output_decls` can access the variables defined by the previous ones. + :param all_bindings: Environment to use when evaluating decls + :param output_decls: Decls to evaluate + :param standard_library: Standard library + :return: New bindings object with only the output_decls + """ + # all_bindings contains output + previous bindings so that the output can reference its own declarations + # output_bindings only contains the output bindings themselves so that bindings from sections such as the input aren't included + output_bindings: WDL.Env.Bindings[WDL.Value.Base] = WDL.Env.Bindings() + for output_decl in output_decls: + output_value = evaluate_decl(output_decl, all_bindings, standard_library) + all_bindings = all_bindings.bind(output_decl.name, output_value) + output_bindings = output_bindings.bind(output_decl.name, output_value) + return output_bindings + class NonDownloadingSize(WDL.StdLib._Size): """ WDL size() implementation that avoids downloading files. @@ -1437,9 +1456,7 @@ def patched_run_invocation(*args: Any, **kwargs: Any) -> List[str]: # objects, and like MiniWDL we can say we only support # working-directory-based relative paths for globs. outputs_library = ToilWDLStdLibTaskOutputs(file_store, host_stdout_txt, host_stderr_txt, current_directory_override=workdir_in_container) - output_bindings: WDLBindings = WDL.Env.Bindings() - for output_decl in self._task.outputs: - output_bindings = output_bindings.bind(output_decl.name, evaluate_decl(output_decl, bindings, outputs_library)) + output_bindings = evaluate_output_decls(self._task.outputs, bindings, outputs_library) # Drop any files from the output which don't actually exist output_bindings = drop_missing_files(output_bindings, current_directory_override=workdir_in_container) @@ -2265,9 +2282,9 @@ def run(self, file_store: AbstractFileStore) -> WDLBindings: # Evaluate all the outputs in the normal, non-task-outputs library context standard_library = ToilWDLStdLibBase(file_store) - output_bindings: WDL.Env.Bindings[WDL.Value.Base] = WDL.Env.Bindings() - for output_decl in self._outputs: - output_bindings = output_bindings.bind(output_decl.name, evaluate_decl(output_decl, unwrap(self._bindings), standard_library)) + # Combine the bindings from the previous job + + output_bindings = evaluate_output_decls(self._outputs, unwrap(self._bindings), standard_library) return self.postprocess(output_bindings)