zrb 1.0.0a12__py3-none-any.whl → 1.0.0a14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zrb/builtin/project/add/fastapp.py +32 -17
- zrb/builtin/project/add/fastapp_template/_zrb/column/create_column_task.py +11 -0
- zrb/builtin/project/add/fastapp_template/_zrb/config.py +4 -4
- zrb/builtin/project/add/fastapp_template/_zrb/entity/create_entity_task.py +196 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/my_entity_usecase.py +66 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/factory.py +13 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/my_entity_db_repository.py +33 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/my_entity_repository.py +39 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/schema.template.py +29 -0
- zrb/builtin/project/add/fastapp_template/_zrb/group.py +9 -5
- zrb/builtin/project/add/fastapp_template/_zrb/helper.py +25 -11
- zrb/builtin/project/add/fastapp_template/_zrb/input.py +43 -0
- zrb/builtin/project/add/fastapp_template/_zrb/main.py +30 -21
- zrb/builtin/project/add/fastapp_template/_zrb/module/create_module_task.py +136 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/alembic.ini +117 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/api_client.py +6 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/direct_client.py +6 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/factory.py +9 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/README +1 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/env.py +108 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/script.py.mako +26 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/versions/3093c7336477_add_user_table.py +37 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration_metadata.py +3 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/route.py +19 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/run_module.template.py +26 -0
- zrb/builtin/project/add/fastapp_template/_zrb/venv_task.py +2 -5
- zrb/builtin/project/add/fastapp_template/common/app.py +3 -1
- zrb/builtin/project/add/fastapp_template/config.py +7 -7
- zrb/builtin/project/add/fastapp_template/module/auth/client/any_client.py +27 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +1 -1
- zrb/builtin/project/add/fastapp_template/module/auth/route.py +1 -1
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/{db_repository.py → user_db_repository.py} +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/{usecase.py → user_usecase.py} +15 -8
- zrb/builtin/project/add/fastapp_template/requirements.txt +5 -6
- zrb/builtin/project/add/fastapp_template/schema/permission.py +31 -0
- zrb/builtin/project/add/fastapp_template/template.env +2 -2
- zrb/builtin/project/create/create.py +2 -2
- zrb/builtin/project/create/project-template/zrb_init.py +0 -4
- zrb/builtin/todo.py +19 -2
- zrb/config.py +1 -2
- zrb/content_transformer/content_transformer.py +14 -2
- zrb/context/context.py +13 -10
- zrb/input/base_input.py +2 -1
- zrb/runner/cli.py +16 -5
- zrb/runner/web_app.py +3 -3
- zrb/runner/web_controller/task_ui/controller.py +8 -6
- zrb/session/session.py +4 -1
- zrb/task/scaffolder.py +7 -9
- zrb/util/cli/style.py +7 -0
- zrb/util/codemod/add_code_to_module.py +12 -0
- zrb/util/load.py +8 -9
- zrb/util/string/conversion.py +52 -0
- zrb/util/todo.py +2 -2
- {zrb-1.0.0a12.dist-info → zrb-1.0.0a14.dist-info}/METADATA +1 -1
- {zrb-1.0.0a12.dist-info → zrb-1.0.0a14.dist-info}/RECORD +65 -41
- /zrb/builtin/project/add/fastapp_template/{module/auth/client/base_client.py → _zrb/module/module_template/client/any_client.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/common/{db_repository.py → base_db_repository.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/common/{usecase.py → base_usecase.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/{repository.py → user_repository.py} +0 -0
- {zrb-1.0.0a12.dist-info → zrb-1.0.0a14.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a12.dist-info → zrb-1.0.0a14.dist-info}/entry_points.txt +0 -0
@@ -2,15 +2,15 @@ import os
|
|
2
2
|
|
3
3
|
APP_PATH = os.path.dirname(__file__)
|
4
4
|
|
5
|
-
APP_MODE = os.getenv("
|
5
|
+
APP_MODE = os.getenv("MY_APP_NAME_MODE", "monolith")
|
6
6
|
APP_MODULES = [
|
7
7
|
module.strip()
|
8
|
-
for module in os.getenv("
|
8
|
+
for module in os.getenv("MY_APP_NAME_MODULES", "").split(",")
|
9
9
|
if module.strip() != ""
|
10
10
|
]
|
11
|
-
APP_PORT = int(os.getenv("
|
11
|
+
APP_PORT = int(os.getenv("MY_APP_NAME_PORT", "3000"))
|
12
12
|
APP_COMMUNICATION = os.getenv(
|
13
|
-
"
|
13
|
+
"MY_APP_NAME_COMMUNICATION", "direct" if APP_MODE == "monolith" else "api"
|
14
14
|
)
|
15
15
|
APP_REPOSITORY_TYPE = os.getenv("APP_REPOSITORY_TYPE", "db")
|
16
16
|
APP_DB_URL = os.getenv(
|
@@ -21,9 +21,9 @@ APP_DB_URL = os.getenv(
|
|
21
21
|
else f"sqlite:///{APP_PATH}/{APP_MODULES[0]}_microservices.db"
|
22
22
|
),
|
23
23
|
)
|
24
|
-
APP_AUTH_SUPER_USER = os.getenv("
|
24
|
+
APP_AUTH_SUPER_USER = os.getenv("MY_APP_NAME_AUTH_SUPER_USER", "admin")
|
25
25
|
APP_AUTH_SUPER_USER_PASSWORD = os.getenv(
|
26
|
-
"
|
26
|
+
"MY_APP_NAME_AUTH_SUPER_USER_PASSWORD", "my-secure-password"
|
27
27
|
)
|
28
28
|
|
29
|
-
APP_AUTH_BASE_URL = os.getenv("
|
29
|
+
APP_AUTH_BASE_URL = os.getenv("MY_APP_NAME_AUTH_BASE_URL", "http://localhost:3001")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
|
4
|
+
|
5
|
+
|
6
|
+
class BaseClient(ABC):
|
7
|
+
@abstractmethod
|
8
|
+
async def get_user_by_id(self, user_id: str) -> UserResponse:
|
9
|
+
pass
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
async def get_all_users(self) -> list[UserResponse]:
|
13
|
+
pass
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
async def create_user(
|
17
|
+
self, data: UserCreate | list[UserCreate]
|
18
|
+
) -> UserResponse | list[UserResponse]:
|
19
|
+
pass
|
20
|
+
|
21
|
+
@abstractmethod
|
22
|
+
async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
|
23
|
+
pass
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
async def delete_user(self, user_id: str) -> UserResponse:
|
27
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from fastapp_template.config import APP_AUTH_BASE_URL
|
2
|
-
from fastapp_template.module.auth.client.
|
3
|
-
from fastapp_template.module.auth.service.user.
|
2
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
3
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
4
4
|
|
5
5
|
|
6
6
|
class APIClient(user_usecase.as_api_client(base_url=APP_AUTH_BASE_URL), BaseClient):
|
@@ -1,5 +1,5 @@
|
|
1
|
-
from fastapp_template.module.auth.client.
|
2
|
-
from fastapp_template.module.auth.service.user.
|
1
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
2
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
3
3
|
|
4
4
|
|
5
5
|
class DirectClient(user_usecase.as_direct_client(), BaseClient):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from fastapp_template.config import APP_COMMUNICATION
|
2
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
2
3
|
from fastapp_template.module.auth.client.api_client import APIClient
|
3
|
-
from fastapp_template.module.auth.client.base_client import BaseClient
|
4
4
|
from fastapp_template.module.auth.client.direct_client import DirectClient
|
5
5
|
|
6
6
|
if APP_COMMUNICATION == "direct":
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from fastapp_template.common.app import app
|
2
2
|
from fastapp_template.common.schema import BasicResponse
|
3
3
|
from fastapp_template.config import APP_MODE, APP_MODULES
|
4
|
-
from fastapp_template.module.auth.service.user.
|
4
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
5
5
|
|
6
6
|
if APP_MODE == "microservices" and "auth" in APP_MODULES:
|
7
7
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from fastapp_template.common.db_engine import engine
|
2
2
|
from fastapp_template.config import APP_REPOSITORY_TYPE
|
3
|
-
from fastapp_template.module.auth.service.user.repository.
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_db_repository import (
|
4
4
|
UserDBRepository,
|
5
5
|
)
|
6
|
-
from fastapp_template.module.auth.service.user.repository.
|
6
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
7
7
|
UserRepository,
|
8
8
|
)
|
9
9
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
from fastapp_template.common.
|
1
|
+
from fastapp_template.common.base_db_repository import BaseDBRepository
|
2
2
|
from fastapp_template.common.error import NotFoundError
|
3
|
-
from fastapp_template.module.auth.service.user.repository.
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
4
4
|
UserRepository,
|
5
5
|
)
|
6
6
|
from fastapp_template.schema.user import User, UserCreate, UserResponse, UserUpdate
|
zrb/builtin/project/add/fastapp_template/module/auth/service/user/{usecase.py → user_usecase.py}
RENAMED
@@ -1,21 +1,28 @@
|
|
1
|
-
from fastapp_template.common.
|
1
|
+
from fastapp_template.common.base_usecase import BaseUsecase
|
2
2
|
from fastapp_template.module.auth.service.user.repository.factory import user_repository
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
4
|
+
UserRepository,
|
5
|
+
)
|
3
6
|
from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
|
4
7
|
|
5
8
|
|
6
9
|
class UserUsecase(BaseUsecase):
|
7
10
|
|
11
|
+
def __init__(self, user_repository: UserRepository):
|
12
|
+
super().__init__()
|
13
|
+
self.user_repository = user_repository
|
14
|
+
|
8
15
|
@BaseUsecase.route(
|
9
16
|
"/api/v1/users/{user_id}", methods=["get"], response_model=UserResponse
|
10
17
|
)
|
11
18
|
async def get_user_by_id(self, user_id: str) -> UserResponse:
|
12
|
-
return await user_repository.get_by_id(user_id)
|
19
|
+
return await self.user_repository.get_by_id(user_id)
|
13
20
|
|
14
21
|
@BaseUsecase.route(
|
15
22
|
"/api/v1/users", methods=["get"], response_model=list[UserResponse]
|
16
23
|
)
|
17
24
|
async def get_all_users(self) -> list[UserResponse]:
|
18
|
-
return await user_repository.get_all()
|
25
|
+
return await self.user_repository.get_all()
|
19
26
|
|
20
27
|
@BaseUsecase.route(
|
21
28
|
"/api/v1/users",
|
@@ -26,20 +33,20 @@ class UserUsecase(BaseUsecase):
|
|
26
33
|
self, data: UserCreate | list[UserCreate]
|
27
34
|
) -> UserResponse | list[UserResponse]:
|
28
35
|
if isinstance(data, UserCreate):
|
29
|
-
return await user_repository.create(data)
|
30
|
-
return await user_repository.create_bulk(data)
|
36
|
+
return await self.user_repository.create(data)
|
37
|
+
return await self.user_repository.create_bulk(data)
|
31
38
|
|
32
39
|
@BaseUsecase.route(
|
33
40
|
"/api/v1/users/{user_id}", methods=["put"], response_model=UserResponse
|
34
41
|
)
|
35
42
|
async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
|
36
|
-
return await user_repository.update(user_id, data)
|
43
|
+
return await self.user_repository.update(user_id, data)
|
37
44
|
|
38
45
|
@BaseUsecase.route(
|
39
46
|
"/api/v1/users/{user_id}", methods=["delete"], response_model=UserResponse
|
40
47
|
)
|
41
48
|
async def delete_user(self, user_id: str) -> UserResponse:
|
42
|
-
return await user_repository.delete(user_id)
|
49
|
+
return await self.user_repository.delete(user_id)
|
43
50
|
|
44
51
|
|
45
|
-
user_usecase = UserUsecase()
|
52
|
+
user_usecase = UserUsecase(user_repository=user_repository)
|
@@ -1,6 +1,5 @@
|
|
1
|
-
fastapi[standard]
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
passlib==1.7.4
|
1
|
+
fastapi[standard]~=0.115.5
|
2
|
+
alembic~=1.14.0
|
3
|
+
sqlmodel~=0.0.22
|
4
|
+
ulid-py~=1.1.0
|
5
|
+
passlib~=1.7.4
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import datetime
|
2
|
+
|
3
|
+
import ulid
|
4
|
+
from sqlmodel import Field, SQLModel
|
5
|
+
|
6
|
+
|
7
|
+
class PermissionBase(SQLModel):
|
8
|
+
name: str
|
9
|
+
|
10
|
+
|
11
|
+
class PermissionCreate(PermissionBase):
|
12
|
+
description: str
|
13
|
+
|
14
|
+
|
15
|
+
class PermissionUpdate(SQLModel):
|
16
|
+
name: str | None = None
|
17
|
+
description: str | None = None
|
18
|
+
|
19
|
+
|
20
|
+
class PermissionResponse(PermissionBase):
|
21
|
+
id: str
|
22
|
+
|
23
|
+
|
24
|
+
class Permission(SQLModel, table=True):
|
25
|
+
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
26
|
+
created_at: datetime.datetime | None
|
27
|
+
created_by: str | None
|
28
|
+
updated_at: datetime.datetime | None
|
29
|
+
updated_by: str | None
|
30
|
+
name: str
|
31
|
+
description: str
|
@@ -1,2 +1,2 @@
|
|
1
|
-
export
|
2
|
-
export
|
1
|
+
export MY_APP_NAME_MODE=monolith
|
2
|
+
export MY_APP_NAME_PORT=3000
|
@@ -18,7 +18,7 @@ scaffold_project = Scaffolder(
|
|
18
18
|
default_str=lambda _: os.getcwd(),
|
19
19
|
),
|
20
20
|
StrInput(
|
21
|
-
name="project
|
21
|
+
name="project",
|
22
22
|
description="Project name",
|
23
23
|
prompt="Project name",
|
24
24
|
default_str=lambda ctx: os.path.basename(ctx.input["project-dir"]),
|
@@ -27,7 +27,7 @@ scaffold_project = Scaffolder(
|
|
27
27
|
source_path=os.path.join(_DIR, "project-template"),
|
28
28
|
render_source_path=False,
|
29
29
|
destination_path="{ctx.input['project-dir']}",
|
30
|
-
transform_content={"Project Name": "{ctx.input
|
30
|
+
transform_content={"Project Name": "{ctx.input.project.title()}"},
|
31
31
|
retries=0,
|
32
32
|
)
|
33
33
|
|
zrb/builtin/todo.py
CHANGED
@@ -41,11 +41,13 @@ from zrb.util.todo import (
|
|
41
41
|
name="project",
|
42
42
|
description="Task project",
|
43
43
|
prompt="Task project (space separated)",
|
44
|
+
allow_empty=True,
|
44
45
|
),
|
45
46
|
StrInput(
|
46
47
|
name="context",
|
47
48
|
description="Task context",
|
48
49
|
prompt="Task context (space separated)",
|
50
|
+
allow_empty=True,
|
49
51
|
),
|
50
52
|
],
|
51
53
|
description="➕ Add todo",
|
@@ -175,10 +177,12 @@ def archive_todo(ctx: AnyContext):
|
|
175
177
|
archive_file_path = os.path.join(TODO_DIR, "archive.txt")
|
176
178
|
if not os.path.isdir(TODO_DIR):
|
177
179
|
os.make_dirs(TODO_DIR, exist_ok=True)
|
180
|
+
# Get archived todo list
|
178
181
|
archived_todo_list = []
|
179
182
|
if os.path.isfile(archive_file_path):
|
180
183
|
archived_todo_list = load_todo_list(archive_file_path)
|
181
184
|
archived_todo_list += new_archived_todo_list
|
185
|
+
# Save the new todo list and add the archived ones
|
182
186
|
save_todo_list(archive_file_path, archived_todo_list)
|
183
187
|
save_todo_list(todo_file_path, working_todo_list)
|
184
188
|
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
@@ -241,9 +245,23 @@ def log_todo(ctx: AnyContext):
|
|
241
245
|
log_work.append(
|
242
246
|
{"log": ctx.input.log, "duration": ctx.input.duration, "start": ctx.input.start}
|
243
247
|
)
|
248
|
+
# save todo with log work
|
244
249
|
with open(log_work_file_path, "w") as f:
|
245
250
|
f.write(json.dumps(log_work, indent=2))
|
246
|
-
|
251
|
+
# get log work list
|
252
|
+
task_id = todo_task.keyval.get("id", "")
|
253
|
+
log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
|
254
|
+
log_work_list = []
|
255
|
+
if os.path.isfile(log_work_path):
|
256
|
+
with open(log_work_path, "r") as f:
|
257
|
+
log_work_list = json.loads(f.read())
|
258
|
+
return "\n".join(
|
259
|
+
[
|
260
|
+
get_visual_todo_list(todo_list, TODO_VISUAL_FILTER),
|
261
|
+
"",
|
262
|
+
get_visual_todo_card(todo_task, log_work_list),
|
263
|
+
]
|
264
|
+
)
|
247
265
|
|
248
266
|
|
249
267
|
def _get_default_start() -> str:
|
@@ -284,4 +302,3 @@ def _get_todo_txt_content() -> str:
|
|
284
302
|
return ""
|
285
303
|
with open(todo_file_path, "r") as f:
|
286
304
|
return f.read()
|
287
|
-
return f.read()
|
zrb/config.py
CHANGED
@@ -47,8 +47,7 @@ INIT_SCRIPTS = (
|
|
47
47
|
)
|
48
48
|
LOGGING_LEVEL = _get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
|
49
49
|
LOAD_BUILTIN = to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
|
50
|
-
|
51
|
-
SHOW_PROMPT = to_boolean(os.getenv("ZRB_SHOW_PROMPT", "1"))
|
50
|
+
SHOW_TIME = to_boolean(os.getenv("ZRB_SHOW_TIME", "1"))
|
52
51
|
WARN_UNRECOMMENDED_COMMAND = to_boolean(
|
53
52
|
os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1")
|
54
53
|
)
|
@@ -11,7 +11,10 @@ class ContentTransformer(AnyContentTransformer):
|
|
11
11
|
def __init__(
|
12
12
|
self,
|
13
13
|
match: list[str] | str | Callable[[AnyContext, str], bool],
|
14
|
-
transform:
|
14
|
+
transform: (
|
15
|
+
dict[str, str | Callable[[AnyContext], str]]
|
16
|
+
| Callable[[AnyContext, str], str]
|
17
|
+
),
|
15
18
|
auto_render: bool = True,
|
16
19
|
):
|
17
20
|
self._match = match
|
@@ -34,7 +37,7 @@ class ContentTransformer(AnyContentTransformer):
|
|
34
37
|
if callable(self._transform_file):
|
35
38
|
return self._transform_file(ctx, file_path)
|
36
39
|
transform_map = {
|
37
|
-
keyword:
|
40
|
+
keyword: self._get_str_replacement(ctx, replacement)
|
38
41
|
for keyword, replacement in self._transform_file.items()
|
39
42
|
}
|
40
43
|
with open(file_path, "r") as f:
|
@@ -43,3 +46,12 @@ class ContentTransformer(AnyContentTransformer):
|
|
43
46
|
content = content.replace(keyword, replacement)
|
44
47
|
with open(file_path, "w") as f:
|
45
48
|
f.write(content)
|
49
|
+
|
50
|
+
def _get_str_replacement(
|
51
|
+
self, ctx: AnyContext, replacement: str | Callable[[AnyContext], str]
|
52
|
+
) -> str:
|
53
|
+
if callable(replacement):
|
54
|
+
return replacement(ctx)
|
55
|
+
if self._auto_render:
|
56
|
+
return ctx.render(replacement)
|
57
|
+
return replacement
|
zrb/context/context.py
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
|
-
import re
|
4
3
|
import sys
|
5
4
|
from typing import Any, TextIO
|
6
5
|
|
6
|
+
from zrb.config import SHOW_TIME
|
7
7
|
from zrb.context.any_context import AnyContext
|
8
8
|
from zrb.context.any_shared_context import AnySharedContext
|
9
9
|
from zrb.dot_dict.dot_dict import DotDict
|
10
10
|
from zrb.session.any_session import AnySession
|
11
|
-
from zrb.util.cli.style import
|
11
|
+
from zrb.util.cli.style import (
|
12
|
+
remove_style,
|
13
|
+
stylize,
|
14
|
+
stylize_error,
|
15
|
+
stylize_log,
|
16
|
+
stylize_warning,
|
17
|
+
)
|
12
18
|
from zrb.util.string.conversion import to_boolean
|
13
19
|
|
14
20
|
|
15
|
-
def _remove_ansi_escape_sequences(text):
|
16
|
-
ansi_escape = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]")
|
17
|
-
return ansi_escape.sub("", text)
|
18
|
-
|
19
|
-
|
20
21
|
class Context(AnyContext):
|
21
22
|
def __init__(
|
22
23
|
self, shared_ctx: AnySharedContext, task_name: str, color: int, icon: str
|
@@ -111,10 +112,12 @@ class Context(AnyContext):
|
|
111
112
|
else:
|
112
113
|
attempt_status = f"{self._attempt}/{self._max_attempt}".ljust(5)
|
113
114
|
now = datetime.datetime.now()
|
114
|
-
formatted_time =
|
115
|
-
|
115
|
+
formatted_time = (
|
116
|
+
now.strftime("%y%m%d %H:%M:%S.%f")[:19] + " " if SHOW_TIME else ""
|
117
|
+
)
|
118
|
+
prefix = f"{formatted_time}{attempt_status} {padded_styled_task_name} ⬤ "
|
116
119
|
message = sep.join([f"{value}" for value in values])
|
117
|
-
self.append_to_shared_log(
|
120
|
+
self.append_to_shared_log(remove_style(f"{prefix} {message}"))
|
118
121
|
stylized_prefix = stylize(prefix, color=color)
|
119
122
|
print(f"{stylized_prefix} {message}", sep=sep, end=end, file=file, flush=flush)
|
120
123
|
|
zrb/input/base_input.py
CHANGED
@@ -66,7 +66,8 @@ class BaseInput(AnyInput):
|
|
66
66
|
default_value = self._get_default_str(shared_ctx)
|
67
67
|
if default_value != "":
|
68
68
|
prompt_message = f"{prompt_message} [{default_value}]"
|
69
|
-
|
69
|
+
print(f"{prompt_message}: ", end="")
|
70
|
+
value = input()
|
70
71
|
if value.strip() == "":
|
71
72
|
value = default_value
|
72
73
|
return value
|
zrb/runner/cli.py
CHANGED
@@ -82,13 +82,24 @@ class Cli(Group):
|
|
82
82
|
shared_ctx = SharedContext(args=args)
|
83
83
|
for task_input in task.inputs:
|
84
84
|
if task_input.name in str_kwargs:
|
85
|
-
|
86
|
-
|
85
|
+
# Update shared context for next input default value
|
86
|
+
task_input.update_shared_context(
|
87
|
+
shared_ctx, str_kwargs[task_input.name]
|
88
|
+
)
|
89
|
+
elif arg_index < len(args):
|
87
90
|
run_kwargs[task_input.name] = args[arg_index]
|
91
|
+
# Update shared context for next input default value
|
92
|
+
task_input.update_shared_context(
|
93
|
+
shared_ctx, run_kwargs[task_input.name]
|
94
|
+
)
|
88
95
|
arg_index += 1
|
89
|
-
|
90
|
-
|
91
|
-
|
96
|
+
else:
|
97
|
+
str_value = task_input.prompt_cli_str(shared_ctx)
|
98
|
+
run_kwargs[task_input.name] = str_value
|
99
|
+
# Update shared context for next input default value
|
100
|
+
task_input.update_shared_context(
|
101
|
+
shared_ctx, run_kwargs[task_input.name]
|
102
|
+
)
|
92
103
|
return run_kwargs
|
93
104
|
|
94
105
|
def _show_task_info(self, task: AnyTask):
|
zrb/runner/web_app.py
CHANGED
@@ -64,7 +64,7 @@ def create_app(root_group: AnyGroup, port: int = WEB_HTTP_PORT):
|
|
64
64
|
# Avoid capturing '/ui' itself
|
65
65
|
if not path:
|
66
66
|
raise HTTPException(status_code=404, detail="Not Found")
|
67
|
-
args = path.split("/")
|
67
|
+
args = path.strip("/").split("/")
|
68
68
|
node, node_path, residual_args = extract_node_from_args(root_group, args)
|
69
69
|
url = f"/ui/{'/'.join(node_path)}/"
|
70
70
|
if isinstance(node, AnyTask):
|
@@ -82,7 +82,7 @@ def create_app(root_group: AnyGroup, port: int = WEB_HTTP_PORT):
|
|
82
82
|
"""
|
83
83
|
Creating new session
|
84
84
|
"""
|
85
|
-
args = path.split("/")
|
85
|
+
args = path.strip("/").split("/")
|
86
86
|
node, _, residual_args = extract_node_from_args(root_group, args)
|
87
87
|
if isinstance(node, AnyTask):
|
88
88
|
session_name = residual_args[0] if residual_args else None
|
@@ -107,7 +107,7 @@ def create_app(root_group: AnyGroup, port: int = WEB_HTTP_PORT):
|
|
107
107
|
"""
|
108
108
|
Getting existing session or sessions
|
109
109
|
"""
|
110
|
-
args = path.split("/")
|
110
|
+
args = path.strip("/").split("/")
|
111
111
|
node, _, residual_args = extract_node_from_args(root_group, args)
|
112
112
|
if isinstance(node, AnyTask) and residual_args:
|
113
113
|
if residual_args[0] == "list":
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
|
3
|
+
from zrb.context.shared_context import SharedContext
|
3
4
|
from zrb.group.any_group import AnyGroup
|
4
5
|
from zrb.session.any_session import AnySession
|
5
6
|
from zrb.task.any_task import AnyTask
|
@@ -45,12 +46,13 @@ def handle_task_ui(
|
|
45
46
|
ui_url_parts = list(api_url_parts)
|
46
47
|
ui_url_parts[1] = "ui"
|
47
48
|
ui_url = "/".join(ui_url_parts)
|
48
|
-
|
49
|
-
|
49
|
+
# Assemble task inputs
|
50
|
+
input_html_list = []
|
51
|
+
for task_input in task.inputs:
|
52
|
+
task_input.update_shared_context(ctx)
|
53
|
+
input_html_list.append(
|
50
54
|
fstring_format(_TASK_INPUT_TEMPLATE, {"task_input": task_input, "ctx": ctx})
|
51
|
-
|
52
|
-
]
|
53
|
-
)
|
55
|
+
)
|
54
56
|
session_name = args[0] if len(args) > 0 else ""
|
55
57
|
return HTMLResponse(
|
56
58
|
fstring_format(
|
@@ -62,7 +64,7 @@ def handle_task_ui(
|
|
62
64
|
"root_description": root_group.description,
|
63
65
|
"url": url,
|
64
66
|
"parent_url": parent_url,
|
65
|
-
"task_inputs":
|
67
|
+
"task_inputs": "\n".join(input_html_list),
|
66
68
|
"api_url": api_url,
|
67
69
|
"ui_url": ui_url,
|
68
70
|
"main_script": _MAIN_SCRIPT,
|
zrb/session/session.py
CHANGED
@@ -28,6 +28,7 @@ from zrb.util.cli.style import (
|
|
28
28
|
ICONS,
|
29
29
|
MAGENTA,
|
30
30
|
YELLOW,
|
31
|
+
remove_style,
|
31
32
|
)
|
32
33
|
from zrb.util.group import get_node_path
|
33
34
|
from zrb.util.string.name import get_random_name
|
@@ -165,7 +166,9 @@ class Session(AnySession):
|
|
165
166
|
main_task_name=self._main_task.name,
|
166
167
|
path=self.task_path,
|
167
168
|
final_result=(
|
168
|
-
f"{self.final_result}"
|
169
|
+
remove_style(f"{self.final_result}")
|
170
|
+
if self.final_result is not None
|
171
|
+
else ""
|
169
172
|
),
|
170
173
|
finished=self.is_terminated,
|
171
174
|
log=self.shared_ctx.shared_log,
|
zrb/task/scaffolder.py
CHANGED
@@ -92,8 +92,8 @@ class Scaffolder(BaseTask):
|
|
92
92
|
destination_path = self._get_destination_path(ctx)
|
93
93
|
self._copy_path(ctx, source_path, destination_path)
|
94
94
|
transformers = self._get_content_transformers()
|
95
|
-
|
96
|
-
for file_path in
|
95
|
+
file_path_list = self._get_all_file_paths(destination_path)
|
96
|
+
for file_path in file_path_list:
|
97
97
|
for transformer in transformers:
|
98
98
|
if transformer.match(ctx, file_path):
|
99
99
|
try:
|
@@ -118,14 +118,10 @@ class Scaffolder(BaseTask):
|
|
118
118
|
dest_dir, self._transform_path(ctx, file_name)
|
119
119
|
)
|
120
120
|
shutil.copy2(src_file, dest_file)
|
121
|
-
ctx.log_info(f"Copied
|
121
|
+
ctx.log_info(f"Copied {src_file} to {dest_file}")
|
122
122
|
else:
|
123
|
-
|
124
|
-
|
125
|
-
self._transform_path(ctx, os.path.basename(source_path)),
|
126
|
-
)
|
127
|
-
shutil.copy2(source_path, dest_file)
|
128
|
-
ctx.log_info(f"Copied and renamed {source_path} to {dest_file}")
|
123
|
+
shutil.copy2(source_path, destination_path)
|
124
|
+
ctx.log_info(f"Copied {source_path} to {destination_path}")
|
129
125
|
|
130
126
|
def _transform_path(self, ctx: AnyContext, file_path: str):
|
131
127
|
if callable(self._path_transformer):
|
@@ -139,6 +135,8 @@ class Scaffolder(BaseTask):
|
|
139
135
|
"""
|
140
136
|
Returns a list of absolute file paths for all files in the given path, recursively.
|
141
137
|
"""
|
138
|
+
if os.path.isfile(path):
|
139
|
+
return [os.path.abspath(path)]
|
142
140
|
file_paths = []
|
143
141
|
for root, _, files in os.walk(path):
|
144
142
|
for file in files:
|
zrb/util/cli/style.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import re
|
2
|
+
|
1
3
|
BLACK = 30
|
2
4
|
RED = 31
|
3
5
|
GREEN = 32
|
@@ -118,6 +120,11 @@ ICONS = [
|
|
118
120
|
]
|
119
121
|
|
120
122
|
|
123
|
+
def remove_style(text):
|
124
|
+
ansi_escape = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]")
|
125
|
+
return ansi_escape.sub("", text)
|
126
|
+
|
127
|
+
|
121
128
|
def stylize(
|
122
129
|
text: str,
|
123
130
|
color: int | None = None,
|
@@ -0,0 +1,12 @@
|
|
1
|
+
def add_code_to_module(source_code: str, new_code: str) -> str:
|
2
|
+
lines = source_code.splitlines()
|
3
|
+
last_import_index = -1
|
4
|
+
for i, line in enumerate(lines):
|
5
|
+
stripped_line = line.strip()
|
6
|
+
if stripped_line.startswith("import") or stripped_line.startswith("from"):
|
7
|
+
last_import_index = i
|
8
|
+
elif stripped_line and not stripped_line.startswith("#"):
|
9
|
+
break
|
10
|
+
if last_import_index != -1:
|
11
|
+
lines.insert(last_import_index + 1, new_code)
|
12
|
+
return "\n".join(lines)
|
zrb/util/load.py
CHANGED
@@ -9,33 +9,33 @@ from typing import Any
|
|
9
9
|
pattern = re.compile("[^a-zA-Z0-9]")
|
10
10
|
|
11
11
|
|
12
|
-
def load_zrb_init(dir_path: str | None = None):
|
12
|
+
def load_zrb_init(dir_path: str | None = None) -> Any | None:
|
13
13
|
if dir_path is None:
|
14
14
|
dir_path = os.getcwd()
|
15
15
|
script_path = os.path.join(dir_path, "zrb_init.py")
|
16
16
|
if os.path.isfile(script_path):
|
17
|
-
load_file(script_path, -1)
|
18
|
-
return
|
17
|
+
return load_file(script_path, -1)
|
19
18
|
new_dir_path = os.path.dirname(dir_path)
|
20
19
|
if new_dir_path == dir_path:
|
21
20
|
return
|
22
|
-
load_zrb_init(new_dir_path)
|
21
|
+
return load_zrb_init(new_dir_path)
|
23
22
|
|
24
23
|
|
25
|
-
@lru_cache
|
26
|
-
def load_file(script_path: str, sys_path_index: int = 0):
|
24
|
+
@lru_cache
|
25
|
+
def load_file(script_path: str, sys_path_index: int = 0) -> Any | None:
|
27
26
|
if not os.path.isfile(script_path):
|
28
|
-
return
|
27
|
+
return None
|
28
|
+
module_name = pattern.sub("", script_path)
|
29
29
|
# Append script dir path
|
30
30
|
script_dir_path = os.path.dirname(script_path)
|
31
31
|
if script_dir_path not in sys.path:
|
32
32
|
sys.path.insert(sys_path_index, script_dir_path)
|
33
33
|
# Add script dir path to Python path
|
34
34
|
os.environ["PYTHONPATH"] = _get_new_python_path(script_dir_path)
|
35
|
-
module_name = pattern.sub("", script_path)
|
36
35
|
spec = importlib.util.spec_from_file_location(module_name, script_path)
|
37
36
|
module = importlib.util.module_from_spec(spec)
|
38
37
|
spec.loader.exec_module(module)
|
38
|
+
return module
|
39
39
|
|
40
40
|
|
41
41
|
def _get_new_python_path(dir_path: str) -> str:
|
@@ -47,7 +47,6 @@ def _get_new_python_path(dir_path: str) -> str:
|
|
47
47
|
return ":".join([current_python_path, dir_path])
|
48
48
|
|
49
49
|
|
50
|
-
@lru_cache()
|
51
50
|
def load_module(module_name: str) -> Any:
|
52
51
|
module = importlib.import_module(module_name)
|
53
52
|
return module
|