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/root.py ADDED
@@ -0,0 +1,170 @@
1
+ from typing import Optional
2
+
3
+ import typer
4
+
5
+ from .client import user_client
6
+ from .client.base import BayesGQLClient
7
+ from .commands import gear, ssh
8
+ from .commands import org
9
+ from .error import credential_notfound_error, Error
10
+ from bayes.model.file.settings import BayesSettings, BayesEnvConfig
11
+ from bayes.usercases.switch_usercase import clean, is_url, is_exist
12
+ from .usercases import auth_usecase
13
+ from .utils import Utils
14
+
15
+ app = typer.Typer(
16
+ help="OpenBayes CLI",
17
+ context_settings={"help_option_names": ["-h", "--help"]}
18
+ )
19
+
20
+ app.add_typer(ssh.app, name="ssh")
21
+ app.add_typer(gear.app, name="gear")
22
+ app.add_typer(org.app, name="org")
23
+
24
+
25
+ @app.callback()
26
+ def main(ctx: typer.Context):
27
+ ctx.obj = BayesSettings()
28
+
29
+
30
+ @app.command()
31
+ def login(ctx: typer.Context,
32
+ username: str = typer.Argument(
33
+ None, envvar="OPENBAYES_TOKEN", help="用户名 | 令牌"
34
+ )):
35
+ """
36
+ 登录
37
+
38
+ 用法:
39
+ bayes login [用户名 | 令牌] [选项]
40
+
41
+ 可用选项:
42
+ -h, --help 查看 login 的帮助
43
+ """
44
+ # 使用账户密码登录时:当用户名为 null 的时候要去 credentials 文件中看看 username 是否为null,因为登出的时候 username 是不覆盖为None
45
+ # 如果 username is None,让用户输入,否则直接显示 请输入 xxx 的密码
46
+ bayes_settings = ctx.obj
47
+ default_env: Optional[BayesEnvConfig] = bayes_settings.default_env
48
+ if default_env.token:
49
+ print(f"{default_env.username} 已成功登入 {default_env.name}")
50
+ raise typer.Exit()
51
+
52
+ if username is None:
53
+ env_username = default_env.username
54
+ if env_username:
55
+ username = env_username
56
+ else:
57
+ username = input("请输入用户名:")
58
+
59
+ gql_client = BayesGQLClient(default_env.graphQL, None)
60
+
61
+ if len(username) > 40:
62
+ try:
63
+ result = user_client.login_with_token(gql_client, username)
64
+ bayes_settings.login(result.username, result.token)
65
+
66
+ print(f"{result.username} 已成功登入 {default_env.name}")
67
+ raise typer.Exit()
68
+ except Error as e:
69
+ print(e.message)
70
+ raise typer.Exit(code=1)
71
+ else:
72
+ password = typer.prompt(f"请输入 {username} 的密码", hide_input=True)
73
+ try:
74
+ result = user_client.login(gql_client, username, password)
75
+ bayes_settings.login(result.username, result.token)
76
+
77
+ print(f"{result.username} 已成功登入 {default_env.name}")
78
+ raise typer.Exit()
79
+ except Error as e:
80
+ print(e.message)
81
+ raise typer.Exit(code=1)
82
+
83
+
84
+ @app.command()
85
+ def logout(ctx: typer.Context):
86
+ """
87
+ 注销
88
+
89
+ 用法:
90
+ bayes logout [选项]
91
+
92
+ 可用选项:
93
+ -h, --help 查看 logout 的帮助
94
+ """
95
+ bayes_settings = ctx.obj
96
+ logout_result = bayes_settings.logout()
97
+ if logout_result:
98
+ print("已成功登出")
99
+ else:
100
+ print("登出失败")
101
+
102
+
103
+ @app.command()
104
+ def switch(ctx: typer.Context, name: str,
105
+ endpoint: str = typer.Option(None, "--endpoint", "-e", help="填入 endpoint")):
106
+ """
107
+ 切换服务端环境
108
+
109
+ 用法:
110
+ bayes switch [配置名] [选项]
111
+
112
+ 可用选项:
113
+ -e, --endpoint string [可选] 填入 endpoint
114
+ -h, --help 查看 switch 的帮助
115
+ """
116
+
117
+ if name == "default" and endpoint:
118
+ print("配置名 default 已存在,且不能被修改,请选择其他配置名")
119
+ raise typer.Exit(code=1)
120
+
121
+ bayes_settings = ctx.obj
122
+
123
+ if endpoint:
124
+ if not is_url(endpoint):
125
+ print(f"{endpoint} 不是合法的链接")
126
+ raise typer.Exit(code=1)
127
+
128
+ # 临时创建一个新的环境,然后判断是否可以访问
129
+ new_env = BayesEnvConfig(name=name, endpoint=clean(endpoint))
130
+ if not is_exist(new_env.graphQL):
131
+ print(f"{endpoint} 无法访问,请再次确认你所输入的链接")
132
+ raise typer.Exit(code=1)
133
+
134
+ bayes_settings.add_new_env(name, new_env.endpoint)
135
+
136
+ switch_result = bayes_settings.switch_default_env(name)
137
+ if switch_result:
138
+ print(f"已成功切换到 {name}")
139
+ else:
140
+ error = credential_notfound_error(name)
141
+ print(error)
142
+
143
+
144
+ @app.command()
145
+ def status(ctx: typer.Context):
146
+ """
147
+ 登录信息
148
+
149
+ 用法:
150
+ bayes status [选项]
151
+
152
+ 可用选项:
153
+ -h, --help 查看 status 的帮助
154
+ """
155
+ settings: BayesSettings = ctx.obj
156
+ default_env: Optional[BayesEnvConfig] = settings.default_env
157
+ userinfo = auth_usecase.get_default_credential_userinfo()
158
+ if userinfo is not None:
159
+ print(f"当前环境: {default_env.endpoint}")
160
+ print(f"当前组织: {default_env.orgName}")
161
+ print(f"用户名: {default_env.username}")
162
+ print(f"邮箱: {userinfo.email}")
163
+ else:
164
+ print(f"当前环境: {default_env.endpoint}")
165
+
166
+
167
+ @app.command()
168
+ def version(ctx: typer.Context):
169
+ version_number = Utils.get_version_from_pyproject_toml()
170
+ print(f"openbayes 的版本号为:{version_number}")
@@ -0,0 +1,134 @@
1
+ ## 有关「 OpenBayes 配置文件」的最新说明,请查阅 https://openbayes.com/docs/cli/config-file/
2
+
3
+ ## data_bindings
4
+ # 指绑定的数据,支持「容器输出」以及「数据集」,最多同时绑定三个
5
+ #
6
+ # 一个完整的 data_bindings 样例如下:
7
+ #
8
+ # data_bindings:
9
+ # - data: openbayes/mnist/1
10
+ # path: /input0
11
+ # type: ro
12
+ # - data: openbayes/jobs/jfaqJeLMcPM/output
13
+ # path: output
14
+ # type: rw
15
+ #
16
+ # 亦可将 data_bindings 替换成 bindings, 简写成如下样例:
17
+ #
18
+ # bindings:
19
+ # - openbayes/mnist/1:/input0
20
+ # - openbayes/mnist/1:/input1:rw
21
+ # - openbayes/jobs/jfaqJeLMcPM/output:/output
22
+ #
23
+ data_bindings: []
24
+
25
+ ## resource
26
+ # 指使用什么算力容器,通过命令 bayes gear resource 可以看到支持的算力类型
27
+ #
28
+ resource: "{resource}"
29
+
30
+ ## env
31
+ # 指使用什么运行时环境,通过命令 bayes gear env 可以查看支持的运行时环境
32
+ #
33
+ env: "{env}"
34
+
35
+ ## command
36
+ # 只有在创建「脚本执行」时需要,指任务执行时的入口命令
37
+ #
38
+ command: ""
39
+
40
+ ## node
41
+ # 指定运行节点数量
42
+ #
43
+ node: 1
44
+
45
+ ## parameters
46
+ # 支持 key / value 形式的参数,该参数会在容器执行时生成 openbayes_params.json 并补充在 command 参数后面
47
+ # 样例如下:
48
+ #
49
+ # parameters:
50
+ # input: /input0
51
+ # epochs: 5
52
+ #
53
+ # 在执行时会生成一个内容为 {"input": "/input0", "epochs": 5} 的 openbayes_params.json,
54
+ # 并且会在执行命令后面追加 `--input=/input0 --epochs=5`
55
+ #
56
+ parameters: {}
57
+
58
+
59
+ ## 有关「 OpenBayes 自动调参」的最新说明,请查阅 https://openbayes.com/docs/hypertuning/
60
+ #
61
+ # 一个完整的 hyper_tuning 样例如下:
62
+ # hyper_tuning:
63
+ # max_job_count: 3
64
+ # hyperparameter_metric: precision
65
+ # goal: MINIMIZE
66
+ # algorithm: Bayesian
67
+ # parameter_specs:
68
+ # - name: regularization
69
+ # type: DOUBLE
70
+ # min_value: 0.001
71
+ # max_value: 10.0
72
+ # scale_type: UNIT_LOG_SCALE
73
+ # - name: latent_factors
74
+ # type: INTEGER
75
+ # min_value: 5
76
+ # max_value: 50
77
+ # scale_type: UNIT_LINEAR_SCALE
78
+ # - name: unobs_weight
79
+ # type: DOUBLE
80
+ # min_value: 0.001
81
+ # max_value: 5.0
82
+ # scale_type: UNIT_LOG_SCALE
83
+ # - name: feature_wt_factor
84
+ # type: DOUBLE
85
+ # min_value: 1
86
+ # max_value: 200
87
+ # scale_type: UNIT_LOG_SCALE
88
+ # - name: level
89
+ # type: DISCRETE
90
+ # discrete_values: [1, 2, 3, 4]
91
+ # - name: category
92
+ # type: CATEGORICAL
93
+ # categorical_values: ["A", "B", "C"]
94
+ #
95
+ hyper_tuning:
96
+
97
+ ## max_job_count
98
+ # 一次自动调参的尝试次数,最多支持 100 次
99
+ #
100
+ max_job_count: 0
101
+
102
+ ## parallel_count
103
+ # 并行的尝试个数受限于用户的单个资源类型的最大并行个数,通常是 1 或者 2
104
+ #
105
+ parallel_count: "{parallel_count}"
106
+
107
+ ## hyperparameter_metric
108
+ # 目标变量
109
+ # 有关目标变量的上报,请查阅 https://openbayes.com/docs/hypertuning/#2-上报目标变量
110
+ hyperparameter_metric: ""
111
+
112
+ ## goal
113
+ # 最优解的方向 ( MAXIMIZE 或 MINIMIZE )
114
+ #
115
+ goal: ""
116
+
117
+ ## algorithm
118
+ # 采用的算法,支持的算法如下:
119
+ # Grid 对于只有 DISCRETE 以及 CATEGORICAL 类型参数的场景可以通过 GridSearch 遍历所有参数的组合
120
+ # Random 针对 INTEGER 以及 DOUBLE 类型,依据其所支持的分布类型,在 min_value 和 max_value 之间随机选择数值,对于 DISCRETE 和 CATEGORICAL 类型,其行为和 Grid 方式类似
121
+ # Bayesian 每次生成参数时考虑之前的「参数」-「目标变量」的结果,通过更新后的分布函数提供参数以期望获取更好的结果,其算法可以参考该文章
122
+ #
123
+ algorithm: ""
124
+
125
+ ## parameter_specs
126
+ # 输入参数的规约
127
+ # 参数规约的定义请查阅:https://openbayes.com/docs/hypertuning/#参数规约的定义
128
+ #
129
+ parameter_specs: []
130
+
131
+ ## side_metrics
132
+ # 其他参考指标
133
+ #
134
+ side_metrics: []
@@ -0,0 +1,3 @@
1
+ __MACOSX
2
+ .DS_Store
3
+ .git
File without changes
@@ -0,0 +1,79 @@
1
+ import logging
2
+ import time
3
+ import zipfile
4
+ import archiver
5
+ import shutil
6
+ import os
7
+
8
+ from bayes.model.file.openbayes_ignore import IGNORE_FILE_NAME, IGNORE_CLEANUPS
9
+ from bayes.usercases.disk_usecase import IgnoreService
10
+
11
+
12
+ def is_file_exist(zip_name, filename):
13
+ try:
14
+ with zipfile.ZipFile(zip_name, 'r') as zr:
15
+ for file in zr.filelist:
16
+ if not file.is_dir():
17
+ if file.filename == filename:
18
+ return True
19
+ except Exception as err:
20
+ return False, err
21
+
22
+ return False, None
23
+
24
+
25
+ def unzip(source, target):
26
+ try:
27
+ if source.endswith('.zip'):
28
+ with zipfile.ZipFile(source, 'r') as zip_ref:
29
+ zip_ref.extractall(target)
30
+ except Exception as e:
31
+ return e
32
+
33
+
34
+ def append_file(zip_writer, source, filename):
35
+ path = os.path.join(source, filename)
36
+
37
+ try:
38
+ # 打开文件
39
+ with open(path, 'rb') as file_to_zip:
40
+ # 获取文件信息
41
+ info = os.stat(path)
42
+
43
+ # 创建 ZIP 文件头
44
+ header = zipfile.ZipInfo(filename)
45
+ header.compress_type = zipfile.ZIP_DEFLATED
46
+ header.file_size = info.st_size
47
+
48
+ # 获取文件的修改时间并设置为 ZIP 文件头的时间
49
+ mtime = time.localtime(info.st_mtime)
50
+ header.date_time = mtime[:6]
51
+
52
+ # 将文件内容写入 ZIP 文件
53
+ zip_writer.writestr(header, file_to_zip.read())
54
+
55
+ except Exception as e:
56
+ return e
57
+
58
+ return None
59
+
60
+
61
+ def archive(source, destination):
62
+ continue_on_error = True
63
+
64
+ try:
65
+ with zipfile.ZipFile(destination, 'w', zipfile.ZIP_DEFLATED) as zip_file:
66
+ ignore_service = IgnoreService(IGNORE_FILE_NAME, IGNORE_CLEANUPS)
67
+ left_files, _, error = ignore_service.left(source)
68
+ if error is not None:
69
+ return error
70
+
71
+ for filename in left_files:
72
+ err = append_file(zip_file, source, filename)
73
+ if err is not None and not continue_on_error:
74
+ return err
75
+ except Exception as e:
76
+ logging.error(f"archive: Error calling ignore_service.left: {e}")
77
+ return e
78
+
79
+ return None
@@ -0,0 +1,35 @@
1
+ from typing import Optional
2
+
3
+ from bayes.client import user_client
4
+ from bayes.client.base import BayesGQLClient
5
+ from bayes.model.file.settings import BayesSettings, BayesEnvConfig
6
+
7
+
8
+ def get_default_credential_userinfo():
9
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
10
+ gql_client = BayesGQLClient(default_env.graphQL, None)
11
+
12
+ if not default_env.token:
13
+ print("用户还未登陆")
14
+ return
15
+
16
+ login_model = user_client.login_with_token(gql_client, default_env.token)
17
+ # 登陆成功,获得用户信息,去修改文件
18
+ BayesSettings().login(login_model.username, login_model.token)
19
+ return login_model
20
+
21
+
22
+ def check_login():
23
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
24
+ if default_env.name and default_env.token:
25
+ return True
26
+ else:
27
+ return False
28
+
29
+
30
+ def is_working_on_org():
31
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
32
+ if default_env.token and default_env.orgName:
33
+ return True
34
+ else:
35
+ return False
@@ -0,0 +1,61 @@
1
+ from typing import Optional
2
+
3
+ from bayes.client.base import BayesGQLClient
4
+ from bayes.client import dataset_version_client
5
+ from bayes.model.file.settings import BayesEnvConfig, BayesSettings
6
+ from bayes.utils import Utils
7
+
8
+
9
+ def get_dataset_version_for_gear_binding(party_name, flag):
10
+ default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
11
+ gql_client = BayesGQLClient(default_env.graphQL, default_env.token)
12
+
13
+ list_data = []
14
+
15
+ # 先得到 PUBLIC
16
+ public_dataset_version_list = dataset_version_client.get_public_dataset_version_for_gear_binding(gql_client)
17
+
18
+ if public_dataset_version_list is not None:
19
+ for dataset_version in public_dataset_version_list.data:
20
+ data = [
21
+ "PUBLIC",
22
+ dataset_version.semanticBindingName,
23
+ Utils.date_from_now(dataset_version.createdAt),
24
+ f"{flag} {dataset_version.semanticBindingName}:/input[0-4]"
25
+ ]
26
+ list_data.append(data)
27
+
28
+ # user PRIVATE
29
+ party_dataset_version_list = dataset_version_client.get_party_private_dataset_version_for_gear_binding(gql_client,
30
+ party_name)
31
+
32
+ if party_dataset_version_list and len(party_dataset_version_list.data) > 0:
33
+ list_data.append(["-"])
34
+
35
+ if party_dataset_version_list is not None:
36
+ for dataset_version in party_dataset_version_list.data:
37
+ data = [
38
+ "PRIVATE",
39
+ dataset_version.semanticBindingName,
40
+ Utils.date_from_now(dataset_version.createdAt),
41
+ f"{flag} {dataset_version.semanticBindingName}:/input[0-4]"
42
+ ]
43
+ list_data.append(data)
44
+
45
+ # job output
46
+ party_job_output_list = dataset_version_client.get_party_job_output_for_gear_binding(gql_client, party_name)
47
+
48
+ if party_job_output_list and len(party_job_output_list.data) > 0:
49
+ list_data.append(["-"])
50
+
51
+ if party_job_output_list is not None:
52
+ for job_output in party_job_output_list.data:
53
+ data = [
54
+ "OUTPUT",
55
+ job_output.output.path,
56
+ Utils.date_from_now(job_output.createdAt),
57
+ f"{flag} {job_output.output.path}:/output"
58
+ ]
59
+ list_data.append(data)
60
+
61
+ return list_data
@@ -0,0 +1,98 @@
1
+ import os
2
+ import logging
3
+ from typing import List, Tuple, Optional
4
+
5
+ import pathspec
6
+
7
+
8
+ class IgnoreService:
9
+ def __init__(self, ignore_file_name: str, cleanups: List[str]):
10
+ self.ignore_file_name = ignore_file_name
11
+ self.cleanups = cleanups
12
+
13
+ def ignored(self, basedir: str) -> Tuple[List[str], List[str], Optional[Exception]]:
14
+ try:
15
+ basedir = os.path.realpath(basedir)
16
+
17
+ with open(self.ignore_file_name, 'r') as file:
18
+ ignore_patterns = file.read().splitlines()
19
+
20
+ spec = pathspec.PathSpec.from_lines('gitwildmatch', ignore_patterns + self.cleanups)
21
+
22
+ matched_files = []
23
+ matched_dirs = []
24
+
25
+ for root, dirs, files in os.walk(basedir):
26
+ for name in files:
27
+ file_path = os.path.relpath(os.path.join(root, name), basedir)
28
+ if spec.match_file(file_path):
29
+ matched_files.append(file_path)
30
+ for name in dirs:
31
+ dir_path = os.path.relpath(os.path.join(root, name), basedir)
32
+ if spec.match_file(dir_path):
33
+ matched_dirs.append(dir_path)
34
+
35
+ return matched_files, matched_dirs, None
36
+ except Exception as e:
37
+ return [], [], e
38
+
39
+ def left(self, basedir: str) -> Tuple[List[str], List[str], Optional[Exception]]:
40
+ try:
41
+ basedir = os.path.realpath(basedir)
42
+
43
+ with open(self.ignore_file_name, 'r') as file:
44
+ ignore_patterns = file.read().splitlines()
45
+
46
+ spec = pathspec.PathSpec.from_lines('gitwildmatch', ignore_patterns + self.cleanups)
47
+
48
+ unmatched_files = []
49
+ unmatched_dirs = []
50
+
51
+ for root, dirs, files in os.walk(basedir):
52
+ for name in files:
53
+ file_path = os.path.relpath(os.path.join(root, name), basedir)
54
+ if not spec.match_file(file_path):
55
+ unmatched_files.append(file_path)
56
+ for name in dirs:
57
+ dir_path = os.path.relpath(os.path.join(root, name), basedir)
58
+ if not spec.match_file(dir_path):
59
+ unmatched_dirs.append(dir_path)
60
+
61
+ return unmatched_files, unmatched_dirs, None
62
+ except Exception as e:
63
+ return [], [], e
64
+
65
+
66
+ def mbyte_to_byte(mb):
67
+ return mb * (1 << 20)
68
+
69
+
70
+ class DiskService:
71
+ def __init__(self, ignore_service):
72
+ self.ignore_service = ignore_service
73
+
74
+ def directory_computing(self, dir, mb_limit):
75
+ try:
76
+ left_files, _, err = self.ignore_service.left(dir)
77
+ if err is not None:
78
+ return 0, 0, err
79
+
80
+ files = 0
81
+ total_bytes = 0
82
+
83
+ for file in left_files:
84
+ file_path = os.path.join(dir, file)
85
+ if os.path.isfile(file_path):
86
+ stat = os.stat(file_path)
87
+ total_bytes += stat.st_size
88
+ files += 1
89
+
90
+ if total_bytes >= mbyte_to_byte(mb_limit):
91
+ return 0, 0, Exception(f"文件总大小超出限制的 {mb_limit} MB")
92
+
93
+ logging.debug(f"Get files [{files}] and sizes [{total_bytes}]")
94
+
95
+
96
+ return files, total_bytes, None
97
+ except Exception as e:
98
+ return 0, 0, e
@@ -0,0 +1,113 @@
1
+ import os
2
+ import queue
3
+ import sys
4
+ import threading
5
+ import time
6
+ from typing import Optional
7
+
8
+ import typer
9
+
10
+ from bayes.client import gear_client
11
+ from bayes.client.base import BayesGQLClient
12
+ from bayes.client.gear_client import DownloadInfoPayload
13
+ from bayes.model.file.settings import BayesSettings
14
+ from bayes.usercases import archive_usecase
15
+ from bayes.utils import Utils
16
+
17
+
18
+ def is_folder_empty(target):
19
+ if not os.path.exists(target):
20
+ return True
21
+
22
+ files = os.listdir(target)
23
+ return len(files) == 0
24
+
25
+
26
+ def get_download_url(id, party_name, download_from):
27
+ default_env = BayesSettings().default_env
28
+ gql_client = BayesGQLClient(default_env.graphQL, default_env.token)
29
+
30
+ return gear_client.get_output_download_link(gql_client, id, party_name, download_from)
31
+
32
+
33
+ def get_target_file_name(target, payload: DownloadInfoPayload):
34
+ return os.path.join(target, payload.get_file_name())
35
+
36
+
37
+ def is_file_exist(filename):
38
+ if os.path.exists(filename):
39
+ return True
40
+ else:
41
+ return False
42
+
43
+
44
+ def is_continuing(filename: str) -> bool:
45
+ is_continuing = typer.prompt(f"{filename} 已存在在目标路径中,是否需要覆盖? [y/N]")
46
+ if not is_continuing:
47
+ print("Operation cancelled by the user.")
48
+ sys.exit(1)
49
+ if is_continuing.lower() in ("y", "yes"):
50
+ return True
51
+ return False
52
+
53
+
54
+ def download(target, payload):
55
+ filename = get_target_file_name(target, payload)
56
+ is_finished = queue.Queue()
57
+
58
+ print("正在下载中,请稍候")
59
+
60
+ def download_process():
61
+ is_done = False
62
+ while not is_done:
63
+ if not is_finished.empty():
64
+ result = is_finished.get()
65
+ if isinstance(result, tuple):
66
+ is_done, err = result
67
+ else:
68
+ raise TypeError("Queue item is not a tuple")
69
+
70
+ if is_done:
71
+ print(f"\r下载完成,文件保存在 {filename}")
72
+ else:
73
+ print(f"\r下载失败: {err}")
74
+ print()
75
+ break
76
+ else:
77
+ try:
78
+ file_stat = os.stat(filename)
79
+ size = Utils.byte_size(file_stat.st_size, False)
80
+ print(f"\r已下载 {size}", end="")
81
+ except FileNotFoundError:
82
+ pass
83
+ time.sleep(1)
84
+
85
+ download_thread = threading.Thread(target=download_process)
86
+ download_thread.start()
87
+
88
+ err = gear_client.download(payload.url, filename, is_finished)
89
+
90
+ if payload.is_file():
91
+ print(f"payload.is_file:{payload.is_file()}")
92
+ return "", err
93
+
94
+ return filename, err
95
+
96
+
97
+ def rename_zip(zip_name, filename):
98
+ is_exist, err = archive_usecase.is_file_exist(zip_name, filename)
99
+ if is_exist and err is None:
100
+ new_file_name = zip_name.replace(".zip", "_" + Utils.generate_uid() + ".zip")
101
+ os.rename(zip_name, new_file_name)
102
+ return new_file_name
103
+ return zip_name
104
+
105
+
106
+ def unzip(source, target):
107
+ try:
108
+ err = archive_usecase.unzip(source, target)
109
+ if err:
110
+ return err
111
+ os.remove(source)
112
+ except Exception as e:
113
+ return e