Skip to content

Commit fa4c7c9

Browse files
committed
basic WDL user output
1 parent a9cc0da commit fa4c7c9

File tree

4 files changed

+109
-46
lines changed

4 files changed

+109
-46
lines changed

tests/test_task_generation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def test_hello_workflow(self):
242242
WorkflowCall(
243243
TestTaskGeneration.test_hello_tasks().name,
244244
alias="hello2",
245-
inputs_map={"salutation": '"Greetings"', "name": '"Michael"'},
245+
inputs_details={"salutation": '"Greetings"', "name": '"Michael"'},
246246
)
247247
)
248248

@@ -255,8 +255,8 @@ def test_call_scatter(self):
255255
"i",
256256
"integers",
257257
[
258-
WorkflowCall(Task("task1").name, inputs_map={"num": "i"}),
259-
WorkflowCall(Task("task2").name, inputs_map={"num": "task1.output"}),
258+
WorkflowCall(Task("task1").name, inputs_details={"num": "i"}),
259+
WorkflowCall(Task("task2").name, inputs_details={"num": "task1.output"}),
260260
],
261261
)
262262

wdlgen/task.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def get_string(self):
232232
blocks.append(
233233
f"{tb}input {{\n"
234234
+ "\n".join(2 * tb + i.get_string() for i in self.inputs)
235-
+ f"\n{tb}}}"
235+
+ f"\n{tb}}}\n"
236236
)
237237

238238
if self.command:
@@ -241,12 +241,12 @@ def get_string(self):
241241
com = "\n".join(c.get_string(indent=2) for c in self.command)
242242
else:
243243
com = self.command.get_string(indent=2)
244-
blocks.append("{tb}command <<<\n{args}\n{tb}>>>".format(tb=tb, args=com))
244+
blocks.append("{tb}command <<<\n{args}\n{tb}>>>\n".format(tb=tb, args=com))
245245

