openbayes-cli 0.4.5__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 (55) hide show
  1. bayes/__init__.py +0 -0
  2. bayes/client/__init__.py +0 -0
  3. bayes/client/base.py +20 -0
  4. bayes/client/dataset_version_client.py +91 -0
  5. bayes/client/gear_client.py +468 -0
  6. bayes/client/job_run_client.py +504 -0
  7. bayes/client/job_upload_client.py +114 -0
  8. bayes/client/org_client.py +67 -0
  9. bayes/client/resource_client.py +108 -0
  10. bayes/client/runtime_client.py +32 -0
  11. bayes/client/ssh_client.py +66 -0
  12. bayes/client/status_client.py +14 -0
  13. bayes/client/user_client.py +58 -0
  14. bayes/commands/__init__.py +0 -0
  15. bayes/commands/gear.py +691 -0
  16. bayes/commands/org.py +100 -0
  17. bayes/commands/ssh.py +70 -0
  18. bayes/error.py +9 -0
  19. bayes/model/__init__.py +0 -0
  20. bayes/model/dataset_version.py +14 -0
  21. bayes/model/file/__init__.py +0 -0
  22. bayes/model/file/data_bindings.py +42 -0
  23. bayes/model/file/openbayes_data.py +133 -0
  24. bayes/model/file/openbayes_gear.py +72 -0
  25. bayes/model/file/openbayes_ignore.py +75 -0
  26. bayes/model/file/openbayes_yaml.py +180 -0
  27. bayes/model/file/settings.py +157 -0
  28. bayes/model/party.py +35 -0
  29. bayes/model/resource.py +147 -0
  30. bayes/model/runtime.py +34 -0
  31. bayes/root.py +170 -0
  32. bayes/templates/openbayes_zh-Hans.yaml +134 -0
  33. bayes/templates/openbayesignore.yaml +3 -0
  34. bayes/usercases/__init__.py +0 -0
  35. bayes/usercases/archive_usecase.py +79 -0
  36. bayes/usercases/auth_usecase.py +35 -0
  37. bayes/usercases/dataset_version_usecase.py +61 -0
  38. bayes/usercases/disk_usecase.py +98 -0
  39. bayes/usercases/gear_download_usecese.py +113 -0
  40. bayes/usercases/gear_logs_usecase.py +44 -0
  41. bayes/usercases/gear_open_usecase.py +16 -0
  42. bayes/usercases/gear_run_usecase.py +123 -0
  43. bayes/usercases/gear_upload_usecase.py +248 -0
  44. bayes/usercases/gear_usecase.py +301 -0
  45. bayes/usercases/openbayes_data_usecase.py +73 -0
  46. bayes/usercases/org_usecase.py +85 -0
  47. bayes/usercases/resource_usecase.py +74 -0
  48. bayes/usercases/runtime_usecase.py +58 -0
  49. bayes/usercases/ssh_usecase.py +110 -0
  50. bayes/usercases/switch_usercase.py +19 -0
  51. bayes/utils.py +163 -0
  52. openbayes_cli-0.4.5.dist-info/METADATA +62 -0
  53. openbayes_cli-0.4.5.dist-info/RECORD +55 -0
  54. openbayes_cli-0.4.5.dist-info/WHEEL +4 -0
  55. openbayes_cli-0.4.5.dist-info/entry_points.txt +3 -0
