flowweave 2.0.0__tar.gz → 3.0.0__tar.gz
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.
- {flowweave-2.0.0 → flowweave-3.0.0}/PKG-INFO +1 -1
- flowweave-3.0.0/flowweave/__init__.py +6 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/base.py +7 -7
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/cli.py +6 -6
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/flowweave.py +59 -35
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/message.py +8 -8
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/PKG-INFO +1 -1
- {flowweave-2.0.0 → flowweave-3.0.0}/pyproject.toml +1 -1
- flowweave-2.0.0/flowweave/__init__.py +0 -6
- {flowweave-2.0.0 → flowweave-3.0.0}/LICENSE +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/README.md +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/schema/flow.json +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave/schema/op_code.json +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/SOURCES.txt +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/dependency_links.txt +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/entry_points.txt +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/requires.txt +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/flowweave.egg-info/top_level.txt +0 -0
- {flowweave-2.0.0 → flowweave-3.0.0}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ from typing import IO, Optional
|
|
|
4
4
|
|
|
5
5
|
from colorama import Fore
|
|
6
6
|
|
|
7
|
-
class
|
|
7
|
+
class FlowWeaveResult(IntEnum):
|
|
8
8
|
FAIL = 0
|
|
9
9
|
SUCCESS = 1
|
|
10
10
|
IGNORE = 2
|
|
@@ -30,17 +30,17 @@ class TaskData:
|
|
|
30
30
|
self.do_only = do_only
|
|
31
31
|
self.show_log = show_log
|
|
32
32
|
|
|
33
|
-
class
|
|
33
|
+
class FlowWeaveTask:
|
|
34
34
|
def __init__(self, prev_future):
|
|
35
35
|
self.prev_future = prev_future
|
|
36
36
|
self.return_data = None
|
|
37
37
|
|
|
38
|
-
def __call__(self)
|
|
39
|
-
result = self.run()
|
|
40
|
-
return result
|
|
38
|
+
def __call__(self):
|
|
39
|
+
result, return_data = self.run()
|
|
40
|
+
return result, return_data
|
|
41
41
|
|
|
42
|
-
def run(self)
|
|
43
|
-
return
|
|
42
|
+
def run(self):
|
|
43
|
+
return FlowWeaveResult.SUCCESS, self.return_data
|
|
44
44
|
|
|
45
45
|
def set_task_data(self, task_data: TaskData) -> None:
|
|
46
46
|
self.task_data = task_data
|
|
@@ -8,7 +8,7 @@ import colorama
|
|
|
8
8
|
|
|
9
9
|
# Local application / relative imports
|
|
10
10
|
from .flowweave import FlowWeave
|
|
11
|
-
from .base import
|
|
11
|
+
from .base import FlowWeaveResult
|
|
12
12
|
|
|
13
13
|
def get_setting_path(args):
|
|
14
14
|
setting_path = None
|
|
@@ -23,9 +23,9 @@ def serialize(obj) -> str:
|
|
|
23
23
|
return obj.__name__
|
|
24
24
|
raise TypeError(f"Type {type(obj)} not serializable")
|
|
25
25
|
|
|
26
|
-
def show_flow_op(setting_path: str, flow_name: str) -> None:
|
|
26
|
+
def show_flow_op(setting_path: str, flow_name: str, info: bool = False) -> None:
|
|
27
27
|
flow_data = FlowWeave.load_and_validate_schema(file=setting_path, schema="flow")
|
|
28
|
-
op_dic = FlowWeave.get_op_dic(flow_data)
|
|
28
|
+
op_dic = FlowWeave.get_op_dic(flow_data, info=info)
|
|
29
29
|
print_op_dic(op_dic, flow_name)
|
|
30
30
|
|
|
31
31
|
def show_available_op() -> None:
|
|
@@ -59,7 +59,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
59
59
|
return parser
|
|
60
60
|
|
|
61
61
|
def main() -> None:
|
|
62
|
-
result =
|
|
62
|
+
result = FlowWeaveResult.SUCCESS
|
|
63
63
|
|
|
64
64
|
colorama.init(autoreset=True)
|
|
65
65
|
|
|
@@ -72,10 +72,10 @@ def main() -> None:
|
|
|
72
72
|
if not setting_path:
|
|
73
73
|
parser.error("run requires flow_file")
|
|
74
74
|
results = FlowWeave.run(setting_file=setting_path, parallel=args.parallel, show_log = args.log)
|
|
75
|
-
result = all(x ==
|
|
75
|
+
result = all(x == FlowWeaveResult.SUCCESS for x in results)
|
|
76
76
|
elif args.command == "info":
|
|
77
77
|
if args.flow_file:
|
|
78
|
-
show_flow_op(setting_path, args.flow_file)
|
|
78
|
+
show_flow_op(setting_path, args.flow_file, info=True)
|
|
79
79
|
else:
|
|
80
80
|
show_available_op()
|
|
81
81
|
else:
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Standard library
|
|
2
2
|
import copy
|
|
3
|
+
from functools import reduce
|
|
3
4
|
import importlib
|
|
4
5
|
from importlib.resources import files
|
|
6
|
+
import inspect
|
|
5
7
|
import itertools
|
|
6
8
|
import json
|
|
7
9
|
import logging
|
|
@@ -14,7 +16,7 @@ import yaml
|
|
|
14
16
|
from prefect import flow, task, get_run_logger
|
|
15
17
|
|
|
16
18
|
# Local application / relative imports
|
|
17
|
-
from .base import
|
|
19
|
+
from .base import FlowWeaveResult, TaskData, FlowWeaveTask
|
|
18
20
|
from .message import FlowMessage
|
|
19
21
|
|
|
20
22
|
class StageData():
|
|
@@ -38,15 +40,13 @@ class StageData():
|
|
|
38
40
|
text += "==========="
|
|
39
41
|
return text
|
|
40
42
|
|
|
41
|
-
class
|
|
42
|
-
task_class = None
|
|
43
|
-
|
|
43
|
+
class TaskRunner():
|
|
44
44
|
@task
|
|
45
45
|
def start(prev_future, task_data: TaskData):
|
|
46
46
|
try:
|
|
47
|
-
task_instance = task_data.task_class
|
|
47
|
+
task_instance = task_data.task_class(prev_future)
|
|
48
48
|
except AttributeError:
|
|
49
|
-
raise TypeError(f"{task_data.task_class}
|
|
49
|
+
raise TypeError(f"Failed to get instance of '{task_data.task_class}'")
|
|
50
50
|
|
|
51
51
|
# set task member variables
|
|
52
52
|
setattr(task_instance, "task_data", task_data)
|
|
@@ -62,23 +62,23 @@ class FlowWeaveTask():
|
|
|
62
62
|
run_task = True
|
|
63
63
|
if prev_future:
|
|
64
64
|
if "pre_success" == task_data.do_only:
|
|
65
|
-
run_task = True if (
|
|
65
|
+
run_task = True if (FlowWeaveResult.SUCCESS == prev_future.get("result")) else False
|
|
66
66
|
elif "pre_fail" == task_data.do_only:
|
|
67
|
-
run_task = True if (
|
|
67
|
+
run_task = True if (FlowWeaveResult.FAIL == prev_future.get("result")) else False
|
|
68
68
|
|
|
69
69
|
if run_task:
|
|
70
|
-
|
|
70
|
+
TaskRunner.message_task_start(prev_future, task_data)
|
|
71
71
|
|
|
72
72
|
try:
|
|
73
73
|
task_result, return_data = task_instance()
|
|
74
74
|
except Exception as e:
|
|
75
75
|
FlowMessage.error(e)
|
|
76
|
-
task_result =
|
|
76
|
+
task_result = FlowWeaveResult.FAIL
|
|
77
77
|
|
|
78
78
|
FlowMessage.task_end(task_data, task_result)
|
|
79
79
|
else:
|
|
80
|
-
|
|
81
|
-
task_result =
|
|
80
|
+
TaskRunner.message_task_ignore(prev_future, task_data)
|
|
81
|
+
task_result = FlowWeaveResult.IGNORE
|
|
82
82
|
|
|
83
83
|
return {"name" : task_data.name, "option" : task_data.option, "data" : return_data, "result" : task_result}
|
|
84
84
|
|
|
@@ -171,7 +171,7 @@ class FlowWeave():
|
|
|
171
171
|
|
|
172
172
|
return data
|
|
173
173
|
|
|
174
|
-
def get_op_dic(flow_data: dict):
|
|
174
|
+
def get_op_dic(flow_data: dict, info: bool = False):
|
|
175
175
|
return_dic = dict()
|
|
176
176
|
|
|
177
177
|
op_source = flow_data.get("op_source")
|
|
@@ -179,7 +179,7 @@ class FlowWeave():
|
|
|
179
179
|
for source in op_source_list:
|
|
180
180
|
source_name = f"task/{source}"
|
|
181
181
|
setting_file = f"{source_name.replace('.', '/')}/op_code.yml"
|
|
182
|
-
return_dic |= FlowWeave._get_op_dic_from_setting_file(setting_file)
|
|
182
|
+
return_dic |= FlowWeave._get_op_dic_from_setting_file(setting_file, info=info)
|
|
183
183
|
|
|
184
184
|
return return_dic
|
|
185
185
|
|
|
@@ -207,27 +207,43 @@ class FlowWeave():
|
|
|
207
207
|
op_dic = setting.get("op", {})
|
|
208
208
|
for op, op_info in op_dic.items():
|
|
209
209
|
script_name = op_info.get('script')
|
|
210
|
-
op_class = FlowWeave._get_op_class(source_name, script_name,
|
|
210
|
+
op_class = FlowWeave._get_op_class(source_name, script_name, FlowWeaveTask)
|
|
211
211
|
|
|
212
212
|
return_dic[str(op)] = op_class
|
|
213
213
|
|
|
214
214
|
return return_dic
|
|
215
215
|
|
|
216
|
-
def _get_op_class(source_name: str, script_name: str,
|
|
216
|
+
def _get_op_class(source_name: str, script_name: str, base_class):
|
|
217
217
|
module_name = f"{source_name}.{script_name}"
|
|
218
|
+
|
|
218
219
|
try:
|
|
219
220
|
module = importlib.import_module(module_name)
|
|
220
221
|
except Exception as e:
|
|
221
222
|
raise RuntimeError(f"Failed to import {module_name}: {e}")
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
candidates = []
|
|
225
|
+
|
|
226
|
+
for _, obj in inspect.getmembers(module, inspect.isclass):
|
|
227
|
+
if obj.__module__ != module.__name__:
|
|
228
|
+
continue
|
|
225
229
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return_module = module.Task.runner
|
|
230
|
+
if issubclass(obj, base_class) and obj is not base_class:
|
|
231
|
+
candidates.append(obj)
|
|
229
232
|
|
|
230
|
-
|
|
233
|
+
if len(candidates) == 0:
|
|
234
|
+
raise RuntimeError(
|
|
235
|
+
f"No subclass of {base_class.__name__} found in {module_name}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if len(candidates) > 1:
|
|
239
|
+
names = ", ".join(c.__name__ for c in candidates)
|
|
240
|
+
raise RuntimeError(
|
|
241
|
+
f"Multiple subclasses of {base_class.__name__} found in {module_name}: {names}"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
cls = candidates[0]
|
|
245
|
+
|
|
246
|
+
return cls
|
|
231
247
|
|
|
232
248
|
def _get_global_option_comb(global_option: dict) -> list:
|
|
233
249
|
keys = list(global_option.keys())
|
|
@@ -247,7 +263,7 @@ class FlowWeave():
|
|
|
247
263
|
|
|
248
264
|
@task
|
|
249
265
|
def run_flow(flow_data: dict, global_cmb: dict, op_dic: dict, part: int, all: int, show_log: bool = False) -> list[str]:
|
|
250
|
-
flow_result =
|
|
266
|
+
flow_result = FlowWeaveResult.SUCCESS
|
|
251
267
|
|
|
252
268
|
if show_log:
|
|
253
269
|
text = "= Flow =\n"
|
|
@@ -269,8 +285,8 @@ class FlowWeave():
|
|
|
269
285
|
FlowWeave._print_log(str(stage_data))
|
|
270
286
|
|
|
271
287
|
result = FlowWeave._run_stage(stage_data, show_log)
|
|
272
|
-
if
|
|
273
|
-
flow_result =
|
|
288
|
+
if FlowWeaveResult.FAIL == result:
|
|
289
|
+
flow_result = FlowWeaveResult.FAIL
|
|
274
290
|
|
|
275
291
|
FlowMessage.stage_end(stage, part, all, flow_result)
|
|
276
292
|
|
|
@@ -293,7 +309,7 @@ class FlowWeave():
|
|
|
293
309
|
logger.info(f"{text}")
|
|
294
310
|
|
|
295
311
|
def _run_stage(stage_data: StageData, show_log: bool = False):
|
|
296
|
-
stage_result =
|
|
312
|
+
stage_result = FlowWeaveResult.SUCCESS
|
|
297
313
|
|
|
298
314
|
all_futures = []
|
|
299
315
|
|
|
@@ -306,11 +322,23 @@ class FlowWeave():
|
|
|
306
322
|
|
|
307
323
|
for f in all_futures:
|
|
308
324
|
result = f.result()
|
|
309
|
-
if
|
|
310
|
-
stage_result =
|
|
325
|
+
if FlowWeaveResult.FAIL == result.get("result"):
|
|
326
|
+
stage_result = FlowWeaveResult.FAIL
|
|
311
327
|
|
|
312
328
|
return stage_result
|
|
313
329
|
|
|
330
|
+
def _deep_merge(a: dict, b: dict) -> dict:
|
|
331
|
+
result = a.copy()
|
|
332
|
+
for k, v in b.items():
|
|
333
|
+
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
|
|
334
|
+
result[k] = FlowWeave._deep_merge(result[k], v)
|
|
335
|
+
else:
|
|
336
|
+
result[k] = v
|
|
337
|
+
return result
|
|
338
|
+
|
|
339
|
+
def _deep_merge_many(*dicts):
|
|
340
|
+
return reduce(FlowWeave._deep_merge, dicts)
|
|
341
|
+
|
|
314
342
|
def _run_task(stage_data: dict, task_name: str, prev_future = None, visited = None, show_log: bool = False):
|
|
315
343
|
if visited is None:
|
|
316
344
|
visited = set()
|
|
@@ -326,11 +354,7 @@ class FlowWeave():
|
|
|
326
354
|
|
|
327
355
|
default_option = stage_data.default_option or {}
|
|
328
356
|
global_option = stage_data.global_option or {}
|
|
329
|
-
task_option =
|
|
330
|
-
default_option
|
|
331
|
-
| global_option
|
|
332
|
-
| task_dic.get("option", {})
|
|
333
|
-
)
|
|
357
|
+
task_option = FlowWeave._deep_merge_many(default_option, global_option, task_dic.get("option", {}))
|
|
334
358
|
|
|
335
359
|
task_data = TaskData(name=task_name,
|
|
336
360
|
task_class=task_module,
|
|
@@ -341,9 +365,9 @@ class FlowWeave():
|
|
|
341
365
|
do_only=task_dic.get("do_only"),
|
|
342
366
|
show_log=show_log)
|
|
343
367
|
if prev_future is None:
|
|
344
|
-
future =
|
|
368
|
+
future = TaskRunner.start.submit(None, task_data)
|
|
345
369
|
else:
|
|
346
|
-
future =
|
|
370
|
+
future = TaskRunner.start.submit(prev_future, task_data)
|
|
347
371
|
|
|
348
372
|
links = task_dic.get("chain", {}).get("next", [])
|
|
349
373
|
links = links if isinstance(links, list) else [links]
|
|
@@ -5,7 +5,7 @@ from typing import IO, Optional
|
|
|
5
5
|
from colorama import Fore
|
|
6
6
|
|
|
7
7
|
# Local application / relative imports
|
|
8
|
-
from .base import
|
|
8
|
+
from .base import FlowWeaveResult, TaskData
|
|
9
9
|
|
|
10
10
|
class FlowMessage:
|
|
11
11
|
@staticmethod
|
|
@@ -17,14 +17,14 @@ class FlowMessage:
|
|
|
17
17
|
print(*args, sep=sep, end=end, file=file, flush=flush)
|
|
18
18
|
|
|
19
19
|
@staticmethod
|
|
20
|
-
def get_result_text(result:
|
|
20
|
+
def get_result_text(result: FlowWeaveResult) -> str:
|
|
21
21
|
text = ""
|
|
22
22
|
|
|
23
|
-
if
|
|
23
|
+
if FlowWeaveResult.SUCCESS == result:
|
|
24
24
|
text = f"{Fore.GREEN}SUCCESS"
|
|
25
|
-
elif
|
|
25
|
+
elif FlowWeaveResult.IGNORE == result:
|
|
26
26
|
text = f"{Fore.CYAN}IGNORE"
|
|
27
|
-
elif
|
|
27
|
+
elif FlowWeaveResult.FAIL == result:
|
|
28
28
|
text = f"{Fore.RED}FAIL"
|
|
29
29
|
else:
|
|
30
30
|
text = f"{Fore.MAGENTA}UNKNOWN: {result.name}({result.value})"
|
|
@@ -42,7 +42,7 @@ class FlowMessage:
|
|
|
42
42
|
FlowMessage._print(text)
|
|
43
43
|
|
|
44
44
|
@staticmethod
|
|
45
|
-
def flow_end(part: int, all: int, result:
|
|
45
|
+
def flow_end(part: int, all: int, result: FlowWeaveResult) -> None:
|
|
46
46
|
result_text = FlowMessage.get_result_text(result)
|
|
47
47
|
text = f"{Fore.YELLOW}[Flow {part} / {all}] Finish - {result_text}"
|
|
48
48
|
FlowMessage._print(text)
|
|
@@ -53,7 +53,7 @@ class FlowMessage:
|
|
|
53
53
|
FlowMessage._print(text)
|
|
54
54
|
|
|
55
55
|
@staticmethod
|
|
56
|
-
def stage_end(stage: str, part: int, all: int, result:
|
|
56
|
+
def stage_end(stage: str, part: int, all: int, result: FlowWeaveResult) -> None:
|
|
57
57
|
result_text = FlowMessage.get_result_text(result)
|
|
58
58
|
text = f"{Fore.MAGENTA}[Flow {part} / {all}] Finish Stage {stage} - {result_text}"
|
|
59
59
|
FlowMessage._print(text)
|
|
@@ -79,7 +79,7 @@ class FlowMessage:
|
|
|
79
79
|
FlowMessage._print(text)
|
|
80
80
|
|
|
81
81
|
@staticmethod
|
|
82
|
-
def task_end(task_data: TaskData, result:
|
|
82
|
+
def task_end(task_data: TaskData, result: FlowWeaveResult) -> None:
|
|
83
83
|
result_text = FlowMessage.get_result_text(result)
|
|
84
84
|
text = f"{Fore.CYAN}[Flow {task_data.flow_part} / {task_data.flow_all}] Finish Task {task_data.stage_name}/{task_data.name} - {result_text}"
|
|
85
85
|
FlowMessage._print(text)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|