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 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
- logger.info(colored('Create CLI instance', attrs=['dark']))
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
- logger.info(colored('Create runner', attrs=['dark']))
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
- logger.info(colored('Runner created', attrs=['dark']))
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
- logger.debug(
39
- colored(f'Register task: "{cli_name}"', attrs=['dark'])
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
- logger.debug(
44
- colored(f'Task registered: "{cli_name}"', attrs=['dark'])
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
- title: str,
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=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-schedule',
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
- title='Schedule',
31
- default_message='{{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}}', # noqa
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)
@@ -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(title='Watch')
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
@@ -1,5 +1,6 @@
1
1
  from zrb.helper.string.conversion import to_boolean, to_logging_level
2
2
  from zrb.helper.typecheck import typechecked
3
+
3
4
  import os
4
5
  import importlib.metadata as metadata
5
6
 
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 init_scripts, should_load_builtin, version
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
- logger.info(colored('Prepare CLI', attrs=['dark']))
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
- logger.info(colored(
40
- f'Load modules from {init_script}', attrs=['dark']
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
- logger.info(colored('Load builtins', attrs=['dark']))
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
- logger.info(colored('Serve CLI', attrs=['dark']))
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
- logger.info(colored(f'Set sys.path to {sys.path}', attrs=['dark']))
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
- logger.info(colored(f'Set PYTHONPATH to {python_path}', attrs=['dark']))
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
- logger.info(colored(f'Load modules from {project_script}', attrs=['dark']))
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 current_python_path + ':' + project_dir
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
- logger.info(colored('Set PYTHONUNBUFFERED to 1', attrs=['dark']))
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
- logger.info(colored(
19
- f'Set ZRB_HOME_DIR to {zrb_home_dir}', attrs=['dark']
20
- ))
21
- os.environ['ZRB_HOME_DIR'] = zrb_home_dir
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
- logger.info(colored('Getting project directory', attrs=['dark']))
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
- logger.info(colored(
31
- f'Set ZRB_PROJECT_DIR to {zrb_project_dir}', attrs=['dark']
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
- logger.info(colored(
38
- f'Set ZRB_PROJECT_NAME to {zrb_project_name}', attrs=['dark']
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')
@@ -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
- logger.error(f'Cannot parse file: {new_dst_name}')
54
+ if logging_level <= logging.ERROR:
55
+ logger.error(f'Cannot parse file: {new_dst_name}')
@@ -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
@@ -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
- logger.info(colored(f'Get module spec: {script_path}', attrs=['dark']))
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
- logger.info(colored(f'Create module: {script_path}', attrs=['dark']))
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
- logger.info(colored(f'Exec module: {script_path}', attrs=['dark']))
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
- logger.info(colored(f'Module executed: {script_path}', attrs=['dark']))
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
- Directly call `print_result`
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
- Outputs the task result to stdout for further processing.
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
@@ -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__(self, operand: Union[AnyParallel, AnyTask]):
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._checkers) == 0:
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._checkers:
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__