bayes/commands/org.py ADDED
@@ -0,0 +1,100 @@
1
+ from typing import Optional
2
+
3
+ import typer
4
+
5
+ from bayes.model.file.settings import BayesEnvConfig
6
+ from bayes.usercases import org_usecase
7
+ from bayes.usercases import auth_usecase
8
+
9
+
10
+ app = typer.Typer()
11
+
12
+
13
+ @app.command()
14
+ def switch(name: str, ctx: typer.Context):
15
+ """
16
+ 组织切换
17
+ 用法:
18
+ bayes org switch [组织名] [选项]
19
+
20
+ 可用选项:
21
+ -h, --help 查看 switch 的帮助
22
+
23
+ """
24
+ # 先检查登陆状态
25
+ login = auth_usecase.check_login()
26
+ if not login:
27
+ print("尚未授权,请先登录")
28
+ raise typer.Exit(code=1)
29
+
30
+ # 检查用户是否尝试切换回用户模式(组织名称是否与用户名相同,忽略大小写)
31
+ bayes_settings = ctx.obj
32
+ default_env: Optional[BayesEnvConfig] = bayes_settings.default_env
33
+ switch_back_to_user = str.lower(name) == str.lower(default_env.username)
34
+
35
+ # 如果不是切换回用户模式,则检查组织是否存在。如果组织不存在,打印相应的错误信息并退出程序
36
+ if not switch_back_to_user:
37
+ # 看这个用户下是否有这个组织(这个地方涉及到一个权限的问题,管理员能在命令行切换到其他人的组织吗?)
38
+ if not org_usecase.user_contains_org(default_env.username, name):
39
+ print("请输入当前登录用户下正确的组织名称")
40
+ raise typer.Exit(code=1)
41
+ # 切换到指定的组织
42
+ result = bayes_settings.switch_org(name)
43
+ if result:
44
+ print(f"已成功切换到组织 {name}")
45
+ raise typer.Exit()
46
+ else:
47
+ result = bayes_settings.switch_user(name)
48
+ if result:
49
+ print(f"已成功切换到个人账号 {name}")
50
+
51
+
52
+ @app.command()
53
+ def status(ctx: typer.Context):
54
+ """
55
+ 组织信息
56
+
57
+ 用法:
58
+ bayes org status [选项]
59
+
60
+ 可用选项:
61
+ -h, --help 查看 status 的帮助
62
+
63
+ 使用用例:
64
+ bayes org status
65
+ """
66
+ bayes_settings = ctx.obj
67
+ default_env: Optional[BayesEnvConfig] = bayes_settings.default_env
68
+
69
+ userinfo = auth_usecase.get_default_credential_userinfo()
70
+ if userinfo is not None:
71
+ print(f"当前环境: {default_env.endpoint}")
72
+ print(f"当前组织: {default_env.orgName}")
73
+ print(f"用户名: {default_env.username}")
74
+ print(f"邮箱: {userinfo.email}")
75
+ else:
76
+ print(f"当前环境: {default_env.endpoint}")
77
+
78
+
79
+ @app.command()
80
+ def ls(ctx: typer.Context):
81
+ """
82
+ 查看所属组织
83
+
84
+ 用法:
85
+ bayes org ls [选项]
86
+
87
+ 可用选项:
88
+ -h, --help 查看 ls 的帮助
89
+ """
90
+ # 先检查登陆状态
91
+ login = auth_usecase.check_login()
92
+ if not login:
93
+ print("尚未授权,请先登录")
94
+ raise typer.Exit(code=1)
95
+
96
+ bayes_settings = ctx.obj
97
+ default_env: Optional[BayesEnvConfig] = bayes_settings.default_env
98
+
99
+ user_orgs_data = org_usecase.list_user_orgs(default_env.username)
100
+ org_usecase.list_display_table(user_orgs_data)
bayes/commands/ssh.py ADDED
@@ -0,0 +1,70 @@
1
+ import os
2
+
3
+ import typer
4
+
5
+ from bayes.usercases import auth_usecase, ssh_usecase
6
+
7
+ app = typer.Typer()
8
+
9
+
10
+ @app.command()
11
+ def create(ctx: typer.Context):
12
+ """
13
+ 创建新的 SSH key
14
+ 用法:
15
+ bayes ssh create [选项]
16
+
17
+ 可用选项:
18
+ -h, --help 查看 create 的帮助
19
+
20
+ """
21
+ # 检查用户是否已登录
22
+ login = auth_usecase.check_login()
23
+ if not login:
24
+ print("尚未授权,请先登录")
25
+ raise typer.Exit(code=1)
26
+
27
+ print("正在检查 SSH 公钥,请稍候...")
28
+
29
+ isExist, err = ssh_usecase.is_finger_print_exist()
30
+ if isExist:
31
+ print("OpenBayes 服务器中已存在此设备的公钥")
32
+ raise typer.Exit(code=1)
33
+
34
+ print("OpenBayes 服务器中不存在关于此设备的公钥")
35
+
36
+ passphrase = typer.prompt(f"请输入 SSH key 的密码:", hide_input=True)
37
+ err = ssh_usecase.create_key(passphrase)
38
+ if err is not None:
39
+ raise typer.Exit(code=1)
40
+
41
+
42
+ @app.command()
43
+ def upload(ctx: typer.Context,
44
+ pub_key_path: str,
45
+ name: str = typer.Option("", "-n", "--name", help="SSH 名字,不填则默认使用 hostname")):
46
+ """
47
+ 上传 SSH 公钥
48
+
49
+ 用法:
50
+ bayes ssh upload [公钥] [选项]
51
+
52
+ 可用选项:
53
+ -h, --help 查看 upload 的帮助
54
+ -n, --name string [可选] SSH 名字,不填则默认使用 hostname
55
+ """
56
+ # 检查用户是否已登录
57
+ login = auth_usecase.check_login()
58
+ if not login:
59
+ print("尚未授权,请先登录")
60
+ raise typer.Exit(code=1)
61
+
62
+ try:
63
+ os.stat(pub_key_path)
64
+ except FileNotFoundError:
65
+ print(f"路径 {pub_key_path} 不存在")
66
+ raise typer.Exit(code=1)
67
+
68
+ err = ssh_usecase.upload_key(name, pub_key_path)
69
+ if err is not None:
70
+ raise typer.Exit(code=1)
bayes/error.py ADDED
@@ -0,0 +1,9 @@
1
+
2
+ class Error(Exception):
3
+ def __init__(self, message: str):
4
+ self.message = message
5
+ super().__init__(self.message)
6
+
7
+
8
+ def credential_notfound_error(name: str) -> Exception:
9
+ return Exception(f"配置名 '{name}' 不存在, 使用 bayes switch '{name}' -e {{endpoint}} 设置")
File without changes
@@ -0,0 +1,14 @@
1
+ from datetime import datetime
2
+ from typing import List
3
+
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class DatasetVersion(BaseModel):
8
+ semanticBindingName: str
9
+ createdAt: datetime
10
+
11
+
12
+ class PublicDatasetVersions(BaseModel):
13
+ data: List[DatasetVersion]
14
+
File without changes
@@ -0,0 +1,42 @@
1
+ from typing import List, Optional
2
+ from pydantic import BaseModel, Field
3
+ import yaml
4
+ from pathlib import Path
5
+ import typer
6
+
7
+
8
+ class OpenBayesDataBinding(BaseModel):
9
+ data: str
10
+ name: Optional[str] = None
11
+ path: str
12
+ type: str
13
+
14
+
15
+ class DataBindings(BaseModel):
16
+ data_binding: Optional[List[OpenBayesDataBinding]] = Field(None)
17
+ binding: Optional[List[str]] = Field(None)
18
+ data_bindings: List[OpenBayesDataBinding] = Field(default_factory=list)
19
+ bindings: Optional[List[str]] = Field(None)
20
+
21
+ def get_data_bindings(self) -> List[OpenBayesDataBinding]:
22
+ if self.data_bindings:
23
+ return self.data_bindings
24
+ if self.data_binding:
25
+ return self.data_binding
26
+ if self.bindings:
27
+ binding_list = []
28
+ for b in self.bindings:
29
+ parts = b.split(":")
30
+ data_binding = OpenBayesDataBinding(
31
+ data=parts[0],
32
+ path=parts[1],
33
+ type=parts[2] if len(parts) > 2 else "ro"
34
+ )
35
+ binding_list.append(data_binding)
36
+ return binding_list
37
+ return []
38
+
39
+ def get_bindings(self) -> List[str]:
40
+ if self.bindings:
41
+ return self.bindings
42
+ return self.binding or []
@@ -0,0 +1,133 @@
1
+ from enum import Enum
2
+ from pathlib import Path
3
+
4
+ import yaml
5
+ from pydantic import BaseModel, Field
6
+ from typing import List, Optional, Tuple
7
+ import os
8
+
9
+ from bayes.model.file.settings import BayesEnvConfig, BayesSettings
10
+ from bayes.utils import Utils
11
+
12
+ DATA_FILE_NAME = ".openbayesdata"
13
+
14
+
15
+ class OpenBayesDataType(str, Enum):
16
+ DATASET = "DATASET"
17
+ CODE = "CODE"
18
+
19
+
20
+ class OpenBayesData(BaseModel):
21
+ user: str
22
+ version: str
23
+ pid: Optional[str] = None # project_id
24
+ did: Optional[str] = None # dataset_id
25
+ path: str # data_file_path
26
+ location: str = ""
27
+ zip: str = "" # zip_path
28
+ length: int = 0 # upload_length
29
+ token: str = ""
30
+
31
+ def has_last_upload(self) -> bool:
32
+ if self.location and self.token and self.zip:
33
+ try:
34
+ os.stat(self.zip)
35
+ return True
36
+ except FileNotFoundError as e:
37
+ print(f"zip_path:{self.zip} not found")
38
+ return False
39
+ return False
40
+
41
+ def has_last_archived_upload(self, zip_path: str) -> bool:
42
+ if self.location and self.token and self.zip == zip_path:
43
+ return self.has_last_upload()
44
+ return False
45
+
46
+ def update_upload(
47
+ self, location: str, token: str, zip_path: str, upload_length: int
48
+ ):
49
+ self.location = location
50
+ self.zip = zip_path
51
+ self.length = upload_length
52
+ self.token = token
53
+
54
+ def update_version(self, version: str):
55
+ self.version = version
56
+
57
+
58
+ def empty_openbayes_datas() -> List[OpenBayesData]:
59
+ return []
60
+
61
+
62
+ def new_openbayes_dataset_data(did: str, data_file_path: str) -> OpenBayesData:
63
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
64
+ cli_version = Utils.get_version_from_pyproject_toml()
65
+ return OpenBayesData(
66
+ user=default_env.username, version=cli_version, did=did, path=data_file_path
67
+ )
68
+
69
+
70
+ def new_openbayes_code_data(pid: str, data_file_path: str) -> OpenBayesData:
71
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
72
+ cli_version = Utils.get_version_from_pyproject_toml()
73
+ return OpenBayesData(
74
+ user=default_env.username, version=cli_version, pid=pid, path=data_file_path
75
+ )
76
+
77
+
78
+ class OpenBayesDataSettings:
79
+ def __init__(self, config_path: Optional[Path] = None):
80
+ self.config_path: Path = Path(config_path) if isinstance(config_path, str) else config_path or Path(DATA_FILE_NAME)
81
+ if self.config_path.is_dir():
82
+ self.config_path = self.config_path / DATA_FILE_NAME
83
+ self.configuration: List[OpenBayesData] = []
84
+ self.load_from_file()
85
+
86
+ def load_from_file(self):
87
+ if self.config_path.exists():
88
+ with self.config_path.open("r") as f:
89
+ config_data = yaml.safe_load(f)
90
+ self.configuration = [OpenBayesData(**data) for data in config_data] if config_data else []
91
+
92
+ def save_to_file(self):
93
+ with self.config_path.open("w") as f:
94
+ yaml.dump([item.model_dump() for item in self.configuration], f, default_flow_style=False)
95
+
96
+ def update_or_add_data(self, new_data: OpenBayesData):
97
+ for i, data in enumerate(self.configuration):
98
+ if data.user == new_data.user and ((data.pid and data.pid == new_data.pid) or (data.did and data.did == new_data.did)):
99
+ self.configuration[i] = new_data
100
+ return
101
+ self.configuration.append(new_data)
102
+
103
+ def get_data_by_user_and_id(self, username: str, id: str) -> Optional[OpenBayesData]:
104
+ for data in self.configuration:
105
+ if data.user == username and ((data.pid and data.pid == id) or (data.did and data.did == id)):
106
+ return data
107
+ return None
108
+
109
+ def remove_data_by_user_and_id(self, username: str, id: str):
110
+ self.configuration = [
111
+ data for data in self.configuration
112
+ if not (data.user == username and ((data.pid and data.pid == id) or (data.did and data.did == id)))
113
+ ]
114
+
115
+ def write(self, new_data: OpenBayesData):
116
+ self.update_or_add_data(new_data)
117
+ self.save_to_file()
118
+
119
+ def read_by_cur_user(self, id: str) -> Tuple[Optional[OpenBayesData], Optional[Exception]]:
120
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
121
+ data = self.get_data_by_user_and_id(default_env.username, id)
122
+ if data:
123
+ return data, None
124
+ return None, FileNotFoundError("No matching upload configuration found")
125
+
126
+ def remove_by_cur_user(self, id: str) -> Optional[Exception]:
127
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
128
+ self.remove_data_by_user_and_id(default_env.username, id)
129
+ try:
130
+ self.save_to_file()
131
+ except Exception as e:
132
+ return e
133
+ return None
@@ -0,0 +1,72 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ import yaml
6
+ from pydantic import BaseModel
7
+
8
+ FILE_NAME = ".openbayesgear"
9
+
10
+
11
+ class OpenBayesGear(BaseModel):
12
+ id: str
13
+ name: str
14
+ jid: Optional[str] = ""
15
+
16
+
17
+ class OpenBayesGearSettings(BaseModel):
18
+ configuration: Optional[OpenBayesGear] = None
19
+ config_path: Optional[Path] = None
20
+
21
+ def __init__(self, config_path: Optional[Path] = None, **kwargs):
22
+ super().__init__(**kwargs)
23
+ self.config_path = config_path or Path(FILE_NAME)
24
+ self.load_from_file()
25
+
26
+ def load_from_file(self):
27
+ if self.config_path.exists():
28
+ with self.config_path.open("r") as f:
29
+ config_data = yaml.safe_load(f)
30
+ if config_data:
31
+ self.configuration = OpenBayesGear(**config_data)
32
+ else:
33
+ self.configuration = None
34
+
35
+ def save_to_file(self):
36
+ with self.config_path.open("w") as f:
37
+ if self.configuration:
38
+ yaml.dump(self.configuration.model_dump(), f, default_flow_style=False)
39
+ else:
40
+ f.write("")
41
+
42
+ def create_or_update(self, directory: str, pid: str, project_name: str) -> None:
43
+ directory_path = Path(directory)
44
+ file_path = directory_path / FILE_NAME
45
+
46
+ os.makedirs(directory_path, exist_ok=True)
47
+
48
+ self.config_path = file_path
49
+ self.configuration = OpenBayesGear(id=pid, name=project_name)
50
+
51
+ self.save_to_file()
52
+
53
+ def read_from_file(self, path: str) -> OpenBayesGear:
54
+ with open(path, "r") as f:
55
+ config_data = yaml.safe_load(f)
56
+ if config_data:
57
+ return OpenBayesGear(**config_data)
58
+ else:
59
+ raise FileNotFoundError(f"No config data found in {path}")
60
+
61
+ def update_jid(self, directory: str, jid: str) -> None:
62
+ path = Path(directory) / FILE_NAME
63
+
64
+ gear = self.read_from_file(str(path))
65
+
66
+ gear.jid = jid
67
+ self.configuration = gear
68
+
69
+ try:
70
+ self.save_to_file()
71
+ except Exception as e:
72
+ raise RuntimeError(f"Error saving to file: {e}")
@@ -0,0 +1,75 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+ from pathlib import Path
4
+ import os
5
+ import yaml
6
+
7
+ IGNORE_FILE_NAME = ".openbayesignore"
8
+ TEMPLATE_PATH = "bayes/templates/openbayesignore.yaml"
9
+ # 定义忽略的文件和目录模式
10
+ IGNORE_CLEANUPS = [
11
+ "__MACOSX",
12
+ "*.rsrc",
13
+ ".openbayesdata",
14
+ ".openbayesgear",
15
+ ".openbayesignore",
16
+ ".DS_Store",
17
+ "Desktop.ini",
18
+ "desktop.ini",
19
+ "._*",
20
+ "Thumbs.db",
21
+ ".Spotlight-V100",
22
+ ".Trashes",
23
+ ".VolumeIcon.icns",
24
+ "$RECYCLE.BIN",
25
+ "$Recycle.Bin",
26
+ ]
27
+
28
+
29
+ class OpenBayesIgnore(BaseModel):
30
+ message: Optional[str] = None
31
+
32
+
33
+ class OpenBayesIgnoreSettings(BaseModel):
34
+ configuration: Optional[OpenBayesIgnore] = None
35
+ config_path: Optional[Path] = None
36
+
37
+ def __init__(self, config_path: Optional[Path] = None, **kwargs):
38
+ super().__init__(**kwargs)
39
+ self.config_path = config_path or Path(IGNORE_FILE_NAME)
40
+ self.load_or_create_default_yaml()
41
+
42
+ def load_or_create_default_yaml(self):
43
+ if self.config_path.exists():
44
+ self.load_from_yaml()
45
+ else:
46
+ self.create_default(self.config_path.parent)
47
+
48
+ def load_from_yaml(self):
49
+ with open(self.config_path, "r", encoding='utf-8') as f:
50
+ message = f.read()
51
+ self.configuration = OpenBayesIgnore(message=message)
52
+
53
+ def save_to_yaml(self):
54
+ with open(self.config_path, "w", encoding='utf-8') as f:
55
+ f.write(self.configuration.message)
56
+
57
+ @staticmethod
58
+ def read_template(template_path):
59
+ with open(template_path, 'r', encoding='utf-8') as file:
60
+ return file.read()
61
+
62
+ def create_default(self, directory):
63
+ # 读取模板内容
64
+ template_content = self.read_template(TEMPLATE_PATH)
65
+
66
+ # 确保目录存在
67
+ os.makedirs(directory, exist_ok=True)
68
+ file_path = directory / IGNORE_FILE_NAME
69
+
70
+ # 写入模板内容到文件
71
+ with open(file_path, 'w', encoding='utf-8') as file:
72
+ file.write(template_content)
73
+
74
+ # 加载文件内容为配置
75
+ self.configuration = OpenBayesIgnore(message=template_content)