246246
if self.runtime:
247247
rt = self.runtime.get_string(indent=2)
248248
blocks.append(
249-
"{tb}runtime {{\n{args}\n{tb}}}".format(
249+
"{tb}runtime {{\n{args}\n{tb}}}\n".format(
250250
tb=tb,
251251
args=rt,
252252
)
@@ -256,7 +256,7 @@ def get_string(self):
256256
mt = self.meta.get_string(indent=2)
257257
if mt:
258258
blocks.append(
259-
"{tb}meta {{\n{args}\n{tb}}}".format(
259+
"{tb}meta {{\n{args}\n{tb}}}\n".format(
260260
tb=tb, args=mt
261261
)
262262
)
@@ -265,15 +265,15 @@ def get_string(self):
265265
pmt = self.param_meta.get_string(indent=2)
266266
if pmt:
267267
blocks.append(
268-
"{tb}parameter_meta {{\n{args}\n{tb}}}".format(
268+
"{tb}parameter_meta {{\n{args}\n{tb}}}\n".format(
269269
tb=tb,
270270
args=pmt
271271
)
272272
)
273273

274274
if self.outputs:
275275
blocks.append(
276-
"{tb}output {{\n{outs}\n{tb}}}".format(
276+
"{tb}output {{\n{outs}\n{tb}}}\n".format(
277277
tb=tb,
278278
outs="\n".join((2 * tb) + o.get_string() for o in self.outputs),
279279
)

wdlgen/workflow.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(
4646
4747
workflow {name} {{
4848
{blocks}
49+
4950
}}""".strip()
5051

5152
def get_string(self):
@@ -63,10 +64,10 @@ def get_string(self):
6364
ins.extend(2 * tb + ii for ii in wd)
6465
else:
6566
ins.append(2 * tb + wd)
66-
blocks.append(f"{tb}input {{\n" + "\n".join(ins) + f"\n{tb}}}")
67+
blocks.append(f"\n{tb}input {{\n" + "\n".join(ins) + f"\n{tb}}}")
6768

6869
if self.calls:
69-
blocks.append("\n".join(c.get_string(indent=1) for c in self.calls))
70+
blocks.append("\n" + "\n\n".join(c.get_string(indent=1) for c in self.calls))
7071

7172
if self.imports:
7273
imports_block = "\n".join(i.get_string() for i in self.imports)
@@ -75,7 +76,7 @@ def get_string(self):
7576
mt = self.meta.get_string(indent=2)
7677
if mt:
7778
blocks.append(
78-
"{tb}meta {{\n{args}\n{tb}}}".format(
79+
"\n{tb}meta {{\n{args}\n{tb}}}".format(
7980
tb=tb, args=mt
8081
)
8182
)
@@ -84,7 +85,7 @@ def get_string(self):
8485
pmt = self.param_meta.get_string(indent=2)
8586
if pmt:
8687
blocks.append(
87-
"{tb}parameter_meta {{\n{args}\n{tb}}}".format(
88+
"\n{tb}parameter_meta {{\n{args}\n{tb}}}".format(
8889
tb=tb,
8990
args=pmt
9091
)
@@ -103,7 +104,7 @@ def get_string(self):
103104
else:
104105
outs.append(str(o))
105106
blocks.append(
106-
"{tb}output {{\n{outs}\n{tb}}}".format(tb=tb, outs="\n".join(outs))
107+
"\n{tb}output {{\n{outs}\n{tb}}}".format(tb=tb, outs="\n".join(outs))
107108
)
108109

109110
return self.format.format(

wdlgen/workflowcall.py

Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,119 @@
11
from abc import ABC, abstractmethod
2-
from typing import Dict, List
2+
from dataclasses import dataclass
3+
from functools import cached_property
4+
from typing import Any, List, Optional
35

4-
from .task import Task
56
from .util import WdlBase
67

78

9+
10+
@dataclass
11+
class StepValueLine:
12+
tag: str
13+
value: Optional[str]
14+
special_label: Optional[str]
15+
prefix_label: Optional[str]
16+
default_label: Optional[str]
17+
#default_label: bool
18+
datatype_label: str
19+
20+
@cached_property
21+
def length(self) -> int:
22+
return len(f'{self.tag_and_value},')
23+
24+
@property
25+
def tag_and_value(self) -> str:
26+
return f'{self.tag}={self.value},'
27+
28+
@property
29+
def info_comment(self) -> str:
30+
prefix = f' {self.prefix_label}' if self.prefix_label is not None else ''
31+
datatype = f' [{self.datatype_label}]' if self.datatype_label is not None else ''
32+
default = f' ({self.default_label})' if self.default_label is not None else ''
33+
special = f' **{self.special_label}' if self.special_label is not None else ''
34+
#default = f' (default)' if self.default_label is True else ''
35+
return f'#{special}{prefix}{datatype}{default}'
36+
37+
38+
839
class WorkflowCallBase(WdlBase, ABC):
940
@abstractmethod
10-
def get_string(self, indent=1):
41+
def get_string(self, indent: int=1):
1142
raise Exception("Must override 'get_string(indent:int)'")
1243

1344

1445
class WorkflowCall(WorkflowCallBase):
46+
1547
def __init__(
1648
self,
17-
namespaced_identifier: str = None,
18-
alias: str = None,
19-
inputs_map: Dict[str, str] = None,
49+
namespaced_identifier: str,
50+
alias: str,
51+
inputs_details: dict[str, dict[str, Any]],
52+
messages: list[str],
53+
render_comments: bool = True
2054
):
2155
"""
22-
2356
:param task:
2457
:param namespaced_identifier: Required if task is imported. The workflow might take care of this later?
2558
:param alias:
26-
:param inputs_map:
59+
:param inputs_details:
2760
"""
2861
self.namespaced_identifier = namespaced_identifier
2962
self.alias = alias
30-
self.inputs_map = inputs_map
31-
32-
self.call_format = """{ind}call {name}{alias} {body}"""
33-
self.body_format = """{{\n{ind}{tb}input:\n{input_map}\n{ind}}}"""
34-
35-
def get_string(self, indent=1):
36-
tb = " "
37-
38-
body = ""
39-
if self.inputs_map:
40-
inpmap = ",\n".join(
41-
((indent + 2) * tb + str(k) + "=" + str(v))
42-
for k, v in self.inputs_map.items()
63+
self.inputs_details = inputs_details
64+
self.messages = messages
65+
self.render_comments = render_comments
66+
67+
def get_string(self, indent: int=1):
68+
self.tb: str = " "
69+
self.indent: int = 1
70+
ind = self.indent * self.tb
71+
name = self.namespaced_identifier
72+
alias = " as " + self.alias if self.alias else "" # alias is always being supplied, but this implies its not?
73+
body = self.get_body()
74+
return f"{ind}call {name}{alias} {body}\n{ind}}}"
75+
76+
def get_body(self) -> str:
77+
lines = self.init_known_input_lines()
78+
return self.input_section_to_string(lines)
79+
80+
def init_known_input_lines(self) -> list[StepValueLine]:
81+
out: list[StepValueLine] = []
82+
for tag, d in self.inputs_details.items():
83+
line = StepValueLine(
84+
tag=tag,
85+
value=d['value'],
86+
special_label=d['special_label'],
87+
prefix_label=d['prefix'],
88+
default_label=d['default'],
89+
datatype_label=d['datatype']
4390
)
44-
body = self.body_format.format(ind=indent * tb, input_map=inpmap, tb=tb)
45-
46-
return self.call_format.format(
47-
ind=indent * tb,
48-
tb=tb,
49-
name=self.namespaced_identifier
50-
if self.namespaced_identifier
51-
else self.task.name,
52-
alias=(" as " + self.alias) if self.alias else "",
53-
body=body,
54-
)
91+
out.append(line)
92+
return out
93+
94+
def input_section_to_string(self, lines: list[StepValueLine]) -> str:
95+
str_lines: list[str] = []
96+
ind = (self.indent + 1) * self.tb
97+
98+
# render 'unknown' input messages
99+
if self.render_comments:
100+
str_lines += [f'{ind}{self.tb}#{m},' for m in self.messages]
101+
102+
# calc longest line so we know how to justify info comments
103+
max_line_len = max([ln.length for ln in lines])
104+
105+
# generate string representaiton of each line
106+
for ln in lines:
107+
if self.render_comments:
108+
str_line = f'{ind}{self.tb}{ln.tag_and_value:<{max_line_len+2}}{ln.info_comment}'
109+
else:
110+
str_line = f'{ind}{self.tb}{ln.tag_and_value}'
111+
str_lines.append(str_line)
112+
113+
# join lines and return body segment
114+
inputs = '\n'.join(str_lines)
115+
return f"{{\n{ind}input:\n{inputs}"
116+
55117

56118

57119
class WorkflowConditional(WorkflowCallBase):

0 commit comments

Comments
 (0)