zrb 0.0.45__py3-none-any.whl → 0.0.47__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zrb/__init__.py +1 -1
- zrb/action/runner.py +2 -3
- zrb/builtin/__init__.py +2 -0
- zrb/builtin/_group.py +1 -1
- zrb/builtin/env.py +0 -2
- zrb/builtin/generator/_common.py +15 -16
- zrb/builtin/generator/docker_compose_task/template/src/kebab-task-name/docker-compose.yml +7 -1
- zrb/builtin/generator/docker_compose_task/template/src/kebab-task-name/image/Dockerfile +3 -1
- zrb/builtin/generator/docker_compose_task/template/src/kebab-task-name/image/main.py +0 -5
- zrb/builtin/generator/fastapp/add.py +3 -3
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/_common.py +17 -7
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/config/docker-compose.env +3 -0
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/container.py +14 -5
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/image.py +1 -1
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/local.py +1 -2
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/docker-compose.yml +24 -6
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/Dockerfile +3 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/template.env +1 -1
- zrb/builtin/generator/fastapp_module/add.py +266 -83
- zrb/builtin/generator/project/template/README.md +19 -3
- zrb/builtin/generator/project_task/add.py +12 -13
- zrb/builtin/generator/project_task/task_factory.py +12 -14
- zrb/builtin/generator/simple_python_app/add.py +3 -3
- zrb/builtin/generator/simple_python_app/template/src/kebab-app-name/docker-compose.yml +7 -1
- zrb/builtin/generator/simple_python_app/template/src/kebab-app-name/src/Dockerfile +3 -1
- zrb/builtin/generator/simple_python_app/template/src/kebab-app-name/src/main.py +0 -5
- zrb/builtin/md5.py +5 -4
- zrb/builtin/project.py +32 -0
- zrb/helper/docker_compose/file.py +22 -0
- zrb/helper/env_map/fetch.py +68 -0
- zrb/helper/file/copy_tree.py +6 -7
- zrb/helper/file/text.py +20 -0
- zrb/helper/string/jinja.py +11 -0
- zrb/task/base_task.py +457 -4
- zrb/task/cmd_task.py +1 -2
- zrb/task/decorator.py +1 -1
- zrb/task/docker_compose_task.py +3 -4
- zrb/task/http_checker.py +1 -2
- zrb/task/installer/factory.py +6 -4
- zrb/task/path_checker.py +3 -4
- zrb/task/port_checker.py +1 -2
- zrb/task/resource_maker.py +2 -3
- zrb/task_env/env.py +4 -3
- {zrb-0.0.45.dist-info → zrb-0.0.47.dist-info}/METADATA +3 -1
- {zrb-0.0.45.dist-info → zrb-0.0.47.dist-info}/RECORD +52 -51
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module_disabled.env +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module_enabled.env +0 -0
- zrb/helper/dockercompose/read.py +0 -9
- zrb/task/base_model.py +0 -456
- zrb/task_group/group.py +0 -36
- /zrb/builtin/generator/fastapp/template/{_automate/snake_app_name/config/module_disabled.env → src/kebab-app-name/all-module-disabled.env} +0 -0
- /zrb/builtin/generator/fastapp/template/{_automate/snake_app_name/config/module_enabled.env → src/kebab-app-name/all-module-enabled.env} +0 -0
- /zrb/helper/{dockercompose → docker_compose}/fetch_external_env.py +0 -0
- /zrb/{task_group → helper/env_map}/__init__.py +0 -0
- {zrb-0.0.45.dist-info → zrb-0.0.47.dist-info}/LICENSE +0 -0
- {zrb-0.0.45.dist-info → zrb-0.0.47.dist-info}/WHEEL +0 -0
- {zrb-0.0.45.dist-info → zrb-0.0.47.dist-info}/entry_points.txt +0 -0
zrb/task/base_model.py
DELETED
@@ -1,456 +0,0 @@
|
|
1
|
-
from typing import Any, Iterable, List, Mapping, Optional, Union
|
2
|
-
from typeguard import typechecked
|
3
|
-
from ..helper.accessories.color import (
|
4
|
-
get_random_color, is_valid_color, colored
|
5
|
-
)
|
6
|
-
from ..helper.accessories.icon import get_random_icon
|
7
|
-
from ..helper.list.ensure_uniqueness import ensure_uniqueness
|
8
|
-
from ..helper.string.conversion import (
|
9
|
-
to_cmd_name, to_variable_name, to_boolean
|
10
|
-
)
|
11
|
-
from ..helper.render_data import DEFAULT_RENDER_DATA
|
12
|
-
from ..helper.log import logger
|
13
|
-
from ..task_env.env import Env
|
14
|
-
from ..task_env.env_file import EnvFile
|
15
|
-
from ..task_group.group import Group
|
16
|
-
from ..task_input.base_input import BaseInput
|
17
|
-
from ..task_input._constant import RESERVED_INPUT_NAMES
|
18
|
-
|
19
|
-
import asyncio
|
20
|
-
import datetime
|
21
|
-
import os
|
22
|
-
import sys
|
23
|
-
import time
|
24
|
-
import jinja2
|
25
|
-
|
26
|
-
|
27
|
-
MAX_NAME_LENGTH = 20
|
28
|
-
MULTILINE_INDENT = ' ' * 8
|
29
|
-
|
30
|
-
|
31
|
-
class AnyExtensionFileSystemLoader(jinja2.FileSystemLoader):
|
32
|
-
def get_source(self, environment, template):
|
33
|
-
for search_dir in self.searchpath:
|
34
|
-
file_path = os.path.join(search_dir, template)
|
35
|
-
if os.path.exists(file_path):
|
36
|
-
with open(file_path, 'r') as file:
|
37
|
-
contents = file.read()
|
38
|
-
return contents, file_path, lambda: False
|
39
|
-
raise jinja2.TemplateNotFound(template)
|
40
|
-
|
41
|
-
|
42
|
-
@typechecked
|
43
|
-
class TimeTracker():
|
44
|
-
|
45
|
-
def __init__(self):
|
46
|
-
self._start_time: float = 0
|
47
|
-
self._end_time: float = 0
|
48
|
-
|
49
|
-
def start_timer(self):
|
50
|
-
self._start_time = time.time()
|
51
|
-
|
52
|
-
def end_timer(self):
|
53
|
-
self._end_time = time.time()
|
54
|
-
|
55
|
-
def get_elapsed_time(self) -> float:
|
56
|
-
return self._end_time - self._start_time
|
57
|
-
|
58
|
-
|
59
|
-
@typechecked
|
60
|
-
class AttemptTracker():
|
61
|
-
|
62
|
-
def __init__(self, retry: int = 2):
|
63
|
-
self.retry = retry
|
64
|
-
self._attempt: int = 1
|
65
|
-
|
66
|
-
def get_max_attempt(self) -> int:
|
67
|
-
return self.retry + 1
|
68
|
-
|
69
|
-
def get_attempt(self) -> int:
|
70
|
-
return self._attempt
|
71
|
-
|
72
|
-
def increase_attempt(self):
|
73
|
-
self._attempt += 1
|
74
|
-
|
75
|
-
def should_attempt(self) -> bool:
|
76
|
-
attempt = self.get_attempt()
|
77
|
-
max_attempt = self.get_max_attempt()
|
78
|
-
return attempt <= max_attempt
|
79
|
-
|
80
|
-
def is_last_attempt(self) -> bool:
|
81
|
-
attempt = self.get_attempt()
|
82
|
-
max_attempt = self.get_max_attempt()
|
83
|
-
return attempt >= max_attempt
|
84
|
-
|
85
|
-
|
86
|
-
@typechecked
|
87
|
-
class FinishTracker():
|
88
|
-
|
89
|
-
def __init__(self):
|
90
|
-
self._execution_queue: Optional[asyncio.Queue] = None
|
91
|
-
self._counter = 0
|
92
|
-
|
93
|
-
async def mark_start(self):
|
94
|
-
if self._execution_queue is None:
|
95
|
-
self._execution_queue = asyncio.Queue()
|
96
|
-
self._counter += 1
|
97
|
-
|
98
|
-
async def mark_as_done(self):
|
99
|
-
# Tracker might be started several times
|
100
|
-
# However, when the execution is marked as done, it applied globally
|
101
|
-
# Thus, we need to send event as much as the counter.
|
102
|
-
for i in range(self._counter):
|
103
|
-
await self._execution_queue.put(True)
|
104
|
-
|
105
|
-
async def is_done(self) -> bool:
|
106
|
-
while self._execution_queue is None:
|
107
|
-
await asyncio.sleep(0.05)
|
108
|
-
return await self._execution_queue.get()
|
109
|
-
|
110
|
-
|
111
|
-
@typechecked
|
112
|
-
class PidModel():
|
113
|
-
|
114
|
-
def __init__(self):
|
115
|
-
self.zrb_task_pid: int = os.getpid()
|
116
|
-
|
117
|
-
def set_task_pid(self, pid: int):
|
118
|
-
self.zrb_task_pid = pid
|
119
|
-
|
120
|
-
def get_task_pid(self) -> int:
|
121
|
-
return self.zrb_task_pid
|
122
|
-
|
123
|
-
|
124
|
-
@typechecked
|
125
|
-
class TaskDataModel():
|
126
|
-
|
127
|
-
def __init__(
|
128
|
-
self,
|
129
|
-
name: str,
|
130
|
-
group: Optional[Group] = None,
|
131
|
-
envs: Iterable[Env] = [],
|
132
|
-
env_files: Iterable[EnvFile] = [],
|
133
|
-
icon: Optional[str] = None,
|
134
|
-
color: Optional[str] = None,
|
135
|
-
):
|
136
|
-
self.name = name
|
137
|
-
self.group = group
|
138
|
-
self.envs = [
|
139
|
-
Env(name=key, os_name=key)
|
140
|
-
for key in os.environ
|
141
|
-
] + envs
|
142
|
-
self.env_files = env_files
|
143
|
-
self.icon = icon
|
144
|
-
self.color = color
|
145
|
-
self._input_map: Mapping[str, Any] = {}
|
146
|
-
self._env_map: Mapping[str, str] = {}
|
147
|
-
self._complete_name: Optional[str] = None
|
148
|
-
self._filled_complete_name: Optional[str] = None
|
149
|
-
self._rendered_str: Mapping[str, str] = {}
|
150
|
-
self._is_keyval_set = False # Flag
|
151
|
-
self._has_cli_interface = False
|
152
|
-
self._render_data: Optional[Mapping[str, Any]] = None
|
153
|
-
self._all_inputs: Optional[List[BaseInput]] = None
|
154
|
-
|
155
|
-
def get_icon(self) -> str:
|
156
|
-
if self.icon is None or self.icon == '':
|
157
|
-
self.icon = get_random_icon()
|
158
|
-
return self.icon
|
159
|
-
|
160
|
-
def get_color(self) -> str:
|
161
|
-
if self.color is None or not is_valid_color(self.color):
|
162
|
-
self.color = get_random_color()
|
163
|
-
return self.color
|
164
|
-
|
165
|
-
def get_cmd_name(self) -> str:
|
166
|
-
return to_cmd_name(self.name)
|
167
|
-
|
168
|
-
def log_debug(self, message: Any):
|
169
|
-
prefix = self._get_log_prefix()
|
170
|
-
colored_message = colored(
|
171
|
-
f'{prefix} • {message}', attrs=['dark']
|
172
|
-
)
|
173
|
-
logger.debug(colored_message)
|
174
|
-
|
175
|
-
def log_warn(self, message: Any):
|
176
|
-
prefix = self._get_log_prefix()
|
177
|
-
colored_message = colored(
|
178
|
-
f'{prefix} • {message}', attrs=['dark']
|
179
|
-
)
|
180
|
-
logger.warning(colored_message)
|
181
|
-
|
182
|
-
def log_info(self, message: Any):
|
183
|
-
prefix = self._get_log_prefix()
|
184
|
-
colored_message = colored(
|
185
|
-
f'{prefix} • {message}', attrs=['dark']
|
186
|
-
)
|
187
|
-
logger.info(colored_message)
|
188
|
-
|
189
|
-
def log_error(self, message: Any):
|
190
|
-
prefix = self._get_log_prefix()
|
191
|
-
colored_message = colored(
|
192
|
-
f'{prefix} • {message}', color='red', attrs=['bold']
|
193
|
-
)
|
194
|
-
logger.error(colored_message, exc_info=True)
|
195
|
-
|
196
|
-
def log_critical(self, message: Any):
|
197
|
-
prefix = self._get_log_prefix()
|
198
|
-
colored_message = colored(
|
199
|
-
f'{prefix} • {message}', color='red', attrs=['bold']
|
200
|
-
)
|
201
|
-
logger.critical(colored_message, exc_info=True)
|
202
|
-
|
203
|
-
def print_out(self, msg: Any):
|
204
|
-
prefix = self._get_colored_print_prefix()
|
205
|
-
print(f'🤖 ➜ {prefix} • {msg}'.rstrip(), file=sys.stderr)
|
206
|
-
|
207
|
-
def print_err(self, msg: Any):
|
208
|
-
prefix = self._get_colored_print_prefix()
|
209
|
-
print(f'🤖 ⚠ {prefix} • {msg}'.rstrip(), file=sys.stderr)
|
210
|
-
|
211
|
-
def print_out_dark(self, msg: Any):
|
212
|
-
self.print_out(colored(msg, attrs=['dark']))
|
213
|
-
|
214
|
-
def colored(self, text: str) -> str:
|
215
|
-
return colored(text, color=self.get_color())
|
216
|
-
|
217
|
-
def play_bell(self):
|
218
|
-
print('\a', end='', file=sys.stderr)
|
219
|
-
|
220
|
-
def _get_colored_print_prefix(self) -> str:
|
221
|
-
return self.colored(self._get_print_prefix())
|
222
|
-
|
223
|
-
def _get_print_prefix(self) -> str:
|
224
|
-
common_prefix = self._get_common_prefix(show_time=True)
|
225
|
-
icon = self.get_icon()
|
226
|
-
truncated_name = self._get_filled_complete_name()
|
227
|
-
return f'{common_prefix} • {icon} {truncated_name}'
|
228
|
-
|
229
|
-
def _get_log_prefix(self) -> str:
|
230
|
-
common_prefix = self._get_common_prefix(show_time=False)
|
231
|
-
icon = self.get_icon()
|
232
|
-
filled_name = self._get_filled_complete_name()
|
233
|
-
return f'{common_prefix} • {icon} {filled_name}'
|
234
|
-
|
235
|
-
def _get_common_prefix(self, show_time: bool) -> str:
|
236
|
-
attempt = self.get_attempt()
|
237
|
-
max_attempt = self.get_max_attempt()
|
238
|
-
pid = self.get_task_pid()
|
239
|
-
if show_time:
|
240
|
-
now = datetime.datetime.now().isoformat()
|
241
|
-
return f'{now} ⚙ {pid} ➤ {attempt} of {max_attempt}'
|
242
|
-
return f'⚙ {pid} ➤ {attempt} of {max_attempt}'
|
243
|
-
|
244
|
-
def _get_filled_complete_name(self) -> str:
|
245
|
-
if self._filled_complete_name is not None:
|
246
|
-
return self._filled_complete_name
|
247
|
-
complete_name = self._get_complete_name()
|
248
|
-
self._filled_complete_name = complete_name.rjust(MAX_NAME_LENGTH, ' ')
|
249
|
-
return self._filled_complete_name
|
250
|
-
|
251
|
-
def get_input_map(self) -> Mapping[str, Any]:
|
252
|
-
return self._input_map
|
253
|
-
|
254
|
-
def get_env_map(self) -> Mapping[str, str]:
|
255
|
-
return self._env_map
|
256
|
-
|
257
|
-
def _inject_env_map(
|
258
|
-
self, env_map: Mapping[str, str], override: bool = False
|
259
|
-
):
|
260
|
-
for key, val in env_map.items():
|
261
|
-
if override or key not in self._env_map:
|
262
|
-
self._env_map[key] = val
|
263
|
-
|
264
|
-
def render_any(self, val: Any) -> Any:
|
265
|
-
if isinstance(val, str):
|
266
|
-
return self.render_str(val)
|
267
|
-
return val
|
268
|
-
|
269
|
-
def render_float(self, val: Union[str, float]) -> float:
|
270
|
-
if isinstance(val, str):
|
271
|
-
return float(self.render_str(val))
|
272
|
-
return val
|
273
|
-
|
274
|
-
def render_int(self, val: Union[str, int]) -> int:
|
275
|
-
if isinstance(val, str):
|
276
|
-
return int(self.render_str(val))
|
277
|
-
return val
|
278
|
-
|
279
|
-
def render_bool(self, val: Union[str, bool]) -> bool:
|
280
|
-
if isinstance(val, str):
|
281
|
-
return to_boolean(self.render_str(val))
|
282
|
-
return val
|
283
|
-
|
284
|
-
def render_str(self, val: str) -> str:
|
285
|
-
if val in self._rendered_str:
|
286
|
-
return self._rendered_str[val]
|
287
|
-
if not self._is_probably_jinja(val):
|
288
|
-
return val
|
289
|
-
template = jinja2.Template(val)
|
290
|
-
data = self._get_render_data()
|
291
|
-
self.log_debug(f'Render string template: {val}, with data: {data}')
|
292
|
-
try:
|
293
|
-
rendered_text = template.render(data)
|
294
|
-
self.log_debug(f'Get rendered result: {rendered_text}')
|
295
|
-
except Exception:
|
296
|
-
self.log_error(f'Fail to render "{val}" with data: {data}')
|
297
|
-
self._rendered_str[val] = rendered_text
|
298
|
-
return rendered_text
|
299
|
-
|
300
|
-
def render_file(self, location: str) -> str:
|
301
|
-
location_dir = os.path.dirname(location)
|
302
|
-
env = jinja2.Environment(
|
303
|
-
loader=AnyExtensionFileSystemLoader([location_dir])
|
304
|
-
)
|
305
|
-
template = env.get_template(location)
|
306
|
-
data = self._get_render_data()
|
307
|
-
data['TEMPLATE_DIR'] = location_dir
|
308
|
-
self.log_debug(f'Render template file: {template}, with data: {data}')
|
309
|
-
rendered_text = template.render(data)
|
310
|
-
self.log_debug(f'Get rendered result: {rendered_text}')
|
311
|
-
return rendered_text
|
312
|
-
|
313
|
-
def _get_render_data(self) -> Mapping[str, Any]:
|
314
|
-
if self._render_data is not None:
|
315
|
-
return self._render_data
|
316
|
-
render_data = dict(DEFAULT_RENDER_DATA)
|
317
|
-
render_data.update({
|
318
|
-
'env': self._env_map,
|
319
|
-
'input': self._input_map,
|
320
|
-
})
|
321
|
-
self._render_data = render_data
|
322
|
-
return render_data
|
323
|
-
|
324
|
-
def _get_multiline_repr(self, text: str) -> str:
|
325
|
-
lines_repr: Iterable[str] = []
|
326
|
-
lines = text.split('\n')
|
327
|
-
if len(lines) == 1:
|
328
|
-
return lines[0]
|
329
|
-
for index, line in enumerate(lines):
|
330
|
-
line_number_repr = str(index + 1).rjust(4, '0')
|
331
|
-
lines_repr.append(f'{MULTILINE_INDENT}{line_number_repr} | {line}')
|
332
|
-
return '\n' + '\n'.join(lines_repr)
|
333
|
-
|
334
|
-
async def _set_local_keyval(
|
335
|
-
self,
|
336
|
-
kwargs: Mapping[str, Any],
|
337
|
-
env_prefix: str = ''
|
338
|
-
):
|
339
|
-
if self._is_keyval_set:
|
340
|
-
return True
|
341
|
-
self._is_keyval_set = True
|
342
|
-
# Add self.inputs to input_map
|
343
|
-
self.log_info('Set input map')
|
344
|
-
for task_input in self.get_all_inputs():
|
345
|
-
map_key = self._get_normalized_input_key(task_input.name)
|
346
|
-
self._input_map[map_key] = self.render_any(
|
347
|
-
kwargs.get(map_key, task_input.default)
|
348
|
-
)
|
349
|
-
self.log_debug(f'Input map: {self._input_map}')
|
350
|
-
# Construct envs based on self.env_files and self.envs
|
351
|
-
self.log_info('Merging env_files and envs')
|
352
|
-
envs: Iterable[Env] = []
|
353
|
-
for env_file in self.env_files:
|
354
|
-
envs += env_file.get_envs()
|
355
|
-
envs += self.envs
|
356
|
-
envs.reverse()
|
357
|
-
envs = ensure_uniqueness(
|
358
|
-
envs, lambda x, y: x.name == y.name
|
359
|
-
)
|
360
|
-
envs.reverse()
|
361
|
-
# Add envs to env_map
|
362
|
-
self.log_info('Set env map')
|
363
|
-
for task_env in envs:
|
364
|
-
env_name = task_env.name
|
365
|
-
if env_name in self._env_map:
|
366
|
-
continue
|
367
|
-
self._env_map[env_name] = self.render_any(
|
368
|
-
task_env.get(env_prefix)
|
369
|
-
)
|
370
|
-
self.log_debug(f'Env map: {self._env_map}')
|
371
|
-
|
372
|
-
def get_all_inputs(self) -> Iterable[BaseInput]:
|
373
|
-
# Override this method!!!
|
374
|
-
return self._all_inputs
|
375
|
-
|
376
|
-
def _is_probably_jinja(self, value: Any) -> bool:
|
377
|
-
if not isinstance(value, str):
|
378
|
-
return False
|
379
|
-
if '{{' in value and '}}' in value:
|
380
|
-
return True
|
381
|
-
if '{%' in value and '%}' in value:
|
382
|
-
return True
|
383
|
-
return False
|
384
|
-
|
385
|
-
def _get_normalized_input_key(self, key: str) -> str:
|
386
|
-
if key in RESERVED_INPUT_NAMES:
|
387
|
-
return key
|
388
|
-
return to_variable_name(key)
|
389
|
-
|
390
|
-
def _get_complete_name(self) -> str:
|
391
|
-
if self._complete_name is not None:
|
392
|
-
return self._complete_name
|
393
|
-
executable_prefix = ''
|
394
|
-
if self._has_cli_interface:
|
395
|
-
executable_prefix += self._get_executable_name() + ' '
|
396
|
-
cmd_name = self.get_cmd_name()
|
397
|
-
if self.group is None:
|
398
|
-
self._complete_name = f'{executable_prefix}{cmd_name}'
|
399
|
-
return self._complete_name
|
400
|
-
group_cmd_name = self.group.get_complete_name()
|
401
|
-
self._complete_name = f'{executable_prefix}{group_cmd_name} {cmd_name}'
|
402
|
-
return self._complete_name
|
403
|
-
|
404
|
-
def _get_executable_name(self) -> str:
|
405
|
-
if len(sys.argv) > 0 and sys.argv[0] != '':
|
406
|
-
return os.path.basename(sys.argv[0])
|
407
|
-
return 'zrb'
|
408
|
-
|
409
|
-
def set_has_cli_interface(self):
|
410
|
-
self._has_cli_interface = True
|
411
|
-
|
412
|
-
|
413
|
-
@typechecked
|
414
|
-
class TaskModel(
|
415
|
-
TaskDataModel, PidModel, FinishTracker, AttemptTracker, TimeTracker
|
416
|
-
):
|
417
|
-
|
418
|
-
def __init__(
|
419
|
-
self,
|
420
|
-
name: str,
|
421
|
-
group: Optional[Group] = None,
|
422
|
-
envs: Iterable[Env] = [],
|
423
|
-
env_files: Iterable[EnvFile] = [],
|
424
|
-
icon: Optional[str] = None,
|
425
|
-
color: Optional[str] = None,
|
426
|
-
retry: int = 2,
|
427
|
-
):
|
428
|
-
TaskDataModel.__init__(
|
429
|
-
self,
|
430
|
-
name=name,
|
431
|
-
group=group,
|
432
|
-
envs=envs,
|
433
|
-
env_files=env_files,
|
434
|
-
icon=icon,
|
435
|
-
color=color
|
436
|
-
)
|
437
|
-
retry = self.ensure_non_negative(retry, 'Find negative retry')
|
438
|
-
PidModel.__init__(self)
|
439
|
-
FinishTracker.__init__(self)
|
440
|
-
AttemptTracker.__init__(self, retry=retry)
|
441
|
-
TimeTracker.__init__(self)
|
442
|
-
|
443
|
-
def ensure_non_negative(self, value: float, error_label: str) -> float:
|
444
|
-
if value < 0:
|
445
|
-
self.log_warn(f'{error_label}: {value}')
|
446
|
-
return 0
|
447
|
-
return value
|
448
|
-
|
449
|
-
def show_done_info(self):
|
450
|
-
complete_name = self._get_complete_name()
|
451
|
-
elapsed_time = self.get_elapsed_time()
|
452
|
-
message = '\n'.join([
|
453
|
-
f'{complete_name} completed in {elapsed_time} seconds',
|
454
|
-
])
|
455
|
-
self.print_out_dark(message)
|
456
|
-
self.play_bell()
|
zrb/task_group/group.py
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
from typing import Optional, TypeVar
|
2
|
-
from typeguard import typechecked
|
3
|
-
from ..helper.string.conversion import to_cmd_name
|
4
|
-
|
5
|
-
TGroup = TypeVar('TGroup', bound='Group')
|
6
|
-
|
7
|
-
|
8
|
-
@typechecked
|
9
|
-
class Group():
|
10
|
-
|
11
|
-
def __init__(
|
12
|
-
self,
|
13
|
-
name: str,
|
14
|
-
description: Optional[str] = None,
|
15
|
-
parent: Optional[TGroup] = None
|
16
|
-
):
|
17
|
-
self.name = name
|
18
|
-
self.description = description
|
19
|
-
self.parent = parent
|
20
|
-
|
21
|
-
def get_cmd_name(self) -> str:
|
22
|
-
return to_cmd_name(self.name)
|
23
|
-
|
24
|
-
def get_complete_name(self) -> str:
|
25
|
-
cmd_name = self.get_cmd_name()
|
26
|
-
if self.parent is None:
|
27
|
-
return cmd_name
|
28
|
-
parent_cmd_name = self.parent.get_complete_name()
|
29
|
-
return f'{parent_cmd_name} {cmd_name}'
|
30
|
-
|
31
|
-
def get_id(self) -> str:
|
32
|
-
group_id = self.get_cmd_name()
|
33
|
-
if self.parent is None:
|
34
|
-
return group_id
|
35
|
-
parent_group_id = self.parent.get_id()
|
36
|
-
return f'{parent_group_id} {group_id}'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|