zrb 1.0.0a2__py3-none-any.whl → 1.0.0a4__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.
Files changed (174) hide show
  1. zrb/__init__.py +49 -40
  2. zrb/__main__.py +5 -3
  3. zrb/attr/type.py +2 -1
  4. zrb/builtin/__init__.py +42 -2
  5. zrb/builtin/base64.py +34 -0
  6. zrb/builtin/git.py +156 -0
  7. zrb/builtin/git_subtree.py +88 -0
  8. zrb/builtin/group.py +34 -0
  9. zrb/builtin/llm/llm_chat.py +47 -0
  10. zrb/builtin/llm/tool/cli.py +9 -0
  11. zrb/builtin/llm/tool/rag.py +189 -0
  12. zrb/builtin/llm/tool/web.py +74 -0
  13. zrb/builtin/md5.py +36 -0
  14. zrb/builtin/project/add/fastapp.py +72 -0
  15. zrb/builtin/project/add/fastapp_template/.gitignore +4 -0
  16. zrb/builtin/project/add/fastapp_template/README.md +7 -0
  17. zrb/builtin/project/add/fastapp_template/_zrb/config.py +17 -0
  18. zrb/builtin/project/add/fastapp_template/_zrb/group.py +16 -0
  19. zrb/builtin/project/add/fastapp_template/_zrb/helper.py +97 -0
  20. zrb/builtin/project/add/fastapp_template/_zrb/main.py +132 -0
  21. zrb/builtin/project/add/fastapp_template/_zrb/venv_task.py +22 -0
  22. zrb/builtin/project/add/fastapp_template/common/app.py +18 -0
  23. zrb/builtin/project/add/fastapp_template/common/db_engine.py +5 -0
  24. zrb/builtin/project/add/fastapp_template/common/db_repository.py +134 -0
  25. zrb/builtin/project/add/fastapp_template/common/error.py +8 -0
  26. zrb/builtin/project/add/fastapp_template/common/schema.py +5 -0
  27. zrb/builtin/project/add/fastapp_template/common/usecase.py +232 -0
  28. zrb/builtin/project/add/fastapp_template/config.py +29 -0
  29. zrb/builtin/project/add/fastapp_template/main.py +7 -0
  30. zrb/builtin/project/add/fastapp_template/migrate.py +3 -0
  31. zrb/builtin/project/add/fastapp_template/module/__init__.py +0 -0
  32. zrb/builtin/project/add/fastapp_template/module/auth/alembic.ini +117 -0
  33. zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +7 -0
  34. zrb/builtin/project/add/fastapp_template/module/auth/client/base_client.py +27 -0
  35. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +6 -0
  36. zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +9 -0
  37. zrb/builtin/project/add/fastapp_template/module/auth/migration/README +1 -0
  38. zrb/builtin/project/add/fastapp_template/module/auth/migration/env.py +108 -0
  39. zrb/builtin/project/add/fastapp_template/module/auth/migration/script.py.mako +26 -0
  40. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +37 -0
  41. zrb/builtin/project/add/fastapp_template/module/auth/migration_metadata.py +6 -0
  42. zrb/builtin/project/add/fastapp_template/module/auth/route.py +22 -0
  43. zrb/builtin/project/add/fastapp_template/module/auth/service/__init__.py +0 -0
  44. zrb/builtin/project/add/fastapp_template/module/auth/service/user/__init__.py +0 -0
  45. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/__init__.py +0 -0
  46. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/db_repository.py +39 -0
  47. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +13 -0
  48. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/repository.py +34 -0
  49. zrb/builtin/project/add/fastapp_template/module/auth/service/user/usecase.py +45 -0
  50. zrb/builtin/project/add/fastapp_template/module/gateway/alembic.ini +117 -0
  51. zrb/builtin/project/add/fastapp_template/module/gateway/migration/README +1 -0
  52. zrb/builtin/project/add/fastapp_template/module/gateway/migration/env.py +108 -0
  53. zrb/builtin/project/add/fastapp_template/module/gateway/migration/script.py.mako +26 -0
  54. zrb/builtin/project/add/fastapp_template/module/gateway/migration/versions/.gitkeep +0 -0
  55. zrb/builtin/project/add/fastapp_template/module/gateway/migration_metadata.py +3 -0
  56. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +27 -0
  57. zrb/builtin/project/add/fastapp_template/requirements.txt +6 -0
  58. zrb/builtin/project/add/fastapp_template/schema/__init__.py +0 -0
  59. zrb/builtin/project/add/fastapp_template/schema/role.py +31 -0
  60. zrb/builtin/project/add/fastapp_template/schema/user.py +31 -0
  61. zrb/builtin/project/add/fastapp_template/template.env +2 -0
  62. zrb/builtin/project/create/__init__.py +0 -0
  63. zrb/builtin/project/create/create.py +41 -0
  64. zrb/builtin/project/create/project-template/README.md +3 -0
  65. zrb/builtin/project/create/project-template/zrb_init.py +7 -0
  66. zrb/builtin/python.py +11 -0
  67. zrb/builtin/shell/__init__.py +0 -5
  68. zrb/builtin/shell/autocomplete/__init__.py +0 -9
  69. zrb/builtin/shell/autocomplete/bash.py +5 -6
  70. zrb/builtin/shell/autocomplete/subcmd.py +7 -8
  71. zrb/builtin/shell/autocomplete/zsh.py +5 -6
  72. zrb/builtin/todo.py +219 -0
  73. zrb/callback/any_callback.py +1 -1
  74. zrb/callback/callback.py +5 -5
  75. zrb/cmd/cmd_val.py +2 -2
  76. zrb/config.py +16 -3
  77. zrb/content_transformer/any_content_transformer.py +1 -1
  78. zrb/content_transformer/content_transformer.py +2 -2
  79. zrb/context/any_context.py +1 -1
  80. zrb/context/any_shared_context.py +3 -3
  81. zrb/context/context.py +10 -8
  82. zrb/context/shared_context.py +9 -8
  83. zrb/env/__init__.py +0 -3
  84. zrb/env/any_env.py +1 -1
  85. zrb/env/env.py +3 -4
  86. zrb/env/env_file.py +4 -4
  87. zrb/env/env_map.py +2 -2
  88. zrb/group/__init__.py +0 -3
  89. zrb/group/any_group.py +3 -3
  90. zrb/group/group.py +7 -6
  91. zrb/input/any_input.py +1 -1
  92. zrb/input/base_input.py +4 -4
  93. zrb/input/bool_input.py +5 -5
  94. zrb/input/float_input.py +3 -3
  95. zrb/input/int_input.py +3 -3
  96. zrb/input/option_input.py +51 -0
  97. zrb/input/password_input.py +2 -2
  98. zrb/input/str_input.py +1 -1
  99. zrb/input/text_input.py +12 -10
  100. zrb/runner/cli.py +80 -45
  101. zrb/runner/web_app.py +150 -0
  102. zrb/runner/web_controller/__init__.py +0 -0
  103. zrb/runner/web_controller/group_info_ui/__init__.py +0 -0
  104. zrb/runner/{web_app → web_controller}/group_info_ui/controller.py +7 -8
  105. zrb/runner/{web_app → web_controller}/group_info_ui/view.html +2 -2
  106. zrb/runner/web_controller/home_page/__init__.py +0 -0
  107. zrb/runner/{web_app → web_controller}/home_page/controller.py +7 -6
  108. zrb/runner/{web_app → web_controller}/home_page/view.html +2 -2
  109. zrb/runner/web_controller/task_ui/__init__.py +0 -0
  110. zrb/runner/{web_app → web_controller}/task_ui/controller.py +8 -12
  111. zrb/runner/{web_app → web_controller}/task_ui/view.html +2 -2
  112. zrb/runner/web_util.py +5 -35
  113. zrb/session/any_session.py +13 -7
  114. zrb/session/session.py +78 -40
  115. zrb/session_state_log/session_state_log.py +7 -5
  116. zrb/session_state_logger/any_session_state_logger.py +1 -1
  117. zrb/session_state_logger/default_session_state_logger.py +2 -2
  118. zrb/session_state_logger/file_session_state_logger.py +19 -27
  119. zrb/task/any_task.py +4 -4
  120. zrb/task/base_task.py +33 -23
  121. zrb/task/base_trigger.py +11 -12
  122. zrb/task/cmd_task.py +72 -65
  123. zrb/task/http_check.py +13 -13
  124. zrb/task/llm_task.py +215 -0
  125. zrb/task/make_task.py +9 -9
  126. zrb/task/rsync_task.py +25 -25
  127. zrb/task/scaffolder.py +18 -15
  128. zrb/task/scheduler.py +6 -7
  129. zrb/task/task.py +1 -1
  130. zrb/task/tcp_check.py +11 -13
  131. zrb/util/attr.py +19 -3
  132. zrb/util/cli/style.py +71 -2
  133. zrb/util/cli/subcommand.py +2 -2
  134. zrb/util/codemod/__init__.py +0 -0
  135. zrb/util/codemod/add_code_to_class.py +35 -0
  136. zrb/util/codemod/add_code_to_function.py +36 -0
  137. zrb/util/codemod/add_code_to_method.py +55 -0
  138. zrb/util/codemod/add_key_to_dict.py +51 -0
  139. zrb/util/codemod/add_param_to_function_call.py +39 -0
  140. zrb/util/codemod/add_property_to_class.py +55 -0
  141. zrb/util/git.py +156 -0
  142. zrb/util/git_subtree.py +94 -0
  143. zrb/util/group.py +2 -2
  144. zrb/util/llm/tool.py +63 -0
  145. zrb/util/string/conversion.py +7 -0
  146. zrb/util/todo.py +259 -0
  147. {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/METADATA +13 -5
  148. zrb-1.0.0a4.dist-info/RECORD +197 -0
  149. zrb/builtin/shell/_group.py +0 -9
  150. zrb/builtin/shell/autocomplete/_group.py +0 -6
  151. zrb/runner/web_app/any_request_handler.py +0 -24
  152. zrb/runner/web_server.py +0 -224
  153. zrb-1.0.0a2.dist-info/RECORD +0 -120
  154. /zrb/{runner/web_app → builtin/project}/__init__.py +0 -0
  155. /zrb/{runner/web_app/group_info_ui → builtin/project/add}/__init__.py +0 -0
  156. /zrb/{runner/web_app/home_page → builtin/project/add/fastapp_template}/__init__.py +0 -0
  157. /zrb/{runner/web_app/task_ui → builtin/project/add/fastapp_template/common}/__init__.py +0 -0
  158. /zrb/runner/{web_app → web_controller}/group_info_ui/partial/group_info.html +0 -0
  159. /zrb/runner/{web_app → web_controller}/group_info_ui/partial/group_li.html +0 -0
  160. /zrb/runner/{web_app → web_controller}/group_info_ui/partial/task_info.html +0 -0
  161. /zrb/runner/{web_app → web_controller}/group_info_ui/partial/task_li.html +0 -0
  162. /zrb/runner/{web_app → web_controller}/home_page/partial/group_info.html +0 -0
  163. /zrb/runner/{web_app → web_controller}/home_page/partial/group_li.html +0 -0
  164. /zrb/runner/{web_app → web_controller}/home_page/partial/task_info.html +0 -0
  165. /zrb/runner/{web_app → web_controller}/home_page/partial/task_li.html +0 -0
  166. /zrb/runner/{web_app → web_controller}/static/favicon-32x32.png +0 -0
  167. /zrb/runner/{web_app → web_controller}/static/pico.min.css +0 -0
  168. /zrb/runner/{web_app → web_controller}/task_ui/partial/common-util.js +0 -0
  169. /zrb/runner/{web_app → web_controller}/task_ui/partial/input.html +0 -0
  170. /zrb/runner/{web_app → web_controller}/task_ui/partial/main.js +0 -0
  171. /zrb/runner/{web_app → web_controller}/task_ui/partial/show-existing-session.js +0 -0
  172. /zrb/runner/{web_app → web_controller}/task_ui/partial/visualize-history.js +0 -0
  173. {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/WHEEL +0 -0
  174. {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/pico.min.css">
8
- <link rel="icon" href="/favicon-32x32.png" sizes="32x32" type="image/png">
7
+ <link rel="stylesheet" href="/static/pico.min.css">
8
+ <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
9
  <title>Zrb</title>
10
10
  </head>
11
11
  <body>
File without changes
@@ -1,10 +1,9 @@
1
1
  import os
2
2
 
3
- from ....group.any_group import AnyGroup
4
- from ....session.any_session import AnySession
5
- from ....task.any_task import AnyTask
6
- from ....util.string.format import fstring_format
7
- from ..any_request_handler import AnyRequestHandler
3
+ from zrb.group.any_group import AnyGroup
4
+ from zrb.session.any_session import AnySession
5
+ from zrb.task.any_task import AnyTask
6
+ from zrb.util.string.format import fstring_format
8
7
 
9
8
  _DIR = os.path.dirname(__file__)
10
9
 
@@ -28,13 +27,10 @@ with open(os.path.join(_DIR, "partial", "common-util.js")) as f:
28
27
 
29
28
 
30
29
  def handle_task_ui(
31
- handler: AnyRequestHandler,
32
- root_group: AnyGroup,
33
- task: AnyTask,
34
- session: AnySession,
35
- url: str,
36
- args: list[str],
30
+ root_group: AnyGroup, task: AnyTask, session: AnySession, url: str, args: list[str]
37
31
  ):
32
+ from fastapi.responses import HTMLResponse
33
+
38
34
  session.register_task(task)
39
35
  ctx = task.get_ctx(session)
40
36
  url_parts = url.split("/")
@@ -56,7 +52,7 @@ def handle_task_ui(
56
52
  ]
57
53
  )
58
54
  session_name = args[0] if len(args) > 0 else ""
59
- handler.send_html_response(
55
+ return HTMLResponse(
60
56
  fstring_format(
61
57
  _VIEW_TEMPLATE,
62
58
  {
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/pico.min.css">
8
- <link rel="icon" href="/favicon-32x32.png" sizes="32x32" type="image/png">
7
+ <link rel="stylesheet" href="/static/pico.min.css">
8
+ <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
9
  <title>Zrb</title>
10
10
  </head>
11
11
  <body>
zrb/runner/web_util.py CHANGED
@@ -1,4 +1,8 @@
1
- import asyncio
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class NewSessionResponse(BaseModel):
5
+ session_name: str
2
6
 
3
7
 
4
8
  def url_to_args(url: str) -> list[str]:
@@ -10,37 +14,3 @@ def node_path_to_url(args: list[str]) -> str:
10
14
  pruned_args = [part for part in args if part.strip() != ""]
11
15
  stripped_url = "/".join(pruned_args)
12
16
  return f"/{stripped_url}/"
13
-
14
-
15
- class SessionSnapshotCondition:
16
- def __init__(self):
17
- self._should_stop = False
18
-
19
- @property
20
- def should_stop(self) -> bool:
21
- return self._should_stop
22
-
23
- def stop(self):
24
- self._should_stop = True
25
-
26
-
27
- def start_event_loop(event_loop: asyncio.AbstractEventLoop):
28
- asyncio.set_event_loop(event_loop)
29
- event_loop.run_forever()
30
-
31
-
32
- async def shutdown_event_loop(event_loop: asyncio.AbstractEventLoop):
33
- tasks = [
34
- t
35
- for t in asyncio.all_tasks(event_loop)
36
- if not t.done() and t is not asyncio.current_task(event_loop)
37
- ]
38
- print("TASKS", tasks)
39
- for task in tasks:
40
- print("CANCEL TASK", task)
41
- task.cancel()
42
- print("CANCELED", task)
43
- await asyncio.gather(*tasks, return_exceptions=True)
44
- print("DONE")
45
- event_loop.stop()
46
- print("LOOP STOP")
@@ -3,15 +3,15 @@ from __future__ import annotations # Enables forward references
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import TYPE_CHECKING, Any, Coroutine, TypeVar
5
5
 
6
- from ..context.any_context import AnyContext
7
- from ..group.any_group import AnyGroup
8
- from ..session_state_log.session_state_log import SessionStateLog
9
- from ..session_state_logger.any_session_state_logger import AnySessionStateLogger
10
- from ..task_status.task_status import TaskStatus
6
+ from zrb.context.any_context import AnyContext
7
+ from zrb.group.any_group import AnyGroup
8
+ from zrb.session_state_log.session_state_log import SessionStateLog
9
+ from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
10
+ from zrb.task_status.task_status import TaskStatus
11
11
 
12
12
  if TYPE_CHECKING:
13
- from ..context import any_shared_context
14
- from ..task import any_task
13
+ from zrb.context import any_shared_context
14
+ from zrb.task import any_task
15
15
 
16
16
  TAnySession = TypeVar("TAnySession", bound="AnySession")
17
17
 
@@ -36,6 +36,12 @@ class AnySession(ABC):
36
36
  """Session root group"""
37
37
  pass
38
38
 
39
+ @property
40
+ @abstractmethod
41
+ def task_names(self) -> list[str]:
42
+ """Task names in this session"""
43
+ pass
44
+
39
45
  @property
40
46
  @abstractmethod
41
47
  def shared_ctx(self) -> any_shared_context.AnySharedContext:
zrb/session/session.py CHANGED
@@ -1,21 +1,37 @@
1
1
  import asyncio
2
2
  from typing import Any, Coroutine
3
3
 
4
- from ..context.any_shared_context import AnySharedContext
5
- from ..context.context import AnyContext, Context
6
- from ..group.any_group import AnyGroup
7
- from ..session_state_log.session_state_log import SessionStateLog, TaskStatusStateLog
8
- from ..session_state_logger.any_session_state_logger import AnySessionStateLogger
9
- from ..session_state_logger.default_session_state_logger import (
4
+ from zrb.context.any_shared_context import AnySharedContext
5
+ from zrb.context.context import AnyContext, Context
6
+ from zrb.group.any_group import AnyGroup
7
+ from zrb.session.any_session import AnySession
8
+ from zrb.session_state_log.session_state_log import (
9
+ SessionStateLog,
10
+ TaskStatusHistoryStateLog,
11
+ TaskStatusStateLog,
12
+ )
13
+ from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
14
+ from zrb.session_state_logger.default_session_state_logger import (
10
15
  default_session_state_logger,
11
16
  )
12
- from ..task.any_task import AnyTask
13
- from ..task_status.task_status import TaskStatus
14
- from ..util.cli.style import BLUE, CYAN, GREEN, ICONS, MAGENTA, YELLOW
15
- from ..util.group import get_node_path
16
- from ..util.string.name import get_random_name
17
- from ..xcom.xcom import Xcom
18
- from .any_session import AnySession
17
+ from zrb.task.any_task import AnyTask
18
+ from zrb.task_status.task_status import TaskStatus
19
+ from zrb.util.cli.style import (
20
+ BLUE,
21
+ BRIGHT_BLUE,
22
+ BRIGHT_CYAN,
23
+ BRIGHT_GREEN,
24
+ BRIGHT_MAGENTA,
25
+ BRIGHT_YELLOW,
26
+ CYAN,
27
+ GREEN,
28
+ ICONS,
29
+ MAGENTA,
30
+ YELLOW,
31
+ )
32
+ from zrb.util.group import get_node_path
33
+ from zrb.util.string.name import get_random_name
34
+ from zrb.xcom.xcom import Xcom
19
35
 
20
36
 
21
37
  class Session(AnySession):
@@ -39,7 +55,18 @@ class Session(AnySession):
39
55
  self._action_coros: dict[AnyTask, Coroutine] = {}
40
56
  self._monitoring_coros: dict[AnyTask, Coroutine] = {}
41
57
  self._coros: list[Coroutine] = []
42
- self._colors = [GREEN, YELLOW, BLUE, MAGENTA, CYAN]
58
+ self._colors = [
59
+ GREEN,
60
+ YELLOW,
61
+ BLUE,
62
+ MAGENTA,
63
+ CYAN,
64
+ BRIGHT_GREEN,
65
+ BRIGHT_YELLOW,
66
+ BRIGHT_BLUE,
67
+ BRIGHT_MAGENTA,
68
+ BRIGHT_CYAN,
69
+ ]
43
70
  self._icons = ICONS
44
71
  self._color_index = 0
45
72
  self._icon_index = 0
@@ -62,6 +89,10 @@ class Session(AnySession):
62
89
  def root_group(self) -> AnyGroup | None:
63
90
  return self._root_group
64
91
 
92
+ @property
93
+ def task_names(self) -> list[str]:
94
+ return [task.name for task in self._task_status.keys()]
95
+
65
96
  @property
66
97
  def shared_ctx(self) -> AnySharedContext:
67
98
  return self._shared_ctx
@@ -105,35 +136,42 @@ class Session(AnySession):
105
136
 
106
137
  def as_state_log(self) -> SessionStateLog:
107
138
  task_status_log: dict[str, TaskStatusStateLog] = {}
139
+ log_start_time = ""
108
140
  for task, task_status in self._task_status.items():
109
- task_status_log[task.name] = {
110
- "is_started": task_status.is_started,
111
- "is_ready": task_status.is_ready,
112
- "is_completed": task_status.is_completed,
113
- "is_skipped": task_status.is_skipped,
114
- "is_failed": task_status.is_failed,
115
- "is_permanently_failed": task_status.is_permanently_failed,
116
- "is_terminated": task_status.is_terminated,
117
- "history": [
118
- {
119
- "status": status,
120
- "time": status_at.strftime("%Y-%m-%d %H:%M:%S.%f"),
121
- }
122
- for status, status_at in task_status.history
123
- ],
124
- }
125
- return {
126
- "name": self.name,
127
- "main_task_name": self._main_task.name,
128
- "path": self.task_path,
129
- "final_result": (
141
+ history_log = [
142
+ TaskStatusHistoryStateLog(
143
+ status=status,
144
+ time=status_at.strftime("%Y-%m-%d %H:%M:%S.%f"),
145
+ )
146
+ for status, status_at in task_status.history
147
+ ]
148
+ if len(history_log) > 0 and (
149
+ log_start_time == "" or history_log[0].time < log_start_time
150
+ ):
151
+ log_start_time = history_log[0].time
152
+ task_status_log[task.name] = TaskStatusStateLog(
153
+ is_started=task_status.is_started,
154
+ is_ready=task_status.is_ready,
155
+ is_completed=task_status.is_completed,
156
+ is_skipped=task_status.is_skipped,
157
+ is_failed=task_status.is_failed,
158
+ is_permanently_failed=task_status.is_permanently_failed,
159
+ is_terminated=task_status.is_terminated,
160
+ history=history_log,
161
+ )
162
+ return SessionStateLog(
163
+ name=self.name,
164
+ start_time=log_start_time,
165
+ main_task_name=self._main_task.name,
166
+ path=self.task_path,
167
+ final_result=(
130
168
  f"{self.final_result}" if self.final_result is not None else ""
131
169
  ),
132
- "finished": self.is_terminated,
133
- "log": self.shared_ctx.shared_log,
134
- "input": self.shared_ctx.input,
135
- "task_status": task_status_log,
136
- }
170
+ finished=self.is_terminated,
171
+ log=self.shared_ctx.shared_log,
172
+ input=self.shared_ctx.input,
173
+ task_status=task_status_log,
174
+ )
137
175
 
138
176
  def get_ctx(self, task: AnyTask) -> AnyContext:
139
177
  self._register_single_task(task)
@@ -1,12 +1,14 @@
1
- from typing import Any, TypedDict
1
+ from typing import Any
2
2
 
3
+ from pydantic import BaseModel
3
4
 
4
- class TaskStatusHistoryStateLog(TypedDict):
5
+
6
+ class TaskStatusHistoryStateLog(BaseModel):
5
7
  status: str
6
8
  time: str
7
9
 
8
10
 
9
- class TaskStatusStateLog(TypedDict):
11
+ class TaskStatusStateLog(BaseModel):
10
12
  history: list[TaskStatusHistoryStateLog]
11
13
  is_started: bool
12
14
  is_ready: bool
@@ -17,7 +19,7 @@ class TaskStatusStateLog(TypedDict):
17
19
  is_terminated: bool
18
20
 
19
21
 
20
- class SessionStateLog(TypedDict):
22
+ class SessionStateLog(BaseModel):
21
23
  name: str
22
24
  start_time: str
23
25
  main_task_name: str
@@ -29,6 +31,6 @@ class SessionStateLog(TypedDict):
29
31
  task_status: dict[str, TaskStatusStateLog]
30
32
 
31
33
 
32
- class SessionStateLogList(TypedDict):
34
+ class SessionStateLogList(BaseModel):
33
35
  total: int
34
36
  data: list[SessionStateLog]
@@ -1,7 +1,7 @@
1
1
  import datetime
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- from ..session_state_log.session_state_log import SessionStateLog, SessionStateLogList
4
+ from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
5
5
 
6
6
 
7
7
  class AnySessionStateLogger(ABC):
@@ -1,4 +1,4 @@
1
- from ..config import SESSION_LOG_DIR
2
- from .file_session_state_logger import FileSessionStateLogger
1
+ from zrb.config import SESSION_LOG_DIR
2
+ from zrb.session_state_logger.file_session_state_logger import FileSessionStateLogger
3
3
 
4
4
  default_session_state_logger = FileSessionStateLogger(SESSION_LOG_DIR)
@@ -1,9 +1,8 @@
1
1
  import datetime
2
- import json
3
2
  import os
4
3
 
5
- from ..session_state_log.session_state_log import SessionStateLog, SessionStateLogList
6
- from .any_session_state_logger import AnySessionStateLogger
4
+ from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
5
+ from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
7
6
 
8
7
 
9
8
  class FileSessionStateLogger(AnySessionStateLogger):
@@ -12,21 +11,24 @@ class FileSessionStateLogger(AnySessionStateLogger):
12
11
  self._session_log_dir = session_log_dir
13
12
 
14
13
  def write(self, session_log: SessionStateLog):
15
- session_file_path = self._get_session_file_path(session_log["name"])
14
+ session_file_path = self._get_session_file_path(session_log.name)
15
+ session_dir_path = os.path.dirname(session_file_path)
16
+ if not os.path.isdir(session_dir_path):
17
+ os.makedirs(session_dir_path, exist_ok=True)
16
18
  with open(session_file_path, "w") as f:
17
- f.write(json.dumps(session_log))
18
- start_time = self._get_start_time(session_log)
19
- if start_time is None:
19
+ f.write(session_log.model_dump_json())
20
+ start_time = session_log.start_time
21
+ if start_time == "":
20
22
  return
21
- timeline_dir_path = self._get_timeline_dir_path(session_log, start_time)
23
+ timeline_dir_path = self._get_timeline_dir_path(session_log)
22
24
  os.makedirs(timeline_dir_path, exist_ok=True)
23
- with open(os.path.join(timeline_dir_path, session_log["name"]), "w"):
25
+ with open(os.path.join(timeline_dir_path, session_log.name), "w"):
24
26
  pass
25
27
 
26
28
  def read(self, session_name: str) -> SessionStateLog:
27
29
  session_file_path = self._get_session_file_path(session_name)
28
30
  with open(session_file_path, "r") as f:
29
- return json.loads(f.read())
31
+ return SessionStateLog.model_validate_json(f.read())
30
32
 
31
33
  def list(
32
34
  self,
@@ -59,21 +61,20 @@ class FileSessionStateLogger(AnySessionStateLogger):
59
61
  paginated_sessions = matching_sessions[start_index:end_index]
60
62
  # Extract session logs from the sorted list of tuples
61
63
  data = [session_log for _, session_log in paginated_sessions]
62
- return {"total": total, "data": data}
64
+ return SessionStateLogList(total=total, data=data)
63
65
 
64
66
  def _get_session_file_path(self, session_name: str) -> str:
65
67
  return os.path.join(self._session_log_dir, f"{session_name}.json")
66
68
 
67
- def _get_timeline_dir_path(
68
- self, session_log: SessionStateLog, start_time: datetime.datetime
69
- ) -> str:
69
+ def _get_timeline_dir_path(self, session_log: SessionStateLog) -> str:
70
+ start_time = self._get_start_time(session_log)
70
71
  year = start_time.year
71
72
  month = start_time.month
72
73
  day = start_time.day
73
74
  hour = start_time.hour
74
75
  minute = start_time.minute
75
76
  second = start_time.second
76
- paths = session_log["path"] + [
77
+ paths = session_log.path + [
77
78
  f"{year}",
78
79
  f"{month}",
79
80
  f"{day}",
@@ -84,15 +85,6 @@ class FileSessionStateLogger(AnySessionStateLogger):
84
85
  return os.path.join(self._session_log_dir, "_timeline", *paths)
85
86
 
86
87
  def _get_start_time(self, session_log: SessionStateLog) -> datetime.datetime:
87
- result: datetime.datetime | None = None
88
- for task_status in session_log["task_status"].values():
89
- histories = task_status["history"]
90
- if len(histories) == 0:
91
- continue
92
- first_history = histories[0]
93
- first_time = datetime.datetime.strptime(
94
- first_history["time"], "%Y-%m-%d %H:%M:%S.%f"
95
- )
96
- if result is None or first_time < result:
97
- result = first_time
98
- return result
88
+ return datetime.datetime.strptime(
89
+ session_log.start_time, "%Y-%m-%d %H:%M:%S.%f"
90
+ )
zrb/task/any_task.py CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations # Enables forward references
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
- from ..env.any_env import AnyEnv
7
- from ..input.any_input import AnyInput
6
+ from zrb.env.any_env import AnyEnv
7
+ from zrb.input.any_input import AnyInput
8
8
 
9
9
  if TYPE_CHECKING:
10
- from ..context import any_context
11
- from ..session import session
10
+ from zrb.context import any_context
11
+ from zrb.session import session
12
12
 
13
13
 
14
14
  class AnyTask(ABC):
zrb/task/base_task.py CHANGED
@@ -3,17 +3,17 @@ import os
3
3
  from collections.abc import Callable
4
4
  from typing import Any
5
5
 
6
- from ..attr.type import BoolAttr, fstring
7
- from ..context.any_context import AnyContext
8
- from ..context.shared_context import AnySharedContext, SharedContext
9
- from ..env.any_env import AnyEnv
10
- from ..input.any_input import AnyInput
11
- from ..session.any_session import AnySession
12
- from ..session.session import Session
13
- from ..util.attr import get_bool_attr
14
- from ..util.run import run_async
15
- from ..xcom.xcom import Xcom
16
- from .any_task import AnyTask
6
+ from zrb.attr.type import BoolAttr, fstring
7
+ from zrb.context.any_context import AnyContext
8
+ from zrb.context.shared_context import AnySharedContext, SharedContext
9
+ from zrb.env.any_env import AnyEnv
10
+ from zrb.input.any_input import AnyInput
11
+ from zrb.session.any_session import AnySession
12
+ from zrb.session.session import Session
13
+ from zrb.task.any_task import AnyTask
14
+ from zrb.util.attr import get_bool_attr
15
+ from zrb.util.run import run_async
16
+ from zrb.xcom.xcom import Xcom
17
17
 
18
18
 
19
19
  class BaseTask(AnyTask):
@@ -24,8 +24,8 @@ class BaseTask(AnyTask):
24
24
  icon: str | None = None,
25
25
  description: str | None = None,
26
26
  cli_only: bool = False,
27
- input: list[AnyInput] | AnyInput | None = None,
28
- env: list[AnyEnv] | AnyEnv | None = None,
27
+ input: list[AnyInput | None] | AnyInput | None = None,
28
+ env: list[AnyEnv | None] | AnyEnv | None = None,
29
29
  action: fstring | Callable[[AnyContext], Any] | None = None,
30
30
  execute_condition: BoolAttr = True,
31
31
  retries: int = 2,
@@ -63,16 +63,22 @@ class BaseTask(AnyTask):
63
63
  return f"<{self.__class__.__name__} name={self._name}>"
64
64
 
65
65
  def __rshift__(self, other: AnyTask | list[AnyTask]) -> AnyTask:
66
- if isinstance(other, AnyTask):
67
- other.append_upstreams(self)
68
- elif isinstance(other, list):
69
- for task in other:
70
- task.append_upstreams(self)
71
- return other
66
+ try:
67
+ if isinstance(other, AnyTask):
68
+ other.append_upstreams(self)
69
+ elif isinstance(other, list):
70
+ for task in other:
71
+ task.append_upstreams(self)
72
+ return other
73
+ except Exception as e:
74
+ raise ValueError(f"Invalid operation {self} >> {other}: {e}")
72
75
 
73
76
  def __lshift__(self, other: AnyTask | list[AnyTask]) -> AnyTask:
74
- self.append_upstreams(other)
75
- return self
77
+ try:
78
+ self.append_upstreams(other)
79
+ return self
80
+ except Exception as e:
81
+ raise ValueError(f"Invalid operation {self} << {other}: {e}")
76
82
 
77
83
  @property
78
84
  def name(self) -> str:
@@ -103,7 +109,7 @@ class BaseTask(AnyTask):
103
109
  envs.append(self._envs)
104
110
  elif self._envs is not None:
105
111
  envs += self._envs
106
- return envs
112
+ return [env for env in envs if env is not None]
107
113
 
108
114
  @property
109
115
  def inputs(self) -> list[AnyInput]:
@@ -112,7 +118,7 @@ class BaseTask(AnyTask):
112
118
  self.__combine_inputs(inputs, upstream.inputs)
113
119
  if self._inputs is not None:
114
120
  self.__combine_inputs(inputs, self._inputs)
115
- return inputs
121
+ return [task_input for task_input in inputs if task_input is not None]
116
122
 
117
123
  def __combine_inputs(
118
124
  self, inputs: list[AnyInput], other_inputs: list[AnyInput] | AnyInput
@@ -120,7 +126,11 @@ class BaseTask(AnyTask):
120
126
  input_names = [task_input.name for task_input in inputs]
121
127
  if isinstance(other_inputs, AnyInput):
122
128
  other_inputs = [other_inputs]
129
+ elif other_inputs is None:
130
+ other_inputs = []
123
131
  for task_input in other_inputs:
132
+ if task_input is None:
133
+ continue
124
134
  if task_input.name not in input_names:
125
135
  inputs.append(task_input)
126
136
 
zrb/task/base_trigger.py CHANGED
@@ -2,21 +2,20 @@ import asyncio
2
2
  from collections.abc import Callable
3
3
  from typing import Any
4
4
 
5
+ from zrb.attr.type import fstring
6
+ from zrb.callback.any_callback import AnyCallback
5
7
  from zrb.context.any_context import AnyContext
6
8
  from zrb.context.any_shared_context import AnySharedContext
9
+ from zrb.context.shared_context import SharedContext
10
+ from zrb.dot_dict.dot_dict import DotDict
7
11
  from zrb.env.any_env import AnyEnv
8
12
  from zrb.input.any_input import AnyInput
13
+ from zrb.session.any_session import AnySession
14
+ from zrb.session.session import Session
9
15
  from zrb.task.any_task import AnyTask
10
-
11
- from ..attr.type import fstring
12
- from ..callback.any_callback import AnyCallback
13
- from ..context.shared_context import SharedContext
14
- from ..dot_dict.dot_dict import DotDict
15
- from ..session.any_session import AnySession
16
- from ..session.session import Session
17
- from ..util.cli.style import CYAN
18
- from ..xcom.xcom import Xcom
19
- from .base_task import BaseTask
16
+ from zrb.task.base_task import BaseTask
17
+ from zrb.util.cli.style import CYAN
18
+ from zrb.xcom.xcom import Xcom
20
19
 
21
20
 
22
21
  class BaseTrigger(BaseTask):
@@ -28,8 +27,8 @@ class BaseTrigger(BaseTask):
28
27
  icon: str | None = None,
29
28
  description: str | None = None,
30
29
  cli_only: bool = False,
31
- input: list[AnyInput] | AnyInput | None = None,
32
- env: list[AnyEnv] | AnyEnv | None = None,
30
+ input: list[AnyInput | None] | AnyInput | None = None,
31
+ env: list[AnyEnv | None] | AnyEnv | None = None,
33
32
  action: fstring | Callable[[AnyContext], Any] | None = None,
34
33
  execute_condition: bool | str | Callable[[AnySharedContext], bool] = True,
35
34
  queue_name: fstring | None = None,