diff --git a/WDL/CLI.py b/WDL/CLI.py index cafa0615..28e79a4e 100644 --- a/WDL/CLI.py +++ b/WDL/CLI.py @@ -893,9 +893,11 @@ def runner_input( decl = available_inputs.get(name) if not decl: - # allow arbitrary runtime overrides + # allow arbitrary runtime/hints overrides nmparts = name.split(".") - runtime_idx = next((i for i, term in enumerate(nmparts) if term in ("runtime",)), -1) + runtime_idx = next( + (i for i, term in enumerate(nmparts) if term in ("runtime", "hints")), -1 + ) if runtime_idx >= 0 and len(nmparts) > (runtime_idx + 1): decl = available_inputs.get(".".join(nmparts[:runtime_idx] + ["_runtime"])) @@ -1073,7 +1075,7 @@ def runner_input_value(s_value, ty, downloadable, root): ty.item_type, [runner_input_value(s_value, ty.item_type, downloadable, root)] ) if isinstance(ty, Type.Any): - # infer dynamically-typed runtime overrides + # infer dynamically-typed runtime/hints overrides try: return Value.Int(int(s_value)) except ValueError: diff --git a/WDL/Tree.py b/WDL/Tree.py index fd2c89f8..67f0af34 100644 --- a/WDL/Tree.py +++ b/WDL/Tree.py @@ -263,6 +263,10 @@ class Task(SourceNode): """:type: Dict[str,WDL.Expr.Base] ``runtime{}`` section, with keys and corresponding expressions to be evaluated""" + hints: Dict[str, Any] + """:type: Dict[str, Any] + + ``hints{}`` section as a JSON-like dict""" meta: Dict[str, Any] """:type: Dict[str,Any] @@ -285,6 +289,7 @@ def __init__( parameter_meta: Dict[str, Any], runtime: Dict[str, Expr.Base], meta: Dict[str, Any], + hints: Dict[str, Any], ) -> None: super().__init__(pos) self.name = name @@ -295,6 +300,7 @@ def __init__( self.parameter_meta = parameter_meta self.runtime = runtime self.meta = meta + self.hints = hints self.effective_wdl_version = "1.0" # overridden by Document.__init__ # TODO: enforce validity constraints on parameter_meta and runtime # TODO: if the input section exists, then all postinputs decls must be @@ -314,7 +320,7 @@ def available_inputs(self) -> Env.Bindings[Decl]: ans = Env.Bindings() if self.effective_wdl_version not in ("draft-2", "1.0"): - # synthetic placeholder to expose runtime overrides + # synthetic placeholder to expose runtime & hints overrides ans = ans.bind("_runtime", Decl(self.pos, Type.Any(), "_runtime")) for decl in reversed(self.inputs if self.inputs is not None else self.postinputs): diff --git a/WDL/__init__.py b/WDL/__init__.py index 8cabe3e5..5d2286f4 100644 --- a/WDL/__init__.py +++ b/WDL/__init__.py @@ -249,14 +249,14 @@ def values_from_json( key2parts = key2.split(".") runtime_idx = next( - (i for i, term in enumerate(key2parts) if term in ("runtime",)), -1 + (i for i, term in enumerate(key2parts) if term in ("runtime", "hints")), -1 ) if ( runtime_idx >= 0 and len(key2parts) > (runtime_idx + 1) and ".".join(key2parts[:runtime_idx] + ["_runtime"]) in available ): - # allow arbitrary keys for runtime + # allow arbitrary keys for runtime/hints ty = Type.Any() elif len(key2parts) == 3 and key2parts[0] and key2parts[1] and key2parts[2]: # attempt to simplify .. from old Cromwell JSON diff --git a/WDL/_grammar.py b/WDL/_grammar.py index 5e0a8cdb..acd67e42 100644 --- a/WDL/_grammar.py +++ b/WDL/_grammar.py @@ -313,6 +313,7 @@ | output_decls | meta_section | runtime_section + | hints_section | any_decl -> noninput_decl tasks: task* @@ -336,6 +337,7 @@ // task runtime section (key-expression pairs) runtime_section: "runtime" "{" [runtime_kv (","? runtime_kv)*] "}" runtime_kv: CNAME ":" expr +hints_section: "hints" meta_object /////////////////////////////////////////////////////////////////////////////////////////////////// // decl @@ -479,7 +481,7 @@ %ignore COMMENT """ keywords["development"] = set( - "Array Directory File Float Int Map None Pair String alias as call command else false if import input left meta object output parameter_meta right runtime scatter struct task then true workflow".split( + "Array Directory File Float Int Map None Pair String alias as call command else false hints if import input left meta object output parameter_meta right runtime scatter struct task then true workflow".split( " " ) ) diff --git a/WDL/_parser.py b/WDL/_parser.py index 46cf080e..3c58df5f 100644 --- a/WDL/_parser.py +++ b/WDL/_parser.py @@ -388,6 +388,9 @@ def runtime_section(self, items, meta): d[k] = v return {"runtime": d} + def hints_section(self, items, meta): + return {"hints": items[0]} + def task(self, items, meta): d = {"noninput_decls": []} for item in items: @@ -416,6 +419,7 @@ def task(self, items, meta): d.get("parameter_meta", {}), d.get("runtime", {}), d.get("meta", {}), + d.get("hints", {}), ) def tasks(self, items, meta): diff --git a/tests/test_7runner.py b/tests/test_7runner.py index 6def83a7..4cabb449 100644 --- a/tests/test_7runner.py +++ b/tests/test_7runner.py @@ -798,6 +798,11 @@ def test_weird_filenames(self): runtime { container: ["ubuntu:20.04"] } + hints { + foo: { + bar: ["bas", 42] + } + } } """