|
1 | 1 | from abc import ABC, abstractmethod |
2 | | -from dataclasses import dataclass |
3 | | -from functools import cached_property |
4 | 2 | from typing import Any, List, Optional |
5 | 3 |
|
6 | 4 | from .util import WdlBase |
7 | 5 |
|
8 | 6 |
|
9 | 7 |
|
10 | | -@dataclass |
11 | 8 | 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 | | - |
| 9 | + def __init__( |
| 10 | + self, |
| 11 | + tag: str, |
| 12 | + value: str, |
| 13 | + special: Optional[str], |
| 14 | + prefix: Optional[str], |
| 15 | + default: Optional[str], |
| 16 | + datatype: Optional[str], |
| 17 | + ordering: int |
| 18 | + ): |
| 19 | + self.tag: str = tag |
| 20 | + self.value: str = value |
| 21 | + self.special: str = special if special else '' |
| 22 | + self.prefix: str = prefix if prefix else '' |
| 23 | + self.default: str = default if default else '' |
| 24 | + self.datatype: str = datatype if datatype else '' |
| 25 | + self.ordering: int = ordering |
| 26 | + |
24 | 27 | @property |
25 | 28 | def tag_and_value(self) -> str: |
26 | 29 | return f'{self.tag}={self.value}' |
| 30 | + |
27 | 31 |
|
| 32 | +class StepValueSection: |
| 33 | + def __init__(self, lines: list[StepValueLine]): |
| 34 | + self.lines = self.order_lines(lines) |
| 35 | + self.padding = 2 |
| 36 | + |
| 37 | + def order_lines(self, lines: list[StepValueLine]) -> list[StepValueLine]: |
| 38 | + lines.sort(key=lambda x: x.ordering) # normal ordering |
| 39 | + lines.sort(key=lambda x: x.special != '', reverse=True) # 'special' priority |
| 40 | + return lines |
| 41 | + |
28 | 42 | @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}' |
| 43 | + def tag_value_width(self) -> int: |
| 44 | + width = max([len(f'{x.tag_and_value},') for x in self.lines]) |
| 45 | + return self.add_padding(width) |
36 | 46 |
|
| 47 | + @property |
| 48 | + def datatype_width(self) -> int: |
| 49 | + width = max([len(x.datatype) for x in self.lines]) |
| 50 | + return self.add_padding(width) |
| 51 | + |
| 52 | + @property |
| 53 | + def prefix_width(self) -> int: |
| 54 | + width = max([len(x.prefix) for x in self.lines]) |
| 55 | + return self.add_padding(width) |
| 56 | + |
| 57 | + def add_padding(self, width: int) -> int: |
| 58 | + if width > 0: |
| 59 | + width += self.padding |
| 60 | + return width |
| 61 | + |
| 62 | + def render(self, indent: int, tb: str, render_comments: bool=True) -> str: |
| 63 | + str_lines: list[str] = [] |
| 64 | + ind = (indent + 1) * tb |
| 65 | + |
| 66 | + # generate string representation of each line |
| 67 | + for i, ln in enumerate(self.lines): |
| 68 | + comma = ',' if i < len(self.lines) - 1 else '' # ignore comma for last line |
| 69 | + tag_value = f'{ln.tag_and_value + comma:<{self.tag_value_width}}' |
| 70 | + datatype = f'{ln.datatype:<{self.datatype_width}}' |
| 71 | + prefix = f'{ln.prefix:<{self.prefix_width}}' |
| 72 | + default = ln.default |
| 73 | + special = ln.special |
| 74 | + if render_comments: |
| 75 | + str_line = f'{ind}{tb}{tag_value}# {datatype}{prefix}{default} {special}' |
| 76 | + else: |
| 77 | + str_line = f'{ind}{tb}{tag_value}' |
| 78 | + str_lines.append(str_line) |
| 79 | + |
| 80 | + # join lines and return body segment |
| 81 | + inputs = '\n'.join(str_lines) |
| 82 | + return f"{{\n{ind}input:\n{inputs}" |
37 | 83 |
|
38 | 84 |
|
39 | 85 | class WorkflowCallBase(WdlBase, ABC): |
@@ -71,49 +117,28 @@ def get_string(self, indent: int=1): |
71 | 117 | name = self.namespaced_identifier |
72 | 118 | alias = " as " + self.alias if self.alias else "" # alias is always being supplied, but this implies its not? |
73 | 119 | body = self.get_body() |
74 | | - return f"{ind}call {name}{alias} {body}\n{ind}}}" |
| 120 | + msgs = '\n'.join([f'{ind}#{msg}' for msg in self.messages]) + '\n' if self.render_comments else '' |
| 121 | + return f"{msgs}{ind}call {name}{alias} {body}\n{ind}}}" |
75 | 122 |
|
76 | 123 | def get_body(self) -> str: |
77 | | - lines = self.init_known_input_lines() |
78 | | - return self.input_section_to_string(lines) |
| 124 | + value_lines = self.init_known_input_lines() |
| 125 | + value_section = StepValueSection(value_lines) |
| 126 | + return value_section.render(indent=self.indent, tb=self.tb, render_comments=self.render_comments) |
79 | 127 |
|
80 | 128 | def init_known_input_lines(self) -> list[StepValueLine]: |
81 | 129 | out: list[StepValueLine] = [] |
82 | 130 | for tag, d in self.inputs_details.items(): |
83 | 131 | line = StepValueLine( |
84 | 132 | tag=tag, |
85 | 133 | value=d['value'], |
86 | | - special_label=d['special_label'], |
87 | | - prefix_label=d['prefix'], |
88 | | - default_label=d['default'], |
89 | | - datatype_label=d['datatype'] |
| 134 | + special=d['special_label'], |
| 135 | + prefix=d['prefix'], |
| 136 | + default=d['default'], |
| 137 | + datatype=d['datatype'], |
| 138 | + ordering=d['ordering'] |
90 | 139 | ) |
91 | 140 | out.append(line) |
92 | 141 | 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}#{msg},' for msg in self.messages] |
101 | | - |
102 | | - # calc longest line so we know how to justify info comments |
103 | | - justification = max([ln.length for ln in lines]) + 2 |
104 | | - |
105 | | - # generate string representation of each line |
106 | | - for i, ln in enumerate(lines): |
107 | | - comma = ',' if i < len(lines) - 1 else '' # ignore comma for last line |
108 | | - if self.render_comments: |
109 | | - str_line = f'{ind}{self.tb}{ln.tag_and_value + comma:<{justification}}{ln.info_comment}' |
110 | | - else: |
111 | | - str_line = f'{ind}{self.tb}{ln.tag_and_value + comma}' |
112 | | - str_lines.append(str_line) |
113 | | - |
114 | | - # join lines and return body segment |
115 | | - inputs = '\n'.join(str_lines) |
116 | | - return f"{{\n{ind}input:\n{inputs}" |
117 | 142 |
|
118 | 143 |
|
119 | 144 |
|
|
0 commit comments