zrb 1.0.0a1__py3-none-any.whl → 1.0.0a3__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 (156) hide show
  1. zrb/__init__.py +48 -39
  2. zrb/__main__.py +3 -3
  3. zrb/attr/type.py +2 -1
  4. zrb/builtin/__init__.py +40 -2
  5. zrb/builtin/base64.py +32 -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.py +31 -0
  10. zrb/builtin/md5.py +34 -0
  11. zrb/builtin/project/__init__.py +0 -0
  12. zrb/builtin/project/add/__init__.py +0 -0
  13. zrb/builtin/project/add/fastapp.py +72 -0
  14. zrb/builtin/project/add/fastapp_template/.gitignore +4 -0
  15. zrb/builtin/project/add/fastapp_template/README.md +7 -0
  16. zrb/builtin/project/add/fastapp_template/__init__.py +0 -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/__init__.py +0 -0
  23. zrb/builtin/project/add/fastapp_template/common/app.py +18 -0
  24. zrb/builtin/project/add/fastapp_template/common/db_engine.py +5 -0
  25. zrb/builtin/project/add/fastapp_template/common/db_repository.py +134 -0
  26. zrb/builtin/project/add/fastapp_template/common/error.py +8 -0
  27. zrb/builtin/project/add/fastapp_template/common/schema.py +5 -0
  28. zrb/builtin/project/add/fastapp_template/common/usecase.py +232 -0
  29. zrb/builtin/project/add/fastapp_template/config.py +29 -0
  30. zrb/builtin/project/add/fastapp_template/main.py +7 -0
  31. zrb/builtin/project/add/fastapp_template/migrate.py +3 -0
  32. zrb/builtin/project/add/fastapp_template/module/__init__.py +0 -0
  33. zrb/builtin/project/add/fastapp_template/module/auth/alembic.ini +117 -0
  34. zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +7 -0
  35. zrb/builtin/project/add/fastapp_template/module/auth/client/base_client.py +27 -0
  36. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +6 -0
  37. zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +9 -0
  38. zrb/builtin/project/add/fastapp_template/module/auth/migration/README +1 -0
  39. zrb/builtin/project/add/fastapp_template/module/auth/migration/env.py +108 -0
  40. zrb/builtin/project/add/fastapp_template/module/auth/migration/script.py.mako +26 -0
  41. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +37 -0
  42. zrb/builtin/project/add/fastapp_template/module/auth/migration_metadata.py +6 -0
  43. zrb/builtin/project/add/fastapp_template/module/auth/route.py +22 -0
  44. zrb/builtin/project/add/fastapp_template/module/auth/service/__init__.py +0 -0
  45. zrb/builtin/project/add/fastapp_template/module/auth/service/user/__init__.py +0 -0
  46. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/__init__.py +0 -0
  47. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/db_repository.py +39 -0
  48. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +13 -0
  49. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/repository.py +34 -0
  50. zrb/builtin/project/add/fastapp_template/module/auth/service/user/usecase.py +45 -0
  51. zrb/builtin/project/add/fastapp_template/module/gateway/alembic.ini +117 -0
  52. zrb/builtin/project/add/fastapp_template/module/gateway/migration/README +1 -0
  53. zrb/builtin/project/add/fastapp_template/module/gateway/migration/env.py +108 -0
  54. zrb/builtin/project/add/fastapp_template/module/gateway/migration/script.py.mako +26 -0
  55. zrb/builtin/project/add/fastapp_template/module/gateway/migration/versions/.gitkeep +0 -0
  56. zrb/builtin/project/add/fastapp_template/module/gateway/migration_metadata.py +3 -0
  57. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +27 -0
  58. zrb/builtin/project/add/fastapp_template/requirements.txt +6 -0
  59. zrb/builtin/project/add/fastapp_template/schema/__init__.py +0 -0
  60. zrb/builtin/project/add/fastapp_template/schema/role.py +31 -0
  61. zrb/builtin/project/add/fastapp_template/schema/user.py +31 -0
  62. zrb/builtin/project/add/fastapp_template/template.env +2 -0
  63. zrb/builtin/project/create/__init__.py +0 -0
  64. zrb/builtin/project/create/create.py +41 -0
  65. zrb/builtin/project/create/project-template/README.md +3 -0
  66. zrb/builtin/project/create/project-template/zrb_init.py +7 -0
  67. zrb/builtin/python.py +11 -0
  68. zrb/builtin/shell/__init__.py +0 -5
  69. zrb/builtin/shell/autocomplete/__init__.py +0 -9
  70. zrb/builtin/shell/autocomplete/bash.py +5 -6
  71. zrb/builtin/shell/autocomplete/subcmd.py +7 -8
  72. zrb/builtin/shell/autocomplete/zsh.py +5 -6
  73. zrb/builtin/todo.py +186 -0
  74. zrb/callback/any_callback.py +1 -1
  75. zrb/callback/callback.py +5 -5
  76. zrb/cmd/cmd_val.py +2 -2
  77. zrb/config.py +4 -1
  78. zrb/content_transformer/any_content_transformer.py +1 -1
  79. zrb/content_transformer/content_transformer.py +2 -2
  80. zrb/context/any_context.py +5 -1
  81. zrb/context/any_shared_context.py +3 -3
  82. zrb/context/context.py +15 -9
  83. zrb/context/shared_context.py +9 -8
  84. zrb/env/__init__.py +0 -3
  85. zrb/env/any_env.py +2 -2
  86. zrb/env/env.py +4 -5
  87. zrb/env/env_file.py +4 -4
  88. zrb/env/env_map.py +4 -4
  89. zrb/group/__init__.py +0 -3
  90. zrb/group/any_group.py +3 -3
  91. zrb/group/group.py +7 -6
  92. zrb/input/any_input.py +1 -1
  93. zrb/input/base_input.py +4 -4
  94. zrb/input/bool_input.py +5 -5
  95. zrb/input/float_input.py +3 -3
  96. zrb/input/int_input.py +3 -3
  97. zrb/input/option_input.py +51 -0
  98. zrb/input/password_input.py +2 -2
  99. zrb/input/str_input.py +1 -1
  100. zrb/input/text_input.py +12 -10
  101. zrb/runner/cli.py +79 -44
  102. zrb/runner/web_app/group_info_ui/controller.py +7 -8
  103. zrb/runner/web_app/group_info_ui/view.html +2 -2
  104. zrb/runner/web_app/home_page/controller.py +7 -6
  105. zrb/runner/web_app/home_page/view.html +2 -2
  106. zrb/runner/web_app/task_ui/controller.py +13 -13
  107. zrb/runner/web_app/task_ui/partial/common-util.js +37 -0
  108. zrb/runner/web_app/task_ui/partial/main.js +9 -2
  109. zrb/runner/web_app/task_ui/partial/show-existing-session.js +20 -5
  110. zrb/runner/web_app/task_ui/partial/visualize-history.js +1 -41
  111. zrb/runner/web_app/task_ui/view.html +4 -2
  112. zrb/runner/web_server.py +137 -211
  113. zrb/runner/web_util.py +5 -35
  114. zrb/session/any_session.py +13 -7
  115. zrb/session/session.py +80 -41
  116. zrb/session_state_log/session_state_log.py +7 -5
  117. zrb/session_state_logger/any_session_state_logger.py +1 -1
  118. zrb/session_state_logger/default_session_state_logger.py +2 -2
  119. zrb/session_state_logger/file_session_state_logger.py +19 -27
  120. zrb/task/any_task.py +8 -3
  121. zrb/task/base_task.py +47 -33
  122. zrb/task/base_trigger.py +11 -12
  123. zrb/task/cmd_task.py +55 -43
  124. zrb/task/http_check.py +8 -8
  125. zrb/task/llm_task.py +160 -0
  126. zrb/task/make_task.py +9 -9
  127. zrb/task/rsync_task.py +7 -7
  128. zrb/task/scaffolder.py +14 -11
  129. zrb/task/scheduler.py +6 -7
  130. zrb/task/task.py +1 -1
  131. zrb/task/tcp_check.py +8 -8
  132. zrb/util/attr.py +19 -3
  133. zrb/util/cli/style.py +71 -2
  134. zrb/util/cli/subcommand.py +2 -2
  135. zrb/util/codemod/__init__.py +0 -0
  136. zrb/util/codemod/add_code_to_class.py +35 -0
  137. zrb/util/codemod/add_code_to_function.py +36 -0
  138. zrb/util/codemod/add_code_to_method.py +55 -0
  139. zrb/util/codemod/add_key_to_dict.py +51 -0
  140. zrb/util/codemod/add_param_to_function_call.py +39 -0
  141. zrb/util/codemod/add_property_to_class.py +55 -0
  142. zrb/util/git.py +156 -0
  143. zrb/util/git_subtree.py +94 -0
  144. zrb/util/group.py +2 -2
  145. zrb/util/llm/tool.py +63 -0
  146. zrb/util/string/conversion.py +7 -0
  147. zrb/util/todo.py +135 -0
  148. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/METADATA +11 -7
  149. zrb-1.0.0a3.dist-info/RECORD +194 -0
  150. zrb/builtin/shell/_group.py +0 -9
  151. zrb/builtin/shell/autocomplete/_group.py +0 -6
  152. zrb/runner/web_app/any_request_handler.py +0 -24
  153. zrb/runner/web_server.bak.py +0 -208
  154. zrb-1.0.0a1.dist-info/RECORD +0 -120
  155. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/WHEEL +0 -0
  156. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/entry_points.txt +0 -0
