zrb 1.0.0a16__py3-none-any.whl → 1.0.0a17__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 (121) hide show
  1. zrb/__main__.py +3 -0
  2. zrb/builtin/__init__.py +2 -2
  3. zrb/builtin/git.py +10 -2
  4. zrb/builtin/git_subtree.py +4 -0
  5. zrb/builtin/llm/tool/rag.py +2 -2
  6. zrb/builtin/project/add/fastapp/fastapp_input.py +16 -0
  7. zrb/builtin/project/add/fastapp/fastapp_task.py +92 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/entity/any_client_method.template.py +27 -0
  9. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/my_entity_usecase.py +6 -6
  10. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/my_entity_db_repository.py +10 -5
  11. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/my_entity_repository.py +5 -5
  12. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/schema.template.py +8 -0
  13. zrb/builtin/project/add/{fastapp_template/_zrb/entity/create_entity_task.py → fastapp/fastapp_template/_zrb/entity/task.py} +106 -42
  14. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/input.py +1 -1
  15. zrb/builtin/project/add/{fastapp_template/_zrb/module/create_module_task.py → fastapp/fastapp_template/_zrb/module/task.py} +38 -31
  16. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/client/any_client.py +7 -0
  17. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/client/api_client.py +6 -0
  18. zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/client/direct_client.py +2 -2
  19. zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/client/factory.py +3 -3
  20. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/route.py +33 -0
  21. zrb/builtin/project/add/{fastapp_template/_zrb/main.py → fastapp/fastapp_template/_zrb/task.py} +3 -3
  22. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/base_usecase.py +19 -6
  23. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/config.py +1 -0
  24. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/any_client.py +10 -4
  25. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/api_client.py +2 -2
  26. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/client/direct_client.py +6 -0
  27. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/factory.py +3 -3
  28. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/route.py +37 -0
  29. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/repository/user_db_repository.py +10 -4
  30. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/service/user/repository/user_repository.py +43 -0
  31. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/user_usecase.py +10 -4
  32. zrb/builtin/project/add/fastapp/fastapp_template/module/gateway/route.py +43 -0
  33. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/requirements.txt +1 -1
  34. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/permission.py +8 -0
  35. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/role.py +8 -0
  36. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/user.py +8 -0
  37. zrb/builtin/project/add/fastapp/fastapp_util.py +22 -0
  38. zrb/builtin/project/create/{create.py → project_task.py} +1 -1
  39. zrb/builtin/setup/asdf/asdf_helper.py +4 -8
  40. zrb/builtin/setup/tmux/tmux.py +7 -12
  41. zrb/builtin/todo.py +7 -12
  42. zrb/cmd/cmd_val.py +2 -2
  43. zrb/content_transformer/content_transformer.py +3 -4
  44. zrb/input/base_input.py +13 -1
  45. zrb/input/text_input.py +4 -4
  46. zrb/runner/web_controller/group_info_ui/controller.py +6 -14
  47. zrb/runner/web_controller/home_page/controller.py +6 -14
  48. zrb/runner/web_controller/task_ui/controller.py +11 -18
  49. zrb/session_state_logger/file_session_state_logger.py +4 -7
  50. zrb/task/cmd_task.py +1 -1
  51. zrb/task/llm_task.py +3 -5
  52. zrb/task/scaffolder.py +13 -1
  53. zrb/util/codemod/{add_code_to_class.py → append_code_to_class.py} +1 -1
  54. zrb/util/codemod/{add_code_to_function.py → append_code_to_function.py} +3 -1
  55. zrb/util/codemod/{add_code_to_method.py → append_code_to_method.py} +1 -1
  56. zrb/util/codemod/{add_key_to_dict.py → append_key_to_dict.py} +1 -1
  57. zrb/util/codemod/{add_param_to_function_call.py → append_param_to_function_call.py} +1 -1
  58. zrb/util/codemod/{add_code_to_module.py → prepend_code_to_module.py} +1 -1
  59. zrb/util/codemod/{add_parent_to_class.py → prepend_parent_to_class.py} +1 -1
  60. zrb/util/codemod/{add_property_to_class.py → prepend_property_to_class.py} +1 -1
  61. zrb/util/file.py +18 -0
  62. zrb/util/git_subtree.py +3 -4
  63. zrb/util/todo.py +5 -7
  64. {zrb-1.0.0a16.dist-info → zrb-1.0.0a17.dist-info}/METADATA +2 -2
  65. zrb-1.0.0a17.dist-info/RECORD +234 -0
  66. zrb/builtin/project/add/fastapp.py +0 -87
  67. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/any_client.py +0 -27
  68. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/api_client.py +0 -6
  69. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/route.py +0 -19
  70. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +0 -6
  71. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  72. zrb/builtin/project/add/fastapp_template/module/auth/route.py +0 -22
  73. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/user_repository.py +0 -34
  74. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +0 -27
  75. zrb-1.0.0a16.dist-info/RECORD +0 -231
  76. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/.gitignore +0 -0
  77. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/README.md +0 -0
  78. /zrb/builtin/project/add/{__init__.py → fastapp/fastapp_template/__init__.py} +0 -0
  79. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/column/create_column_task.py +0 -0
  80. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/config.py +0 -0
  81. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/factory.py +0 -0
  82. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/group.py +0 -0
  83. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/alembic.ini +0 -0
  84. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/README +0 -0
  85. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/env.py +0 -0
  86. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/script.py.mako +0 -0
  87. /zrb/builtin/project/add/{fastapp_template/module/gateway → fastapp/fastapp_template/_zrb/module/template/module_template}/migration/versions/.gitkeep +0 -0
  88. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration_metadata.py +0 -0
  89. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/_zrb/module/template/module_template/service}/__init__.py +0 -0
  90. /zrb/builtin/project/add/{fastapp_template/_zrb/module/run_module.template.py → fastapp/fastapp_template/_zrb/module/template/task_definition.py} +0 -0
  91. /zrb/builtin/project/add/{fastapp_template/_zrb/helper.py → fastapp/fastapp_template/_zrb/util.py} +0 -0
  92. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/venv_task.py +0 -0
  93. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template/service → fastapp/fastapp_template/common}/__init__.py +0 -0
  94. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/app.py +0 -0
  95. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/base_db_repository.py +0 -0
  96. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/db_engine.py +0 -0
  97. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/error.py +0 -0
  98. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/schema.py +0 -0
  99. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/main.py +0 -0
  100. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/migrate.py +0 -0
  101. /zrb/builtin/project/add/{fastapp_template/common → fastapp/fastapp_template/module}/__init__.py +0 -0
  102. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/alembic.ini +0 -0
  103. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/README +0 -0
  104. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/env.py +0 -0
  105. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/script.py.mako +0 -0
  106. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/module/auth}/migration/versions/3093c7336477_add_user_table.py +0 -0
  107. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration_metadata.py +0 -0
  108. /zrb/builtin/project/add/{fastapp_template/module → fastapp/fastapp_template/module/auth/service}/__init__.py +0 -0
  109. /zrb/builtin/project/add/{fastapp_template/module/auth/service → fastapp/fastapp_template/module/auth/service/user}/__init__.py +0 -0
  110. /zrb/builtin/project/add/{fastapp_template/module/auth/service/user → fastapp/fastapp_template/module/auth/service/user/repository}/__init__.py +0 -0
  111. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/repository/factory.py +0 -0
  112. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/alembic.ini +0 -0
  113. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/README +0 -0
  114. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/env.py +0 -0
  115. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/script.py.mako +0 -0
  116. /zrb/builtin/project/add/{fastapp_template/module/auth/service/user/repository/__init__.py → fastapp/fastapp_template/module/gateway/migration/versions/.gitkeep} +0 -0
  117. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration_metadata.py +0 -0
  118. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/__init__.py +0 -0
  119. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/template.env +0 -0
  120. {zrb-1.0.0a16.dist-info → zrb-1.0.0a17.dist-info}/WHEEL +0 -0
  121. {zrb-1.0.0a16.dist-info → zrb-1.0.0a17.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import os
2
2
 
3
3
  from zrb.context.any_context import AnyContext
4
+ from zrb.util.file import read_file, write_file
4
5
 
5
6
 
6
7
  def get_install_prerequisites_cmd(ctx: AnyContext) -> str:
@@ -31,14 +32,9 @@ def setup_asdf_ps_config(file_path: str):
31
32
 
32
33
 
33
34
  def _setup_asdf_config(file_path: str, asdf_config: str):
34
- dir_path = os.path.dirname(file_path)
35
- os.makedirs(dir_path, exist_ok=True)
36
35
  if not os.path.isfile(file_path):
37
- with open(file_path, "w") as f:
38
- f.write("")
39
- with open(file_path, "r") as f:
40
- content = f.read()
36
+ write_file(file_path, "")
37
+ content = read_file(file_path)
41
38
  if asdf_config in content:
42
39
  return
43
- with open(file_path, "a") as f:
44
- f.write(f"\n{asdf_config}\n")
40
+ write_file(file_path, [content, asdf_config, ""])
@@ -7,6 +7,7 @@ from zrb.context.any_context import AnyContext
7
7
  from zrb.input.str_input import StrInput
8
8
  from zrb.task.cmd_task import CmdTask
9
9
  from zrb.task.make_task import make_task
10
+ from zrb.util.file import read_file, write_file
10
11
 
11
12
  install_tmux = CmdTask(
12
13
  name="install-tmux",
@@ -28,22 +29,16 @@ install_tmux = CmdTask(
28
29
  alias="tmux",
29
30
  )
30
31
  def setup_tmux(ctx: AnyContext):
31
- with open(os.path.join(os.path.dirname(__file__), "tmux_config.sh"), "r") as f:
32
- tmux_config_template = f.read()
32
+ tmux_config = read_file(os.path.join(os.path.dirname(__file__), "tmux_config.sh"))
33
33
  tmux_config_file = os.path.expanduser(ctx.input["tmux-config"])
34
- tmux_config_dir = os.path.dirname(tmux_config_file)
35
34
  # Make sure config file exists
36
- os.makedirs(tmux_config_dir, exist_ok=True)
37
35
  if not os.path.isfile(tmux_config_file):
38
- with open(tmux_config_file, "w") as f:
39
- f.write("")
40
- with open(tmux_config_file, "r") as f:
41
- # config file already contain the config
42
- if tmux_config_template in f.read():
43
- return
36
+ write_file(tmux_config_file, "")
37
+ content = read_file(tmux_config_file)
38
+ if tmux_config in content:
39
+ return
44
40
  # Write config
45
- with open(tmux_config_file, "a") as f:
46
- f.write(f"\n{tmux_config_template}\n")
41
+ write_file(tmux_config_file, [content, tmux_config, ""])
47
42
  ctx.print("Setup complete, restart your terminal to continue")
48
43
 
49
44
 
zrb/builtin/todo.py CHANGED
@@ -9,6 +9,7 @@ from zrb.context.any_context import AnyContext
9
9
  from zrb.input.str_input import StrInput
10
10
  from zrb.input.text_input import TextInput
11
11
  from zrb.task.make_task import make_task
12
+ from zrb.util.file import read_file, write_file
12
13
  from zrb.util.todo import (
13
14
  TodoTaskModel,
14
15
  add_durations,
@@ -119,8 +120,7 @@ def show_todo(ctx: AnyContext):
119
120
  log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
120
121
  log_work_list = []
121
122
  if os.path.isfile(log_work_path):
122
- with open(log_work_path, "r") as f:
123
- log_work_list = json.loads(f.read())
123
+ log_work_list = json.loads(read_file(log_work_path))
124
124
  return get_visual_todo_card(todo_task, log_work_list)
125
125
 
126
126
 
@@ -237,8 +237,7 @@ def log_todo(ctx: AnyContext):
237
237
  log_work_dir, f"{todo_task.keyval.get('id')}.json"
238
238
  )
239
239
  if os.path.isfile(log_work_file_path):
240
- with open(log_work_file_path, "r") as f:
241
- log_work_json = f.read()
240
+ log_work_json = read_file(log_work_file_path)
242
241
  else:
243
242
  log_work_json = "[]"
244
243
  log_work: list[dict[str, Any]] = json.loads(log_work_json)
@@ -246,15 +245,13 @@ def log_todo(ctx: AnyContext):
246
245
  {"log": ctx.input.log, "duration": ctx.input.duration, "start": ctx.input.start}
247
246
  )
248
247
  # save todo with log work
249
- with open(log_work_file_path, "w") as f:
250
- f.write(json.dumps(log_work, indent=2))
248
+ write_file(log_work_file_path, json.dumps(log_work, indent=2))
251
249
  # get log work list
252
250
  task_id = todo_task.keyval.get("id", "")
253
251
  log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
254
252
  log_work_list = []
255
253
  if os.path.isfile(log_work_path):
256
- with open(log_work_path, "r") as f:
257
- log_work_list = json.loads(f.read())
254
+ log_work_list = json.loads(read_file(log_work_path))
258
255
  return "\n".join(
259
256
  [
260
257
  get_visual_todo_list(todo_list, TODO_VISUAL_FILTER),
@@ -290,8 +287,7 @@ def edit_todo(ctx: AnyContext):
290
287
  ]
291
288
  new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_list)
292
289
  todo_file_path = os.path.join(TODO_DIR, "todo.txt")
293
- with open(todo_file_path, "w") as f:
294
- f.write(new_content)
290
+ write_file(todo_file_path, new_content)
295
291
  todo_list = load_todo_list(todo_file_path)
296
292
  return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
297
293
 
@@ -300,5 +296,4 @@ def _get_todo_txt_content() -> str:
300
296
  todo_file_path = os.path.join(TODO_DIR, "todo.txt")
301
297
  if not os.path.isfile(todo_file_path):
302
298
  return ""
303
- with open(todo_file_path, "r") as f:
304
- return f.read()
299
+ return read_file(todo_file_path)
zrb/cmd/cmd_val.py CHANGED
@@ -3,6 +3,7 @@ from collections.abc import Callable
3
3
 
4
4
  from zrb.attr.type import fstring
5
5
  from zrb.context.context import Context
6
+ from zrb.util.file import read_file
6
7
 
7
8
 
8
9
  class AnyCmdVal(ABC):
@@ -18,8 +19,7 @@ class CmdPath(AnyCmdVal):
18
19
 
19
20
  def to_str(self, ctx: Context) -> str:
20
21
  file_path = ctx.render(self._path) if self._auto_render else self._path
21
- with open(file_path) as file:
22
- return file.read()
22
+ return read_file(file_path)
23
23
 
24
24
 
25
25
  class Cmd(AnyCmdVal):
@@ -4,6 +4,7 @@ from collections.abc import Callable
4
4
 
5
5
  from zrb.content_transformer.any_content_transformer import AnyContentTransformer
6
6
  from zrb.context.any_context import AnyContext
7
+ from zrb.util.file import read_file, write_file
7
8
 
8
9
 
9
10
  class ContentTransformer(AnyContentTransformer):
@@ -40,12 +41,10 @@ class ContentTransformer(AnyContentTransformer):
40
41
  keyword: self._get_str_replacement(ctx, replacement)
41
42
  for keyword, replacement in self._transform_file.items()
42
43
  }
