zrb 0.1.1__py3-none-any.whl → 0.2.0__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/__main__.py +5 -1
- zrb/action/runner.py +15 -8
- zrb/builtin/helper/reccuring_action.py +27 -2
- zrb/builtin/schedule.py +4 -3
- zrb/builtin/watch_changes.py +13 -2
- zrb/config/config.py +1 -0
- zrb/helper/cli.py +26 -11
- zrb/helper/default_env.py +23 -13
- zrb/helper/file/copy_tree.py +4 -1
- zrb/helper/file/match.py +20 -0
- zrb/helper/loader/load_module.py +13 -5
- zrb/task/any_task.py +114 -2
- zrb/task/base_task/base_task.py +39 -3
- zrb/task/base_task/component/base_task_model.py +28 -5
- zrb/task/base_task/component/common_task_model.py +30 -15
- zrb/task/base_task/component/renderer.py +6 -0
- zrb/task/cmd_task.py +17 -24
- zrb/task/docker_compose_task.py +0 -1
- zrb/task/path_checker.py +25 -4
- zrb/task/path_watcher.py +48 -7
- zrb/task/recurring_task.py +60 -9
- zrb/task/time_watcher.py +7 -0
- zrb/task_group/group.py +3 -3
- {zrb-0.1.1.dist-info → zrb-0.2.0.dist-info}/METADATA +5 -5
- {zrb-0.1.1.dist-info → zrb-0.2.0.dist-info}/RECORD +28 -27
- {zrb-0.1.1.dist-info → zrb-0.2.0.dist-info}/LICENSE +0 -0
- {zrb-0.1.1.dist-info → zrb-0.2.0.dist-info}/WHEEL +0 -0
- {zrb-0.1.1.dist-info → zrb-0.2.0.dist-info}/entry_points.txt +0 -0
zrb/__main__.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
from zrb.config.config import logging_level
|
1
2
|
from zrb.helper.cli import create_cli
|
2
3
|
from zrb.helper.default_env import inject_default_env
|
3
4
|
from zrb.helper.log import logger
|
4
5
|
from zrb.helper.accessories.color import colored
|
5
6
|
|
7
|
+
import logging
|
8
|
+
|
6
9
|
inject_default_env()
|
7
|
-
|
10
|
+
if logging_level <= logging.INFO:
|
11
|
+
logger.info(colored('Create CLI instance', attrs=['dark']))
|
8
12
|
cli = create_cli()
|
9
13
|
|
10
14
|
if __name__ == '__main__':
|
zrb/action/runner.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
from zrb.config.config import logging_level
|
1
2
|
from zrb.helper.typing import Any, Callable, List, Mapping, Union
|
2
3
|
from zrb.helper.typecheck import typechecked
|
3
4
|
from zrb.helper.log import logger
|
4
5
|
from zrb.helper.accessories.color import colored
|
5
6
|
from zrb.task_group.group import Group as TaskGroup
|
6
7
|
from zrb.task.any_task import AnyTask
|
8
|
+
|
7
9
|
import click
|
10
|
+
import logging
|
8
11
|
import sys
|
9
12
|
|
10
13
|
CliSubcommand = Union[click.Group, click.Command]
|
@@ -18,14 +21,16 @@ class Runner():
|
|
18
21
|
'''
|
19
22
|
|
20
23
|
def __init__(self, env_prefix: str = ''):
|
21
|
-
|
24
|
+
if logging_level <= logging.INFO:
|
25
|
+
logger.info(colored('Create runner', attrs=['dark']))
|
22
26
|
self.__env_prefix = env_prefix
|
23
27
|
self.__tasks: List[AnyTask] = []
|
24
28
|
self.__registered_groups: Mapping[str, click.Group] = {}
|
25
29
|
self.__top_levels: List[CliSubcommand] = []
|
26
30
|
self.__subcommands: Mapping[str, List[click.Group]] = {}
|
27
31
|
self.__registered_task_cli_name: List[str] = []
|
28
|
-
|
32
|
+
if logging_level <= logging.INFO:
|
33
|
+
logger.info(colored('Runner created', attrs=['dark']))
|
29
34
|
|
30
35
|
def register(self, *tasks: AnyTask):
|
31
36
|
for task in tasks:
|
@@ -35,14 +40,16 @@ class Runner():
|
|
35
40
|
raise RuntimeError(
|
36
41
|
f'Task "{cli_name}" has already been registered'
|
37
42
|
)
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
if logging_level <= logging.DEBUG:
|
44
|
+
logger.debug(
|
45
|
+
colored(f'Register task: "{cli_name}"', attrs=['dark'])
|
46
|
+
)
|
41
47
|
self.__tasks.append(task)
|
42
48
|
self.__registered_task_cli_name.append(cli_name)
|
43
|
-
|
44
|
-
|
45
|
-
|
49
|
+
if logging_level <= logging.DEBUG:
|
50
|
+
logger.debug(
|
51
|
+
colored(f'Task registered: "{cli_name}"', attrs=['dark'])
|
52
|
+
)
|
46
53
|
|
47
54
|
def serve(self, cli: click.Group) -> click.Group:
|
48
55
|
for task in self.__tasks:
|
@@ -1,11 +1,15 @@
|
|
1
|
+
from zrb.helper.typing import Any
|
1
2
|
from zrb.task.task import Task
|
3
|
+
from zrb.task.decorator import python_task
|
2
4
|
from zrb.task.cmd_task import CmdTask
|
3
5
|
from zrb.task.notifier import Notifier
|
4
6
|
from zrb.task_input.str_input import StrInput
|
5
7
|
|
6
8
|
|
7
9
|
def create_recurring_action(
|
8
|
-
|
10
|
+
notif_title: str,
|
11
|
+
trigger_caption: str,
|
12
|
+
trigger_xcom_key: str,
|
9
13
|
default_message: str = '👋'
|
10
14
|
) -> Task:
|
11
15
|
# define inputs
|
@@ -20,11 +24,16 @@ def create_recurring_action(
|
|
20
24
|
prompt='Command to be executed',
|
21
25
|
)
|
22
26
|
# define tasks
|
27
|
+
show_trigger_info = _create_show_trigger_info(
|
28
|
+
trigger_caption=trigger_caption,
|
29
|
+
trigger_xcom_key=trigger_xcom_key
|
30
|
+
)
|
23
31
|
run_command = CmdTask(
|
24
32
|
name='run-command',
|
25
33
|
icon='⚙️',
|
26
34
|
color='blue',
|
27
35
|
inputs=[command_input],
|
36
|
+
upstreams=[show_trigger_info],
|
28
37
|
should_execute='{{ input.command != "" }}',
|
29
38
|
cmd='{{ input.command }}'
|
30
39
|
)
|
@@ -33,8 +42,9 @@ def create_recurring_action(
|
|
33
42
|
icon='📢',
|
34
43
|
color='green',
|
35
44
|
inputs=[message_input],
|
36
|
-
title=
|
45
|
+
title=notif_title,
|
37
46
|
message='{{ input.message }}',
|
47
|
+
upstreams=[show_trigger_info],
|
38
48
|
should_execute='{{ input.message != "" }}',
|
39
49
|
)
|
40
50
|
# return aggregator task
|
@@ -44,3 +54,18 @@ def create_recurring_action(
|
|
44
54
|
upstreams=[run_command, notify],
|
45
55
|
retry=0
|
46
56
|
)
|
57
|
+
|
58
|
+
|
59
|
+
def _create_show_trigger_info(
|
60
|
+
trigger_caption: str,
|
61
|
+
trigger_xcom_key: str
|
62
|
+
) -> Task:
|
63
|
+
@python_task(
|
64
|
+
name='show-trigger-info',
|
65
|
+
icon='🔍',
|
66
|
+
color='magenta',
|
67
|
+
)
|
68
|
+
def show_trigger_info(*args: Any, **kwargs: Any):
|
69
|
+
task: Task = kwargs.get('_task')
|
70
|
+
task.print_out(f'{trigger_caption}: {task.get_xcom(trigger_xcom_key)}')
|
71
|
+
return show_trigger_info
|
zrb/builtin/schedule.py
CHANGED
@@ -20,15 +20,16 @@ schedule = RecurringTask(
|
|
20
20
|
],
|
21
21
|
triggers=[
|
22
22
|
TimeWatcher(
|
23
|
-
name='watch-
|
23
|
+
name='watch-time',
|
24
24
|
color='cyan',
|
25
25
|
icon='⏰',
|
26
26
|
schedule='{{input.schedule}}'
|
27
27
|
)
|
28
28
|
],
|
29
29
|
task=create_recurring_action(
|
30
|
-
|
31
|
-
|
30
|
+
notif_title='Schedule',
|
31
|
+
trigger_caption='Schedule',
|
32
|
+
trigger_xcom_key='watch-time.scheduled-time'
|
32
33
|
)
|
33
34
|
)
|
34
35
|
runner.register(schedule)
|
zrb/builtin/watch_changes.py
CHANGED
@@ -17,15 +17,26 @@ watch_changes = RecurringTask(
|
|
17
17
|
prompt='File pattern',
|
18
18
|
description='File pattern to be watched'
|
19
19
|
),
|
20
|
+
StrInput(
|
21
|
+
name='ignored-pattern',
|
22
|
+
default='',
|
23
|
+
prompt='Ignored file pattern',
|
24
|
+
description='Ignored file pattern'
|
25
|
+
),
|
20
26
|
],
|
21
27
|
triggers=[
|
22
28
|
PathWatcher(
|
23
29
|
name='watch-path',
|
24
30
|
color='cyan',
|
25
31
|
icon='👀',
|
26
|
-
path='{{input.pattern}}'
|
32
|
+
path='{{input.pattern}}',
|
33
|
+
ignored_path='{{input.ignored_pattern}}'
|
27
34
|
)
|
28
35
|
],
|
29
|
-
task=create_recurring_action(
|
36
|
+
task=create_recurring_action(
|
37
|
+
notif_title='Watch',
|
38
|
+
trigger_caption='File changes',
|
39
|
+
trigger_xcom_key='watch-path.file'
|
40
|
+
)
|
30
41
|
)
|
31
42
|
runner.register(watch_changes)
|
zrb/config/config.py
CHANGED
zrb/helper/cli.py
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
from zrb.helper.typecheck import typechecked
|
2
2
|
from zrb.runner import runner
|
3
|
-
from zrb.config.config import
|
3
|
+
from zrb.config.config import (
|
4
|
+
init_scripts, should_load_builtin, version, logging_level
|
5
|
+
)
|
4
6
|
from zrb.helper.loader.load_module import load_module
|
5
7
|
from zrb.helper.log import logger
|
6
8
|
from zrb.helper.accessories.color import colored
|
9
|
+
from functools import lru_cache
|
7
10
|
|
8
11
|
import click
|
12
|
+
import logging
|
9
13
|
import os
|
10
14
|
import sys
|
11
15
|
|
@@ -30,41 +34,52 @@ class MultilineHelpClickGroup(click.Group):
|
|
30
34
|
formatter.write(self.help)
|
31
35
|
|
32
36
|
|
37
|
+
@lru_cache
|
33
38
|
@typechecked
|
34
39
|
def create_cli() -> click.Group:
|
35
|
-
|
40
|
+
if logging_level <= logging.INFO:
|
41
|
+
logger.info(colored('Prepare CLI', attrs=['dark']))
|
36
42
|
zrb_cli_group = MultilineHelpClickGroup(name='zrb', help=HELP)
|
37
43
|
# load from ZRB_INIT_SCRIPTS environment
|
38
44
|
for init_script in init_scripts:
|
39
|
-
|
40
|
-
|
41
|
-
|
45
|
+
if logging_level <= logging.INFO:
|
46
|
+
logger.info(colored(
|
47
|
+
f'Load modules from {init_script}', attrs=['dark']
|
48
|
+
))
|
42
49
|
load_module(script_path=init_script)
|
43
50
|
# Load default tasks
|
44
51
|
if should_load_builtin:
|
45
|
-
|
52
|
+
if logging_level <= logging.INFO:
|
53
|
+
logger.info(colored('Load builtins', attrs=['dark']))
|
46
54
|
from zrb import builtin
|
47
55
|
assert builtin
|
48
56
|
# Load zrb_init.py
|
49
57
|
project_dir = os.getenv('ZRB_PROJECT_DIR', os.getcwd())
|
50
58
|
_load_zrb_init(project_dir)
|
51
59
|
# Serve all tasks registered to runner
|
52
|
-
|
60
|
+
if logging_level <= logging.INFO:
|
61
|
+
logger.info(colored('Serve CLI', attrs=['dark']))
|
53
62
|
cli = runner.serve(zrb_cli_group)
|
54
63
|
return cli
|
55
64
|
|
56
65
|
|
66
|
+
@lru_cache
|
57
67
|
@typechecked
|
58
68
|
def _load_zrb_init(project_dir: str):
|
59
69
|
project_script = os.path.join(project_dir, 'zrb_init.py')
|
60
70
|
if not os.path.isfile(project_script):
|
61
71
|
return
|
62
72
|
sys.path.append(project_dir)
|
63
|
-
|
73
|
+
if logging_level <= logging.INFO:
|
74
|
+
logger.info(colored(f'Set sys.path to {sys.path}', attrs=['dark']))
|
64
75
|
python_path = _get_new_python_path(project_dir)
|
65
|
-
|
76
|
+
if logging_level <= logging.INFO:
|
77
|
+
logger.info(
|
78
|
+
colored(f'Set PYTHONPATH to {python_path}', attrs=['dark'])
|
79
|
+
)
|
66
80
|
os.environ['PYTHONPATH'] = python_path
|
67
|
-
|
81
|
+
if logging_level <= logging.INFO:
|
82
|
+
logger.info(colored(f'Load modules from {project_script}', attrs=['dark']))
|
68
83
|
load_module(script_path=project_script)
|
69
84
|
|
70
85
|
|
@@ -73,4 +88,4 @@ def _get_new_python_path(project_dir: str) -> str:
|
|
73
88
|
current_python_path = os.getenv('PYTHONPATH')
|
74
89
|
if current_python_path is None or current_python_path == '':
|
75
90
|
return project_dir
|
76
|
-
return
|
91
|
+
return ':'.join([current_python_path, project_dir])
|
zrb/helper/default_env.py
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
from zrb.config.config import logging_level
|
1
2
|
from zrb.helper.typecheck import typechecked
|
2
3
|
from zrb.helper.typing import Mapping, Optional
|
3
4
|
from zrb.helper.accessories.color import colored
|
4
5
|
from zrb.helper.log import logger
|
6
|
+
from functools import lru_cache
|
7
|
+
|
8
|
+
import logging
|
5
9
|
import os
|
6
10
|
|
7
11
|
_PROJECT_DIR_MAP: Mapping[str, str] = {}
|
@@ -10,36 +14,42 @@ _PROJECT_DIR_MAP: Mapping[str, str] = {}
|
|
10
14
|
def inject_default_env():
|
11
15
|
# Inject PYTHONUNBUFFERED
|
12
16
|
if 'PYTHONUNBUFFERED' not in os.environ:
|
13
|
-
|
17
|
+
if logging_level <= logging.INFO:
|
18
|
+
logger.info(colored('Set PYTHONUNBUFFERED to 1', attrs=['dark']))
|
14
19
|
os.environ['PYTHONUNBUFFERED'] = '1'
|
15
20
|
# Inject ZRB_HOME_DIR
|
16
|
-
zrb_home_dir = os.path.dirname(os.path.dirname(__file__))
|
17
21
|
if 'ZRB_HOME_DIR' not in os.environ:
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
default_home_dir = os.path.dirname(os.path.dirname(__file__))
|
23
|
+
if logging_level <= logging.INFO:
|
24
|
+
logger.info(colored(
|
25
|
+
f'Set ZRB_HOME_DIR to {default_home_dir}', attrs=['dark']
|
26
|
+
))
|
27
|
+
os.environ['ZRB_HOME_DIR'] = default_home_dir
|
22
28
|
# Inject ZRB_PROJECT_DIR
|
23
29
|
current_dir = os.getcwd()
|
24
30
|
if current_dir not in _PROJECT_DIR_MAP:
|
25
|
-
|
31
|
+
if logging_level <= logging.INFO:
|
32
|
+
logger.info(colored('Getting project directory', attrs=['dark']))
|
26
33
|
zrb_project_dir = _get_project_dir(current_dir)
|
27
34
|
if zrb_project_dir is None:
|
28
35
|
zrb_project_dir = current_dir
|
29
36
|
_PROJECT_DIR_MAP[current_dir] = zrb_project_dir
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
if logging_level <= logging.INFO:
|
38
|
+
logger.info(colored(
|
39
|
+
f'Set ZRB_PROJECT_DIR to {zrb_project_dir}', attrs=['dark']
|
40
|
+
))
|
33
41
|
os.environ['ZRB_PROJECT_DIR'] = zrb_project_dir
|
34
42
|
# Inject ZRB_PROJECT_NAME
|
35
43
|
if 'ZRB_PROJECT_NAME' not in os.environ:
|
36
44
|
zrb_project_name = os.path.basename(zrb_project_dir)
|
37
|
-
|
38
|
-
|
39
|
-
|
45
|
+
if logging_level <= logging.INFO:
|
46
|
+
logger.info(colored(
|
47
|
+
f'Set ZRB_PROJECT_NAME to {zrb_project_name}', attrs=['dark']
|
48
|
+
))
|
40
49
|
os.environ['ZRB_PROJECT_NAME'] = zrb_project_name
|
41
50
|
|
42
51
|
|
52
|
+
@lru_cache
|
43
53
|
@typechecked
|
44
54
|
def _get_project_dir(project_dir: str) -> Optional[str]:
|
45
55
|
project_script = os.path.join(project_dir, 'zrb_init.py')
|
zrb/helper/file/copy_tree.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
from zrb.config.config import logging_level
|
1
2
|
from zrb.helper.typing import Iterable, Mapping, Optional
|
2
3
|
from zrb.helper.typecheck import typechecked
|
3
4
|
from zrb.helper.file.text import read_text_file_async, write_text_file_async
|
4
5
|
from zrb.helper.string.parse_replacement import parse_replacement
|
5
6
|
from zrb.helper.log import logger
|
6
7
|
|
8
|
+
import logging
|
7
9
|
import os
|
8
10
|
import shutil
|
9
11
|
import fnmatch
|
@@ -49,4 +51,5 @@ async def copy_tree(
|
|
49
51
|
new_file_content = parse_replacement(file_content, replacements)
|
50
52
|
await write_text_file_async(new_dst_name, new_file_content)
|
51
53
|
except Exception:
|
52
|
-
|
54
|
+
if logging_level <= logging.ERROR:
|
55
|
+
logger.error(f'Cannot parse file: {new_dst_name}')
|
zrb/helper/file/match.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
from zrb.helper.typing import List, Iterable
|
2
|
+
from zrb.helper.typecheck import typechecked
|
3
|
+
import fnmatch
|
4
|
+
import glob
|
5
|
+
|
6
|
+
|
7
|
+
@typechecked
|
8
|
+
def get_file_names(
|
9
|
+
glob_path: str, glob_ignored_paths: Iterable[str]
|
10
|
+
) -> List[str]:
|
11
|
+
matches = []
|
12
|
+
for file in glob.glob(glob_path, recursive=True):
|
13
|
+
should_ignore = any(
|
14
|
+
fnmatch.fnmatch(file, ignored_path) or
|
15
|
+
fnmatch.fnmatch(file, ignored_path + '**')
|
16
|
+
for ignored_path in glob_ignored_paths
|
17
|
+
)
|
18
|
+
if not should_ignore:
|
19
|
+
matches.append(file)
|
20
|
+
return matches
|
zrb/helper/loader/load_module.py
CHANGED
@@ -1,23 +1,31 @@
|
|
1
|
+
from zrb.config.config import logging_level
|
1
2
|
from zrb.helper.typecheck import typechecked
|
2
3
|
from zrb.helper.log import logger
|
3
4
|
from zrb.helper.accessories.color import colored
|
5
|
+
from functools import lru_cache
|
4
6
|
|
5
|
-
import importlib.util
|
6
7
|
import os
|
8
|
+
import logging
|
7
9
|
import re
|
8
10
|
|
9
11
|
pattern = re.compile('[^a-zA-Z0-9]')
|
10
12
|
|
11
13
|
|
14
|
+
@lru_cache
|
12
15
|
@typechecked
|
13
16
|
def load_module(script_path: str):
|
14
17
|
if not os.path.isfile(script_path):
|
15
18
|
return
|
19
|
+
import importlib.util
|
16
20
|
module_name = pattern.sub('', script_path)
|
17
|
-
|
21
|
+
if logging_level <= logging.INFO:
|
22
|
+
logger.info(colored(f'Get module spec: {script_path}', attrs=['dark']))
|
18
23
|
spec = importlib.util.spec_from_file_location(module_name, script_path)
|
19
|
-
|
24
|
+
if logging_level <= logging.INFO:
|
25
|
+
logger.info(colored(f'Create module: {script_path}', attrs=['dark']))
|
20
26
|
module = importlib.util.module_from_spec(spec)
|
21
|
-
|
27
|
+
if logging_level <= logging.INFO:
|
28
|
+
logger.info(colored(f'Exec module: {script_path}', attrs=['dark']))
|
22
29
|
spec.loader.exec_module(module)
|
23
|
-
|
30
|
+
if logging_level <= logging.INFO:
|
31
|
+
logger.info(colored(f'Module executed: {script_path}', attrs=['dark']))
|
zrb/task/any_task.py
CHANGED
@@ -94,6 +94,69 @@ class AnyTask(ABC):
|
|
94
94
|
'''
|
95
95
|
pass
|
96
96
|
|
97
|
+
@abstractmethod
|
98
|
+
def set_xcom(self, key: str, value: Any) -> str:
|
99
|
+
'''
|
100
|
+
Set xcom for cross task communication.
|
101
|
+
|
102
|
+
Argss:
|
103
|
+
key (str): Xcom key
|
104
|
+
value (str): The value of the xcom
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
str: Empty string
|
108
|
+
|
109
|
+
Examples:
|
110
|
+
>>> from zrb import Task
|
111
|
+
>>> class MyTask(Task):
|
112
|
+
>>> async def run(self, *args: Any, **kwargs: Any) -> int:
|
113
|
+
>>> self.set_xcom('magic_word', 'hello')
|
114
|
+
>>> magic_word = self.get_xcom('magic_word')
|
115
|
+
>>> return 42
|
116
|
+
'''
|
117
|
+
pass
|
118
|
+
|
119
|
+
@abstractmethod
|
120
|
+
def set_task_xcom(self, key: str, value: Any) -> str:
|
121
|
+
'''
|
122
|
+
Set task xcom for cross task communication.
|
123
|
+
|
124
|
+
Argss:
|
125
|
+
key (str): Xcom key
|
126
|
+
value (str): The value of the xcom
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
str: Empty string
|
130
|
+
|
131
|
+
Examples:
|
132
|
+
>>> from zrb import Task
|
133
|
+
>>> class MyTask(Task):
|
134
|
+
>>> async def run(self, *args: Any, **kwargs: Any) -> int:
|
135
|
+
>>> self.set_task_xcom('magic_word', 'hello')
|
136
|
+
>>> magic_word = self.get_xcom(f'{self.get_name()}.magic_word')
|
137
|
+
>>> return 42
|
138
|
+
'''
|
139
|
+
pass
|
140
|
+
|
141
|
+
@abstractmethod
|
142
|
+
def get_xcom(self, key: str) -> str:
|
143
|
+
'''
|
144
|
+
Get xcom value for cross task communication.
|
145
|
+
|
146
|
+
Argss:
|
147
|
+
key (str): Xcom key
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
str: Value of xcom
|
151
|
+
|
152
|
+
Examples:
|
153
|
+
>>> from zrb import Task
|
154
|
+
>>> class MyTask(Task):
|
155
|
+
>>> async def run(self, *args: Any, **kwargs: Any) -> int:
|
156
|
+
>>> return self.get_xcom('magic_word')
|
157
|
+
'''
|
158
|
+
pass
|
159
|
+
|
97
160
|
@abstractmethod
|
98
161
|
async def on_triggered(self):
|
99
162
|
'''
|
@@ -254,6 +317,45 @@ class AnyTask(ABC):
|
|
254
317
|
'''
|
255
318
|
pass
|
256
319
|
|
320
|
+
@abstractmethod
|
321
|
+
def insert_checker(self, *checkers: TAnyTask):
|
322
|
+
'''
|
323
|
+
Inserts one or more `AnyTask` instances at the beginning of the current task's checker list.
|
324
|
+
|
325
|
+
This method is used to define dependencies for the current task. Tasks in the checker list are
|
326
|
+
executed before the current task. Adding a task to the beginning of the list means it will be
|
327
|
+
executed earlier than those already in the list.
|
328
|
+
|
329
|
+
Args:
|
330
|
+
checkers (TAnyTask): One or more task instances to be added to the checker list.
|
331
|
+
|
332
|
+
Examples:
|
333
|
+
>>> from zrb import Task
|
334
|
+
>>> task = Task(name='task')
|
335
|
+
>>> checker_task = Task(name='checker-task')
|
336
|
+
>>> task.insert_checker(checker_task)
|
337
|
+
'''
|
338
|
+
pass
|
339
|
+
|
340
|
+
@abstractmethod
|
341
|
+
def add_checker(self, *checkers: TAnyTask):
|
342
|
+
'''
|
343
|
+
Adds one or more `AnyTask` instances to the end of the current task's checker list.
|
344
|
+
|
345
|
+
This method appends tasks to the checker list, indicating that these tasks should be executed
|
346
|
+
before the current task, but after any tasks already in the checker list.
|
347
|
+
|
348
|
+
Args:
|
349
|
+
checkers (TAnyTask): One or more task instances to be added to the checker list.
|
350
|
+
|
351
|
+
Examples:
|
352
|
+
>>> from zrb import Task
|
353
|
+
>>> task = Task(name='task')
|
354
|
+
>>> checker_task = Task(name='checker-task')
|
355
|
+
>>> task.add_checker(checker_task)
|
356
|
+
'''
|
357
|
+
pass
|
358
|
+
|
257
359
|
@abstractmethod
|
258
360
|
def insert_upstream(self, *upstreams: TAnyTask):
|
259
361
|
'''
|
@@ -584,6 +686,16 @@ class AnyTask(ABC):
|
|
584
686
|
'''
|
585
687
|
pass
|
586
688
|
|
689
|
+
@abstractmethod
|
690
|
+
def get_name(self) -> str:
|
691
|
+
'''
|
692
|
+
Get task name
|
693
|
+
|
694
|
+
Returns:
|
695
|
+
str: name of the task
|
696
|
+
'''
|
697
|
+
pass
|
698
|
+
|
587
699
|
@abstractmethod
|
588
700
|
def get_cli_name(self) -> str:
|
589
701
|
'''
|
@@ -972,14 +1084,14 @@ class AnyTask(ABC):
|
|
972
1084
|
'''
|
973
1085
|
For internal use.
|
974
1086
|
|
975
|
-
|
1087
|
+
Call `print_result` or print values based on result type and other conditions.
|
976
1088
|
'''
|
977
1089
|
pass
|
978
1090
|
|
979
1091
|
@abstractmethod
|
980
1092
|
def print_result(self, result: Any):
|
981
1093
|
'''
|
982
|
-
|
1094
|
+
Print the task result to stdout for further processing.
|
983
1095
|
|
984
1096
|
Override this method in subclasses to customize how the task result is displayed
|
985
1097
|
or processed. Useful for integrating the task output with other systems or
|
zrb/task/base_task/base_task.py
CHANGED
@@ -37,6 +37,8 @@ class BaseTask(
|
|
37
37
|
Base class for all tasks.
|
38
38
|
Every task definition should be extended from this class.
|
39
39
|
'''
|
40
|
+
__xcom: Mapping[str, Mapping[str, str]] = {}
|
41
|
+
|
40
42
|
def __init__(
|
41
43
|
self,
|
42
44
|
name: str,
|
@@ -104,7 +106,9 @@ class BaseTask(
|
|
104
106
|
self.__is_execution_triggered: bool = False
|
105
107
|
self.__is_execution_started: bool = False
|
106
108
|
|
107
|
-
def __rshift__(
|
109
|
+
def __rshift__(
|
110
|
+
self, operand: Union[AnyParallel, AnyTask]
|
111
|
+
) -> Union[AnyParallel, AnyTask]:
|
108
112
|
if isinstance(operand, AnyTask):
|
109
113
|
operand.add_upstream(self)
|
110
114
|
return operand
|
@@ -114,6 +118,33 @@ class BaseTask(
|
|
114
118
|
other_task.add_upstream(self)
|
115
119
|
return operand
|
116
120
|
|
121
|
+
def set_task_xcom(self, key: str, value: Any) -> str:
|
122
|
+
return self.set_xcom(
|
123
|
+
key='.'.join([self.get_name(), key]),
|
124
|
+
value=value
|
125
|
+
)
|
126
|
+
|
127
|
+
def set_xcom(self, key: str, value: Any) -> str:
|
128
|
+
execution_id = self.get_execution_id()
|
129
|
+
if execution_id not in self.__xcom:
|
130
|
+
self.__xcom[execution_id] = {}
|
131
|
+
execution_id = self.get_execution_id()
|
132
|
+
self.__xcom[execution_id][key] = f'{value}'
|
133
|
+
return ''
|
134
|
+
|
135
|
+
def get_xcom(self, key: str) -> str:
|
136
|
+
execution_id = self.get_execution_id()
|
137
|
+
if execution_id not in self.__xcom:
|
138
|
+
return ''
|
139
|
+
return self.__xcom[execution_id].get(key, '')
|
140
|
+
|
141
|
+
def clear_xcom(self, execution_id: str = '') -> str:
|
142
|
+
if execution_id == '':
|
143
|
+
execution_id = self.get_execution_id()
|
144
|
+
if execution_id in self.__xcom:
|
145
|
+
del self.__xcom[execution_id]
|
146
|
+
return ''
|
147
|
+
|
117
148
|
def copy(self) -> AnyTask:
|
118
149
|
return copy.deepcopy(self)
|
119
150
|
|
@@ -224,6 +255,7 @@ class BaseTask(
|
|
224
255
|
]
|
225
256
|
results = await asyncio.gather(*coroutines)
|
226
257
|
result = results[-1]
|
258
|
+
self.set_xcom(self.get_name(), f'{result}')
|
227
259
|
self._print_result(result)
|
228
260
|
return result
|
229
261
|
except Exception as e:
|
@@ -270,14 +302,14 @@ class BaseTask(
|
|
270
302
|
this will return True once every self.checkers is completed
|
271
303
|
- Otherwise, this will return check method's return value.
|
272
304
|
'''
|
273
|
-
if len(self.
|
305
|
+
if len(self._get_checkers()) == 0:
|
274
306
|
return await self.check()
|
275
307
|
self.log_debug('Waiting execution to be started')
|
276
308
|
while not self.__is_execution_started:
|
277
309
|
# Don't start checking before the execution itself has been started
|
278
310
|
await asyncio.sleep(0.1)
|
279
311
|
check_coroutines: Iterable[asyncio.Task] = []
|
280
|
-
for checker_task in self.
|
312
|
+
for checker_task in self._get_checkers():
|
281
313
|
checker_task._set_execution_id(self.get_execution_id())
|
282
314
|
check_coroutines.append(
|
283
315
|
asyncio.create_task(checker_task._run_all())
|
@@ -397,6 +429,7 @@ class BaseTask(
|
|
397
429
|
if self.__is_keyval_set:
|
398
430
|
return True
|
399
431
|
self.__is_keyval_set = True
|
432
|
+
# Set input_map for rendering
|
400
433
|
self.log_info('Set input map')
|
401
434
|
for task_input in self._get_combined_inputs():
|
402
435
|
input_name = to_variable_name(task_input.get_name())
|
@@ -408,6 +441,7 @@ class BaseTask(
|
|
408
441
|
'Input map:\n' + map_to_str(self.get_input_map(), item_prefix=' ')
|
409
442
|
)
|
410
443
|
self.log_info('Merging task envs, task env files, and native envs')
|
444
|
+
# Set env_map for rendering
|
411
445
|
for env_name, env in self._get_combined_env().items():
|
412
446
|
env_value = env.get(env_prefix)
|
413
447
|
if env.should_render():
|
@@ -417,6 +451,8 @@ class BaseTask(
|
|
417
451
|
self.log_debug(
|
418
452
|
'Env map:\n' + map_to_str(self.get_env_map(), item_prefix=' ')
|
419
453
|
)
|
454
|
+
# set task
|
455
|
+
self._set_task(self)
|
420
456
|
|
421
457
|
def __repr__(self) -> str:
|
422
458
|
cls_name = self.__class__.__name__
|