zrb/builtin/todo.py ADDED
@@ -0,0 +1,186 @@
1
+ import datetime
2
+ import os
3
+
4
+ from zrb.builtin.group import todo_group
5
+ from zrb.config import TODO_DIR
6
+ from zrb.context.any_context import AnyContext
7
+ from zrb.input.str_input import StrInput
8
+ from zrb.input.text_input import TextInput
9
+ from zrb.task.make_task import make_task
10
+ from zrb.util.cli.style import (
11
+ stylize_bold_green,
12
+ stylize_cyan,
13
+ stylize_magenta,
14
+ stylize_yellow,
15
+ )
16
+ from zrb.util.string.name import get_random_name
17
+ from zrb.util.todo import (
18
+ TodoTask,
19
+ parse_todo_line,
20
+ read_todo_from_file,
21
+ todo_task_to_line,
22
+ write_todo_to_file,
23
+ )
24
+
25
+
26
+ @make_task(
27
+ name="todo-add",
28
+ input=[
29
+ StrInput(
30
+ name="description",
31
+ description="Task description",
32
+ prompt="Task description",
33
+ ),
34
+ StrInput(
35
+ name="priority",
36
+ description="Task priority",
37
+ prompt="Task priority",
38
+ default_str="E",
39
+ ),
40
+ StrInput(
41
+ name="project",
42
+ description="Task project",
43
+ prompt="Task project (space separated)",
44
+ ),
45
+ StrInput(
46
+ name="context",
47
+ description="Task context",
48
+ prompt="Task context (space separated)",
49
+ ),
50
+ ],
51
+ description="➕ Add todo",
52
+ group=todo_group,
53
+ alias="add",
54
+ )
55
+ def todo_add(ctx: AnyContext):
56
+ todo_file_path = os.path.join(TODO_DIR, "todo.txt")
57
+ todo_tasks: list[TodoTask] = []
58
+ if os.path.isfile(todo_file_path):
59
+ todo_tasks = read_todo_from_file(todo_file_path)
60
+ else:
61
+ os.makedirs(TODO_DIR, exist_ok=True)
62
+ todo_tasks.append(
63
+ _complete_todo_task(
64
+ TodoTask(
65
+ priority=ctx.input.priority.upper(),
66
+ description=ctx.input.description,
67
+ contexts=[
68
+ context.strip()
69
+ for context in ctx.input.context.split(" ")
70
+ if context.strip() != ""
71
+ ],
72
+ projects=[
73
+ project.strip()
74
+ for project in ctx.input.project.split(" ")
75
+ if project.strip() != ""
76
+ ],
77
+ )
78
+ )
79
+ )
80
+ write_todo_to_file(todo_file_path, todo_tasks)
81
+ return _get_visual_todo_list()
82
+
83
+
84
+ @make_task(name="todo-list", description="📋 List todo", group=todo_group, alias="list")
85
+ def todo_list(ctx: AnyContext):
86
+ return _get_visual_todo_list()
87
+
88
+
89
+ @make_task(
90
+ name="todo-edit",
91
+ input=[
92
+ TextInput(
93
+ name="text",
94
+ description="Todo.txt content",
95
+ prompt="Todo.txt content (will override existing)",
96
+ default_str=lambda _: _get_todo_txt_content(),
97
+ ),
98
+ ],
99
+ description="✏️ Edit todo",
100
+ group=todo_group,
101
+ alias="edit",
102
+ )
103
+ def todo_edit(ctx: AnyContext):
104
+ todo_tasks = [
105
+ _complete_todo_task(parse_todo_line(line))
106
+ for line in ctx.input.text.split("\n")
107
+ if line.strip() != ""
108
+ ]
109
+ new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_tasks)
110
+ todo_file_path = os.path.join(TODO_DIR, "todo.txt")
111
+ with open(todo_file_path, "w") as f:
112
+ f.write(new_content)
113
+ return _get_visual_todo_list()
114
+
115
+
116
+ def _complete_todo_task(todo_task: TodoTask):
117
+ if todo_task.creation_date is None:
118
+ todo_task.creation_date = datetime.date.today()
119
+ if "id" not in todo_task.keyval:
120
+ todo_task.keyval["id"] = get_random_name()
121
+ return todo_task
122
+
123
+
124
+ def _get_visual_todo_list() -> str:
125
+ todo_file_path = os.path.join(TODO_DIR, "todo.txt")
126
+ if not os.path.isfile(todo_file_path):
127
+ return "\n".join(["", " Todo.txt not found... 🌵🦖", ""])
128
+ todo_tasks = read_todo_from_file(todo_file_path)
129
+ if len(todo_tasks) == 0:
130
+ return "\n".join(["", " Empty todo list... 🌵🦖", ""])
131
+ max_desc_name_length = max(len(todo_task.description) for todo_task in todo_tasks)
132
+ if max_desc_name_length < len("DESCRIPTION"):
133
+ max_desc_name_length = len("DESCRIPTION")
134
+ # Headers
135
+ results = [
136
+ stylize_bold_green(
137
+ " ".join(
138
+ [
139
+ "".ljust(3), # priority
140
+ "".ljust(3), # completed
141
+ "COMPLETED AT".rjust(14), # completed date
142
+ "CREATED AT".rjust(14), # completed date
143
+ "DESCRIPTION".ljust(max_desc_name_length),
144
+ "PROJECT/CONTEXT/OTHERS",
145
+ ]
146
+ )
147
+ )
148
+ ]
149
+ for todo_task in todo_tasks:
150
+ completed = "[x]" if todo_task.completed else "[ ]"
151
+ priority = " " if todo_task.priority is None else f"({todo_task.priority})"
152
+ completion_date = stylize_yellow(_date_to_str(todo_task.completion_date))
153
+ creation_date = stylize_cyan(_date_to_str(todo_task.creation_date))
154
+ description = todo_task.description.ljust(max_desc_name_length)
155
+ additions = ", ".join(
156
+ [stylize_yellow(f"+{project}") for project in todo_task.projects]
157
+ + [stylize_cyan(f"@{context}") for context in todo_task.contexts]
158
+ + [stylize_magenta(f"{key}:{val}") for key, val in todo_task.keyval.items()]
159
+ )
160
+ results.append(
161
+ " ".join(
162
+ [
163
+ completed,
164
+ priority,
165
+ completion_date,
166
+ creation_date,
167
+ description,
168
+ additions,
169
+ ]
170
+ )
171
+ )
172
+ return "\n".join(results)
173
+
174
+
175
+ def _date_to_str(date: datetime.date | None) -> str:
176
+ if date is None:
177
+ return "".ljust(14)
178
+ return date.strftime("%a %Y-%m-%d")
179
+
180
+
181
+ def _get_todo_txt_content() -> str:
182
+ todo_file_path = os.path.join(TODO_DIR, "todo.txt")
183
+ if not os.path.isfile(todo_file_path):
184
+ return ""
185
+ with open(todo_file_path, "r") as f:
186
+ return f.read()
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Any
3
3
 