43
- with open(file_path, "r") as f:
44
- content = f.read()
44
+ content = read_file(file_path)
45
45
  for keyword, replacement in transform_map.items():
46
46
  content = content.replace(keyword, replacement)
47
- with open(file_path, "w") as f:
48
- f.write(content)
47
+ write_file(file_path, content)
49
48
 
50
49
  def _get_str_replacement(
51
50
  self, ctx: AnyContext, replacement: str | Callable[[AnyContext], str]
zrb/input/base_input.py CHANGED
@@ -4,6 +4,7 @@ from zrb.attr.type import StrAttr
4
4
  from zrb.context.any_shared_context import AnySharedContext
5
5
  from zrb.input.any_input import AnyInput
6
6
  from zrb.util.attr import get_str_attr
7
+ from zrb.util.string.conversion import to_snake_case
7
8
 
8
9
 
9
10
  class BaseInput(AnyInput):
@@ -49,7 +50,18 @@ class BaseInput(AnyInput):
49
50
  ):
50
51
  if str_value is None:
51
52
  str_value = self._get_default_str(shared_ctx)
52
- shared_ctx.input[self.name] = self._parse_str_value(str_value)
53
+ value = self._parse_str_value(str_value)
54
+ if self.name in shared_ctx.input:
55
+ raise ValueError(f"Input already defined in the context: {self.name}")
56
+ shared_ctx.input[self.name] = value
57
+ # We want to be able to access ctx.input["project-name"] as
58
+ # ctx.input.project_name
59
+ snake_key = to_snake_case(self.name)
60
+ if snake_key == self.name:
61
+ return
62
+ if snake_key in shared_ctx.input:
63
+ raise ValueError("Input already defined in the context: {snake_key}")
64
+ shared_ctx.input[snake_key] = value
53
65
 