4
- from ..context.any_shared_context import AnySharedContext
4
+ from zrb.context.any_shared_context import AnySharedContext
5
5
 
6
6
 
7
7
  class AnyCallback(ABC):
zrb/callback/callback.py CHANGED
@@ -1,10 +1,10 @@
1
1
  from typing import Any
2
2
 
3
- from ..attr.type import StrDictAttr
4
- from ..session.any_session import AnySession
5
- from ..task.any_task import AnyTask
6
- from ..util.attr import get_str_dict_attr
7
- from .any_callback import AnyCallback
3
+ from zrb.attr.type import StrDictAttr
4
+ from zrb.callback.any_callback import AnyCallback
5
+ from zrb.session.any_session import AnySession
6
+ from zrb.task.any_task import AnyTask
7
+ from zrb.util.attr import get_str_dict_attr
8
8
 
9
9
 
10
10
  class Callback(AnyCallback):
zrb/cmd/cmd_val.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import Callable
3
3
 
4
- from ..attr.type import fstring
5
- from ..context.context import Context
4
+ from zrb.attr.type import fstring
5
+ from zrb.context.context import Context
6
6
 
7
7
 
8
8
  class AnyCmdVal(ABC):
zrb/config.py CHANGED
@@ -3,7 +3,7 @@ import logging
3
3
  import os
4
4
  import platform
5
5
 
6
- from .util.string.conversion import to_boolean
6
+ from zrb.util.string.conversion import to_boolean
7
7
 
8
8
 
9
9
  def _get_current_shell() -> str:
@@ -53,8 +53,11 @@ SHOW_PROMPT = to_boolean(os.getenv("ZRB_SHOW_PROMPT", "1"))
53
53
  SESSION_LOG_DIR = os.getenv(
54
54
  "ZRB_SESSION_LOG_DIR", os.path.expanduser(os.path.join("~", ".zrb-session"))
55
55
  )
56
+ TODO_DIR = os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
56
57
  VERSION = metadata.version("zrb")
57
58
  WEB_HTTP_PORT = int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
59
+ LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
60
+ LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", "You are a helpful assistant")
58
61
 
59
62
  BANNER = f"""
60
63
  bb
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from ..context.any_context import AnyContext
3
+ from zrb.context.any_context import AnyContext
4
4
 
5
5
 
6
6
  class AnyContentTransformer(ABC):
@@ -2,8 +2,8 @@ import fnmatch
2
2
  import re
3
3
  from collections.abc import Callable
4
4
 
5
- from ..context.any_context import AnyContext
6
- from .any_content_transformer import AnyContentTransformer
5
+ from zrb.content_transformer.any_content_transformer import AnyContentTransformer
6
+ from zrb.context.any_context import AnyContext
7
7
 
8
8
 
9
9
  class ContentTransformer(AnyContentTransformer):
@@ -2,7 +2,7 @@ import sys
2
2
  from abc import abstractmethod
3
3
  from typing import TextIO
4
4
 
5
- from .any_shared_context import AnySharedContext
5
+ from zrb.context.any_shared_context import AnySharedContext
6
6
 
7
7
 
8
8
  class AnyContext(AnySharedContext):
@@ -31,6 +31,10 @@ class AnyContext(AnySharedContext):
31
31
  """