54
66
  def _parse_str_value(self, str_value: str) -> Any:
55
67
  """Override this to transform str_value"""
zrb/input/text_input.py CHANGED
@@ -6,6 +6,7 @@ from collections.abc import Callable
6
6
  from zrb.config import DEFAULT_EDITOR
7
7
  from zrb.context.any_shared_context import AnySharedContext
8
8
  from zrb.input.base_input import BaseInput
9
+ from zrb.util.file import read_file
9
10
 
10
11
 
11
12
  class TextInput(BaseInput):
@@ -83,9 +84,8 @@ class TextInput(BaseInput):
83
84
  # Open the editor
84
85
  subprocess.call([self._editor, temp_file_name])
85
86
  # Read the edited content
86
- with open(temp_file_name, "r") as temp_file:
87
- edited_content = temp_file.read()
88
- parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
89
- edited_content = "\n".join(parts).lstrip()
87
+ edited_content = read_file(temp_file_name)
88
+ parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
89
+ edited_content = "\n".join(parts).lstrip()
90
90
  os.remove(temp_file_name)
91
91
  return edited_content
@@ -1,25 +1,17 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
+ from zrb.util.file import read_file
4
5
  from zrb.util.group import get_non_empty_subgroups, get_subtasks
5
6
  from zrb.util.string.format import fstring_format
6
7
 
7
8
  _DIR = os.path.dirname(__file__)
8
9
 
9
- with open(os.path.join(_DIR, "view.html"), "r") as f:
10
- _VIEW_TEMPLATE = f.read()
11
-
12
- with open(os.path.join(_DIR, "partial", "group_info.html")) as f:
13
- _GROUP_INFO_TEMPLATE = f.read()
14
-
15
- with open(os.path.join(_DIR, "partial", "group_li.html")) as f:
16
- _GROUP_LI_TEMPLATE = f.read()
17
-
18
- with open(os.path.join(_DIR, "partial", "task_info.html")) as f:
19
- _TASK_INFO_TEMPLATE = f.read()
20
-
21
- with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
22
- _TASK_LI_TEMPLATE = f.read()
10
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
11
+ _GROUP_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_info.html"))
12
+ _GROUP_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_li.html"))
13
+ _TASK_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_info.html"))
14
+ _TASK_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_li.html"))
23
15
 
24
16
 
25
17
  def handle_group_info_ui(root_group: AnyGroup, group: AnyGroup, url: str):
@@ -1,25 +1,17 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
+ from zrb.util.file import read_file
4
5
  from zrb.util.group import get_non_empty_subgroups, get_subtasks
5
6
  from zrb.util.string.format import fstring_format