32
32
  pass
33
33
 
34
+ @abstractmethod
35
+ def update_task_env(self, task_env: dict[str, str]):
36
+ pass
37
+
34
38
  @abstractmethod
35
39
  def print(
36
40
  self,
@@ -3,11 +3,11 @@ 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 ..dot_dict.dot_dict import DotDict
7
- from ..xcom.xcom import Xcom
6
+ from zrb.dot_dict.dot_dict import DotDict
7
+ from zrb.xcom.xcom import Xcom
8
8
 
9
9
  if TYPE_CHECKING:
10
- from ..session import any_session
10
+ from zrb.session import any_session
11
11
 
12
12
 
13
13
  class AnySharedContext(ABC):
zrb/context/context.py CHANGED
@@ -4,12 +4,12 @@ import re
4
4
  import sys
5
5
  from typing import Any, TextIO
6
6
 
7
- from ..dot_dict.dot_dict import DotDict
8
- from ..session.any_session import AnySession
9
- from ..util.cli.style import stylize, stylize_error, stylize_log, stylize_warning
10
- from ..util.string.conversion import to_boolean
11
- from .any_context import AnyContext
12
- from .any_shared_context import AnySharedContext
7
+ from zrb.context.any_context import AnyContext
8
+ from zrb.context.any_shared_context import AnySharedContext
9
+ from zrb.dot_dict.dot_dict import DotDict
10
+ from zrb.session.any_session import AnySession
11
+ from zrb.util.cli.style import stylize, stylize_error, stylize_log, stylize_warning
12
+ from zrb.util.string.conversion import to_boolean
13
13
 
14
14
 
15
15
  def _remove_ansi_escape_sequences(text):
@@ -22,6 +22,7 @@ class Context(AnyContext):
22
22
  self, shared_ctx: AnySharedContext, task_name: str, color: int, icon: str
23
23
  ):
24
24
  self._shared_ctx = shared_ctx
25
+ self._env = shared_ctx.env.copy()
25
26
  self._task_name = task_name
26
27
  self._color = color
27
28
  self._icon = icon
@@ -38,7 +39,7 @@ class Context(AnyContext):
38
39
 
39
40
  @property
40
41
  def env(self) -> DotDict:
41
- return self._shared_ctx.env
42
+ return self._env
42
43
 
43
44
  @property
44
45
  def args(self) -> list[Any]:
@@ -56,6 +57,9 @@ class Context(AnyContext):
56
57
  def session(self) -> AnySession | None:
57
58
  return self._shared_ctx._session
58
59
 
60
+ def update_task_env(self, task_env: dict[str, str]):
61
+ self._env.update(task_env)
62
+
59
63
  def append_to_shared_log(self, message: str):
60
64
  self._shared_ctx.append_to_shared_log(message)
61
65
 
@@ -99,14 +103,16 @@ class Context(AnyContext):
99
103
  ):
100
104
  color = self._color
101
105
  icon = self._icon
102
- task_name = self._task_name.rjust(15)
106
+ max_name_length = max(len(name) + len(icon) for name in self.session.task_names)
107
+ styled_task_name = f"{icon} {self._task_name}"
108
+ padded_styled_task_name = styled_task_name.rjust(max_name_length + 1)
103
109
  if self._attempt == 0:
104
110
  attempt_status = "".ljust(5)
105
111
  else:
106
112
  attempt_status = f"{self._attempt}/{self._max_attempt}".ljust(5)
107
113
  now = datetime.datetime.now()
108
114
  formatted_time = now.strftime("%y%m%d %H:%M:%S.%f")[:19]
109
- prefix = f"{formatted_time} {attempt_status} {icon} {task_name} ⬤ "
115
+ prefix = f"{formatted_time} {attempt_status} {padded_styled_task_name} ⬤ "
110
116
  message = sep.join([f"{value}" for value in values])
111
117
  self.append_to_shared_log(_remove_ansi_escape_sequences(f"{prefix} {message}"))
112
118
  stylized_prefix = stylize(prefix, color=color)
@@ -1,10 +1,12 @@
1
1
  import datetime
2
2
  from typing import Any
3
3
 