6
7
 
7
8
  _DIR = os.path.dirname(__file__)
8
9
 
9
- with open(os.path.join(_DIR, "view.html"), "r") as f:
10
- _VIEW_TEMPLATE = f.read()
11
-
12
- with open(os.path.join(_DIR, "partial", "group_info.html")) as f:
13
- _GROUP_INFO_TEMPLATE = f.read()
14
-
15
- with open(os.path.join(_DIR, "partial", "group_li.html")) as f:
16
- _GROUP_LI_TEMPLATE = f.read()
17
-
18
- with open(os.path.join(_DIR, "partial", "task_info.html")) as f:
19
- _TASK_INFO_TEMPLATE = f.read()
20
-
21
- with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
22
- _TASK_LI_TEMPLATE = f.read()
10
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
11
+ _GROUP_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_info.html"))
12
+ _GROUP_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_li.html"))
13
+ _TASK_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_info.html"))
14
+ _TASK_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_li.html"))
23
15
 
24
16
 
25
17
  def handle_home_page(root_group: AnyGroup):
@@ -1,30 +1,23 @@
1
1
  import os
2
2
 
3
- from zrb.context.shared_context import SharedContext
4
3
  from zrb.group.any_group import AnyGroup
5
4
  from zrb.session.any_session import AnySession
6
5
  from zrb.task.any_task import AnyTask
6
+ from zrb.util.file import read_file
7
7
  from zrb.util.string.format import fstring_format
8
8
 
9
9
  _DIR = os.path.dirname(__file__)
10
10
 
11
- with open(os.path.join(_DIR, "view.html"), "r") as f:
12
- _VIEW_TEMPLATE = f.read()
13
-
14
- with open(os.path.join(_DIR, "partial", "input.html")) as f:
15
- _TASK_INPUT_TEMPLATE = f.read()
16
-
17
- with open(os.path.join(_DIR, "partial", "main.js")) as f:
18
- _MAIN_SCRIPT = f.read()
19
-
20
- with open(os.path.join(_DIR, "partial", "show-existing-session.js")) as f:
21
- _SHOW_EXISTING_SESSION_SCRIPT = f.read()
22
-
23
- with open(os.path.join(_DIR, "partial", "visualize-history.js")) as f:
24
- _VISUALIZE_HISTORY_SCRIPT = f.read()
25
-
26
- with open(os.path.join(_DIR, "partial", "common-util.js")) as f:
27
- _COMMON_UTIL_SCRIPT = f.read()
11
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
12
+ _TASK_INPUT_TEMPLATE = read_file(os.path.join(_DIR, "partial", "input.html"))
13
+ _MAIN_SCRIPT = read_file(os.path.join(_DIR, "partial", "main.js"))
14
+ _SHOW_EXISTING_SESSION_SCRIPT = read_file(
15
+ os.path.join(_DIR, "partial", "show-existing-session.js")
16
+ )
17
+ _VISUALIZE_HISTORY_SCRIPT = read_file(
18
+ os.path.join(_DIR, "partial", "visualize-history.js")
19
+ )
20
+ _COMMON_UTIL_SCRIPT = read_file(os.path.join(_DIR, "partial", "common-util.js"))
28
21
 
29
22
 
30
23
  def handle_task_ui(
@@ -3,6 +3,7 @@ import os
3
3
 
4
4
  from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
5
5
  from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
6
+ from zrb.util.file import read_file, write_file
6
7
 
7
8
 
8
9
  class FileSessionStateLogger(AnySessionStateLogger):
@@ -15,20 +16,16 @@ class FileSessionStateLogger(AnySessionStateLogger):
15
16
  session_dir_path = os.path.dirname(session_file_path)
16
17
  if not os.path.isdir(session_dir_path):
17
18
  os.makedirs(session_dir_path, exist_ok=True)
18
- with open(session_file_path, "w") as f:
19
- f.write(session_log.model_dump_json())
19
+ write_file(session_file_path, session_log.model_dump_json())
20
20
  start_time = session_log.start_time
21
21
  if start_time == "":
22
22
  return
23
23
  timeline_dir_path = self._get_timeline_dir_path(session_log)
24
- os.makedirs(timeline_dir_path, exist_ok=True)
25
- with open(os.path.join(timeline_dir_path, session_log.name), "w"):
26
- pass
24
+ write_file(os.path.join(timeline_dir_path, session_log.name), "")
27
25
 
28
26
  def read(self, session_name: str) -> SessionStateLog:
29
27
  session_file_path = self._get_session_file_path(session_name)
30
- with open(session_file_path, "r") as f:
31
- return SessionStateLog.model_validate_json(f.read())
28
+ return SessionStateLog.model_validate_json(read_file(session_file_path))
32
29
 
33
30
  def list(
34
31
  self,
zrb/task/cmd_task.py CHANGED
@@ -132,7 +132,7 @@ class CmdTask(BaseTask):
132
132
  if return_code != 0:
133
133
  ctx.log_error(f"Exit status: {return_code}")
134
134
  raise Exception(
135
- f"Process {self._name} exited ({return_code}): {cmd_result.stderr}"
135
+ f"Process {self._name} exited ({return_code}): {cmd_result.error}"
136
136
  )
137
137
  ctx.log_info(f"Exit status: {return_code}")
138
138
  return cmd_result
zrb/task/llm_task.py CHANGED
@@ -15,6 +15,7 @@ from zrb.task.any_task import AnyTask
15
15
  from zrb.task.base_task import BaseTask
16
16
  from zrb.util.attr import get_str_attr
17
17
  from zrb.util.cli.style import stylize_faint
18
+ from zrb.util.file import read_file, write_file
18
19
  from zrb.util.llm.tool import callable_to_tool_schema
19
20
 
20
21
  ListOfDict = list[dict[str, Any]]
@@ -171,9 +172,7 @@ class LLMTask(BaseTask):
171
172
  messages.append(tool_call_message)
172
173
  continue
173
174
  if history_file != "":
174
- os.makedirs(os.path.dirname(history_file), exist_ok=True)
175
- with open(history_file, "w") as f:
176
- f.write(json.dumps(messages, indent=2))
175
+ write_file(history_file, json.dumps(messages, indent=2))
177
176
  return response_message.content
178
177
 
179
178
  def _get_model(self, ctx: AnyContext) -> str:
@@ -211,8 +210,7 @@ class LLMTask(BaseTask):
211
210
  and history_file != ""
212
211
  and os.path.isfile(history_file)
213
212
  ):
214
- with open(history_file, "r") as f:
215
- return json.loads(f.read())
213
+ return json.loads(read_file(history_file))
216
214
  return self._history
217
215
 
218
216
  def _get_history_file(self, ctx: AnyContext) -> str:
zrb/task/scaffolder.py CHANGED
@@ -30,9 +30,11 @@ class Scaffolder(BaseTask):
30
30
  destination_path: StrAttr | None = None,
31
31
  render_destination_path: bool = True,
32
32
  transform_path: TransformConfig = {},
33
+ render_transform_path: bool = True,
33
34
  transform_content: (
34
35
  list[AnyContentTransformer] | AnyContentTransformer | TransformConfig
35
36
  ) = [],
37
+ render_transform_content: bool = True,
36
38
  execute_condition: BoolAttr = True,
37
39
  retries: int = 2,
38
40
  retry_period: float = 0,
@@ -70,7 +72,9 @@ class Scaffolder(BaseTask):
70
72
  self._destination_path = destination_path
71
73
  self._render_destination_path = render_destination_path
72
74
  self._content_transformers = transform_content
75
+ self._render_content_transformers = render_transform_content
73
76
  self._path_transformer = transform_path
77
+ self._render_path_transformer = render_transform_path
74
78
 
75
79
  def _get_source_path(self, ctx: AnyContext) -> str:
76
80
  return get_str_attr(ctx, self._source_path, "", auto_render=True)
@@ -82,7 +86,13 @@ class Scaffolder(BaseTask):
82
86
  if callable(self._content_transformers):
83
87
  return [ContentTransformer(match="*", transform=self._content_transformers)]
84
88
  if isinstance(self._content_transformers, dict):
85
- return [ContentTransformer(match="*", transform=self._content_transformers)]
89
+ return [
90
+ ContentTransformer(
91
+ match="*",
92
+ transform=self._content_transformers,
93
+ auto_render=self._render_content_transformers,
94
+ )
95
+ ]
86
96
  if isinstance(self._content_transformers, AnyContentTransformer):
87
97
  return [self._content_transformers]
88
98
  return self._content_transformers
@@ -128,6 +138,8 @@ class Scaffolder(BaseTask):
128
138
  return self._path_transformer(ctx, file_path)
129
139
  new_file_path = file_path
130
140
  for keyword, replacement in self._path_transformer.items():
141
+ if self._render_path_transformer:
142
+ replacement = ctx.render(replacement)
131
143
  new_file_path = new_file_path.replace(keyword, replacement)
132
144
  return new_file_path
133
145
 
@@ -21,7 +21,7 @@ class ClassCodeAdder(cst.CSTTransformer):
21
21
  return updated_node
22
22
 
23
23
 
24
- def add_code_to_class(original_code: str, class_name: str, method_code: str) -> str:
24
+ def append_code_to_class(original_code: str, class_name: str, method_code: str) -> str:
25
25
  # Parse the original code into a module
26
26
  module = cst.parse_module(original_code)
27
27
  # Initialize transformer with the class name and method code
@@ -22,7 +22,9 @@ class FunctionCodeAdder(cst.CSTTransformer):
22
22
  return updated_node
23
23
 
24
24
 
25
- def add_code_to_function(original_code: str, function_name: str, new_code: str) -> str:
25
+ def append_code_to_function(
26
+ original_code: str, function_name: str, new_code: str
27
+ ) -> str:
26
28
  # Parse the original code into a module
27
29
  module = cst.parse_module(original_code)
28
30
  # Initialize the transformer with the necessary information
@@ -37,7 +37,7 @@ class MethodCodeAdder(cst.CSTTransformer):
37
37
  return updated_node
38
38
 
39
39
 
40
- def add_code_to_method(
40
+ def append_code_to_method(
41
41
  original_code: str, class_name: str, function_name: str, new_code: str
42
42
  ) -> str:
43
43
  # Parse the original code into a module
@@ -33,7 +33,7 @@ class DictionaryModifier(cst.CSTTransformer):
33
33
  return updated_node
34
34
 
35
35
 
36
- def add_key_to_dict(
36
+ def append_key_to_dict(
37
37
  original_code: str, dictionary_name: str, new_key: str, new_value: str
38
38
  ) -> str:
39
39
  # Parse the original code into a module
@@ -21,7 +21,7 @@ class FunctionCallParamAdder(cst.CSTTransformer):
21
21
  return updated_node
22
22
 
23
23
 
24
- def add_param_to_function_call(
24
+ def append_param_to_function_call(
25
25
  original_code: str, func_name: str, new_param: str
26
26
  ) -> str:
27
27
  # Parse the original code into a module
@@ -1,4 +1,4 @@
1
- def add_code_to_module(source_code: str, new_code: str) -> str:
1
+ def prepend_code_to_module(source_code: str, new_code: str) -> str:
2
2
  lines = source_code.splitlines()
3
3
  last_import_index = -1
4
4
  for i, line in enumerate(lines):
@@ -22,7 +22,7 @@ class ParentClassAdder(cst.CSTTransformer):
22
22
  return updated_node
23
23
 
24
24
 
25
- def add_parent_to_class(
25
+ def prepend_parent_class(
26
26
  original_code: str, class_name: str, parent_class_name: str
27
27
  ) -> str:
28
28
  # Parse the original code into a module
@@ -33,7 +33,7 @@ class ClassPropertyAdder(cst.CSTTransformer):
33
33
  return updated_node
34
34
 
35
35
 
36
- def add_property_to_class(
36
+ def prepend_property_to_class(
37
37
  original_code: str,
38
38
  class_name: str,
39
39
  property_name: str,
zrb/util/file.py ADDED
@@ -0,0 +1,18 @@
1
+ import os
2
+
3
+
4
+ def read_file(file_path: str, replace_map: dict[str, str] = {}) -> str:
5
+ with open(file_path, "r", encoding="utf-8") as f:
6
+ content = f.read()
7
+ for key, val in replace_map.items():
8
+ content = content.replace(key, val)
9
+ return content
10
+
11
+
12
+ def write_file(file_path: str, content: str | list[str]):
13
+ if isinstance(content, list):
14
+ content = "\n".join([line for line in content if line is not None])
15
+ dir_path = os.path.dirname(file_path)
16
+ os.makedirs(dir_path, exist_ok=True)
17
+ with open(file_path, "w") as f:
18
+ f.write(content)
zrb/util/git_subtree.py CHANGED
@@ -5,6 +5,7 @@ from typing import Any
5
5
  from pydantic import BaseModel
6
6
 
7
7
  from zrb.util.cmd.command import run_command
8
+ from zrb.util.file import read_file, write_file
8
9
 
9
10
 
10
11
  class SingleSubTreeConfig(BaseModel):
@@ -21,14 +22,12 @@ def load_config(repo_dir: str) -> SubTreeConfig:
21
22
  file_path = os.path.join(repo_dir, "subtrees.json")
22
23
  if not os.path.exists(file_path):
23
24
  return SubTreeConfig(data={})
24
- with open(file_path, "r") as f:
25
- return SubTreeConfig.model_validate_json(f.read())
25
+ return SubTreeConfig.model_validate_json(read_file(file_path))
26
26
 
27
27
 
28
28
  def save_config(repo_dir: str, config: SubTreeConfig):
29
29
  file_path = os.path.join(repo_dir, "subtrees.json")
30
- with open(file_path, "w") as f:
31
- f.write(config.model_dump_json(indent=2))
30
+ write_file(file_path, config.model_dump_json(indent=2))
32
31
 
33
32
 
34
33
  async def add_subtree(
zrb/util/todo.py CHANGED
@@ -1,18 +1,17 @@
1
1
  import datetime
2
2
  import re
3
3
  import shutil
4
- from typing import Any
5
4
 
6
5
  from pydantic import BaseModel, Field, model_validator
7
6
 
8
7
  from zrb.util.cli.style import (
9
- stylize_bold_green,
10
8
  stylize_bold_yellow,
11
9
  stylize_cyan,
12
10
  stylize_faint,
13
11
  stylize_magenta,
14
12
  stylize_yellow,
15
13
  )
14
+ from zrb.util.file import read_file, write_file
16
15
  from zrb.util.string.name import get_random_name
17
16
 
18
17
 
@@ -87,8 +86,7 @@ def select_todo_task(
87
86
 
88
87
 
89
88
  def load_todo_list(todo_file_path: str) -> list[TodoTaskModel]:
90
- with open(todo_file_path, "r") as f:
91
- todo_lines = f.read().strip().split("\n")
89
+ todo_lines = read_file(todo_file_path).strip().split("\n")
92
90
  todo_list: list[TodoTaskModel] = []
93
91
  for todo_line in todo_lines:
94
92
  todo_line = todo_line.strip()
@@ -108,9 +106,9 @@ def load_todo_list(todo_file_path: str) -> list[TodoTaskModel]:
108
106
 
109
107
 
110
108
  def save_todo_list(todo_file_path: str, todo_list: list[TodoTaskModel]):
111
- with open(todo_file_path, "w") as f:
112
- for todo_task in todo_list:
113
- f.write(todo_task_to_line(todo_task) + "\n")
109
+ write_file(
110
+ todo_file_path, [todo_task_to_line(todo_task) for todo_task in todo_list]
111
+ )
114
112
 
115
113
 
116
114
  def line_to_todo_task(line: str) -> TodoTaskModel:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.0.0a16
3
+ Version: 1.0.0a17
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -18,7 +18,7 @@ Requires-Dist: autopep8 (>=2.0.4,<3.0.0)
18
18
  Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
19
19
  Requires-Dist: black (>=24.10.0,<24.11.0)
20
20
  Requires-Dist: chromadb (>=0.5.20,<0.6.0) ; extra == "rag"
21
- Requires-Dist: fastapi[standard] (>=0.115.5,<0.116.0)
21
+ Requires-Dist: fastapi[standard] (>=0.115.6,<0.116.0)
22
22
  Requires-Dist: isort (>=5.13.2,<5.14.0)
23
23
  Requires-Dist: libcst (>=1.5.0,<2.0.0)
24
24
  Requires-Dist: litellm (>=1.52.12,<2.0.0)