4
- from ..config import LOGGING_LEVEL
5
- from ..dot_dict.dot_dict import DotDict
6
- from ..session.any_session import AnySession
7
- from ..util.string.conversion import (
4
+ from zrb.config import LOGGING_LEVEL
5
+ from zrb.context.any_shared_context import AnySharedContext
6
+ from zrb.dot_dict.dot_dict import DotDict
7
+ from zrb.session.any_session import AnySession
8
+ from zrb.util.string.conversion import (
9
+ double_quote,
8
10
  to_boolean,
9
11
  to_camel_case,
10
12
  to_human_case,
@@ -12,9 +14,8 @@ from ..util.string.conversion import (
12
14
  to_pascal_case,
13
15
  to_snake_case,
14
16
  )
15
- from ..util.string.format import fstring_format
16
- from ..xcom.xcom import Xcom
17
- from .any_shared_context import AnySharedContext
17
+ from zrb.util.string.format import fstring_format
18
+ from zrb.xcom.xcom import Xcom
18
19
 
19
20
 
20
21
  class SharedContext(AnySharedContext):
@@ -86,12 +87,12 @@ class SharedContext(AnySharedContext):
86
87
  data={
87
88
  "ctx": self,
88
89
  "datetime": datetime,
89
- "Xcom": Xcom,
90
90
  "to_boolean": to_boolean,
91
91
  "to_camel_case": to_camel_case,
92
92
  "to_human_case": to_human_case,
93
93
  "to_kebab_case": to_kebab_case,
94
94
  "to_pascal_case": to_pascal_case,
95
95
  "to_snake_case": to_snake_case,
96
+ "double_quote": double_quote,
96
97
  },
97
98
  )
zrb/env/__init__.py CHANGED
@@ -1,3 +0,0 @@
1
- from .env import Env
2
-
3
- assert Env
zrb/env/any_env.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from ..context.any_shared_context import AnySharedContext
3
+ from zrb.context.any_shared_context import AnySharedContext
4
4
 
5
5
 
6
6
  class AnyEnv(ABC):
7
7
  @abstractmethod
8
- def update_shared_context(self, shared_ctx: AnySharedContext):
8
+ def update_context(self, shared_ctx: AnySharedContext):
9
9
  pass
zrb/env/env.py CHANGED
@@ -1,10 +1,9 @@
1
1
  import os
2
2
 
3
+ from zrb.attr.type import StrAttr
3
4
  from zrb.context.any_shared_context import AnySharedContext
4
-
5
- from ..attr.type import StrAttr
6
- from ..util.attr import get_str_attr
7
- from .any_env import AnyEnv
5
+ from zrb.env.any_env import AnyEnv
6
+ from zrb.util.attr import get_str_attr
8
7
 
9
8
 
10
9
  class Env(AnyEnv):
@@ -22,7 +21,7 @@ class Env(AnyEnv):
22
21
  self._link_to_os = link_to_os
23
22
  self._os_name = os_name
24
23
 
25
- def update_shared_context(self, shared_ctx: AnySharedContext):
24
+ def update_context(self, shared_ctx: AnySharedContext):
26
25
  if self._link_to_os:
27
26
  os_name = self._name if self._os_name is None else self._os_name
28
27
  value = os.getenv(os_name, self._get_default_value(shared_ctx))
zrb/env/env_file.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from dotenv import dotenv_values
2
2
 
3
- from ..attr.type import StrAttr
4
- from ..context.shared_context import SharedContext
5
- from ..util.attr import get_str_attr
6
- from .env_map import EnvMap
3
+ from zrb.attr.type import StrAttr
4
+ from zrb.context.shared_context import SharedContext
5
+ from zrb.env.env_map import EnvMap
6
+ from zrb.util.attr import get_str_attr
7
7
 
8
8
 
9
9
  class EnvFile(EnvMap):
zrb/env/env_map.py CHANGED
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  from collections.abc import Callable
3
3
 
4
- from ..context.shared_context import SharedContext
5
- from .any_env import AnyEnv
4
+ from zrb.context.shared_context import SharedContext
5
+ from zrb.env.any_env import AnyEnv
6
6
 
7
7
 
8
8
  class EnvMap(AnyEnv):
@@ -18,7 +18,7 @@ class EnvMap(AnyEnv):
18
18
  self._os_prefix = os_prefix
19
19
  self._auto_render = auto_render
20
20
 
21
- def update_shared_context(self, shared_ctx: SharedContext) -> dict[str, str]:
21
+ def update_context(self, shared_ctx: SharedContext) -> dict[str, str]:
22
22
  env_map = self._get_env_map(shared_ctx)
23
23
  for name, default_value in env_map.items():
24
24
  if self._link_to_os:
@@ -27,7 +27,7 @@ class EnvMap(AnyEnv):
27
27
  value = os.getenv(os_name, default_value)
28
28
  else:
29
29
  value = default_value
30
- shared_ctx.env[self._name] = value
30
+ shared_ctx.env[name] = value
31
31
 
32
32
  def _get_env_map(self, shared_ctx: SharedContext) -> dict[str, str]:
33
33
  if callable(self._env_map):
zrb/group/__init__.py CHANGED
@@ -1,3 +0,0 @@
1
- from .group import Group
2
-
3
- assert Group
zrb/group/any_group.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Optional
2
+ from typing import Optional, Union
3
3
 
4
- from ..task.any_task import AnyTask
4
+ from zrb.task.any_task import AnyTask
5
5
 
6
6
 
7
7
  class AnyGroup(ABC):
@@ -36,7 +36,7 @@ class AnyGroup(ABC):
36
36
  pass
37
37
 
38
38
  @abstractmethod
39
- def add_group(self, group: "AnyGroup") -> "AnyGroup":
39
+ def add_group(self, group: Union["AnyGroup", str]) -> "AnyGroup":
40
40
  pass
41
41
 
42
42
  @abstractmethod
zrb/group/group.py CHANGED
@@ -1,5 +1,5 @@
1
- from ..task.any_task import AnyTask
2
- from .any_group import AnyGroup
1
+ from zrb.group.any_group import AnyGroup
2
+ from zrb.task.any_task import AnyTask
3
3
 
4
4
 
5
5
  class Group(AnyGroup):
@@ -42,10 +42,11 @@ class Group(AnyGroup):
42
42
  alias.sort()
43
43
  return {name: self._tasks.get(name) for name in alias}
44
44
 
45
- def add_group(self, group: AnyGroup, alias: str | None = None) -> AnyGroup:
46
- alias = alias if alias is not None else group.name
47
- self._groups[alias] = group
48
- return group
45
+ def add_group(self, group: AnyGroup | str, alias: str | None = None) -> AnyGroup:
46
+ real_group = Group(group) if isinstance(group, str) else group
47
+ alias = alias if alias is not None else real_group.name
48
+ self._groups[alias] = real_group
49
+ return real_group
49
50
 
50
51
  def add_task(self, task: AnyTask, alias: str | None = None) -> AnyTask:
51
52
  alias = alias if alias is not None else task.name
zrb/input/any_input.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from ..context.any_shared_context import AnySharedContext
3
+ from zrb.context.any_shared_context import AnySharedContext
4
4
 
5
5
 
6
6
  class AnyInput(ABC):
zrb/input/base_input.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from typing import Any
2
2
 
3
- from ..attr.type import StrAttr
4
- from ..context.any_shared_context import AnySharedContext
5
- from ..util.attr import get_str_attr
6
- from .any_input import AnyInput
3
+ from zrb.attr.type import StrAttr
4
+ from zrb.context.any_shared_context import AnySharedContext
5
+ from zrb.input.any_input import AnyInput
6
+ from zrb.util.attr import get_str_attr
7
7
 
8
8
 
9
9
  class BaseInput(AnyInput):
zrb/input/bool_input.py CHANGED
@@ -1,10 +1,10 @@
1
- from ..attr.type import StrAttr
2
- from ..context.any_shared_context import AnySharedContext
3
- from ..util.string.conversion import to_boolean
4
- from .base_input import BaseInput
1
+ from zrb.attr.type import StrAttr
2
+ from zrb.context.any_shared_context import AnySharedContext
3
+ from zrb.input.base_input import BaseInput
4
+ from zrb.util.string.conversion import to_boolean
5
5
 
6
6
 
7
- class IntInput(BaseInput):
7
+ class BoolInput(BaseInput):
8
8
  def __init__(
9
9
  self,
10
10
  name: str,
zrb/input/float_input.py CHANGED
@@ -1,6 +1,6 @@
1
- from ..attr.type import StrAttr
2
- from ..context.any_shared_context import AnySharedContext
3
- from .base_input import BaseInput
1
+ from zrb.attr.type import StrAttr
2
+ from zrb.context.any_shared_context import AnySharedContext
3
+ from zrb.input.base_input import BaseInput
4
4
 
5
5
 
6
6
  class FloatInput(BaseInput):
zrb/input/int_input.py CHANGED
@@ -1,6 +1,6 @@
1
- from ..attr.type import StrAttr
2
- from ..context.any_shared_context import AnySharedContext
3
- from .base_input import BaseInput
1
+ from zrb.attr.type import StrAttr
2
+ from zrb.context.any_shared_context import AnySharedContext
3
+ from zrb.input.base_input import BaseInput
4
4
 
5
5
 
6
6
  class IntInput(BaseInput):
@@ -0,0 +1,51 @@
1
+ from zrb.attr.type import StrAttr, StrListAttr
2
+ from zrb.context.any_shared_context import AnySharedContext
3
+ from zrb.input.base_input import BaseInput
4
+ from zrb.util.attr import get_str_list_attr
5
+
6
+
7
+ class OptionInput(BaseInput):
8
+ def __init__(
9
+ self,
10
+ name: str,
11
+ description: str | None = None,
12
+ prompt: str | None = None,
13
+ options: StrListAttr = [],
14
+ default_str: StrAttr = "",
15
+ auto_render: bool = True,
16
+ allow_empty: bool = True,
17
+ ):
18
+ super().__init__(
19
+ name=name,
20
+ description=description,
21
+ prompt=prompt,
22
+ default_str=default_str,
23
+ auto_render=auto_render,
24
+ allow_empty=allow_empty,
25
+ )
26
+ self._options = options
27
+
28
+ def to_html(self, ctx: AnySharedContext) -> str:
29
+ name = self.name
30
+ description = self.description
31
+ default = self._get_default_str(ctx)
32
+ html = [f'<select name="{name}" placeholder="{description}">']
33
+ for value in get_str_list_attr(ctx, self._options, self._auto_render):
34
+ selected = "selected" if value == default else ""
35
+ html.append(f'<option value="{value}" {selected}>{value}</option>')
36
+ html.append("</select>")
37
+ return "\n".join(html)
38
+
39
+ def _prompt_cli_str(self, shared_ctx: AnySharedContext) -> str:
40
+ prompt_message = self.prompt_message
41
+ default_value = self._get_default_str(shared_ctx)
42
+ options = get_str_list_attr(shared_ctx, self._options, self._auto_render)
43
+ option_str = ", ".join(options)
44
+ if default_value != "":
45
+ prompt_message = f"{prompt_message} ({option_str}) [{default_value}]"
46
+ value = input(f"{prompt_message}: ")
47
+ if value.strip() != "" and value.strip() not in options:
48
+ value = self._prompt_cli_str(shared_ctx)
49
+ if value.strip() == "":
50
+ value = default_value
51
+ return value