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.
- bayes/__init__.py +0 -0
- bayes/client/__init__.py +0 -0
- bayes/client/base.py +20 -0
- bayes/client/dataset_version_client.py +91 -0
- bayes/client/gear_client.py +468 -0
- bayes/client/job_run_client.py +504 -0
- bayes/client/job_upload_client.py +114 -0
- bayes/client/org_client.py +67 -0
- bayes/client/resource_client.py +108 -0
- bayes/client/runtime_client.py +32 -0
- bayes/client/ssh_client.py +66 -0
- bayes/client/status_client.py +14 -0
- bayes/client/user_client.py +58 -0
- bayes/commands/__init__.py +0 -0
- bayes/commands/gear.py +691 -0
- bayes/commands/org.py +100 -0
- bayes/commands/ssh.py +70 -0
- bayes/error.py +9 -0
- bayes/model/__init__.py +0 -0
- bayes/model/dataset_version.py +14 -0
- bayes/model/file/__init__.py +0 -0
- bayes/model/file/data_bindings.py +42 -0
- bayes/model/file/openbayes_data.py +133 -0
- bayes/model/file/openbayes_gear.py +72 -0
- bayes/model/file/openbayes_ignore.py +75 -0
- bayes/model/file/openbayes_yaml.py +180 -0
- bayes/model/file/settings.py +157 -0
- bayes/model/party.py +35 -0
- bayes/model/resource.py +147 -0
- bayes/model/runtime.py +34 -0
- bayes/root.py +170 -0
- bayes/templates/openbayes_zh-Hans.yaml +134 -0
- bayes/templates/openbayesignore.yaml +3 -0
- bayes/usercases/__init__.py +0 -0
- bayes/usercases/archive_usecase.py +79 -0
- bayes/usercases/auth_usecase.py +35 -0
- bayes/usercases/dataset_version_usecase.py +61 -0
- bayes/usercases/disk_usecase.py +98 -0
- bayes/usercases/gear_download_usecese.py +113 -0
- bayes/usercases/gear_logs_usecase.py +44 -0
- bayes/usercases/gear_open_usecase.py +16 -0
- bayes/usercases/gear_run_usecase.py +123 -0
- bayes/usercases/gear_upload_usecase.py +248 -0
- bayes/usercases/gear_usecase.py +301 -0
- bayes/usercases/openbayes_data_usecase.py +73 -0
- bayes/usercases/org_usecase.py +85 -0
- bayes/usercases/resource_usecase.py +74 -0
- bayes/usercases/runtime_usecase.py +58 -0
- bayes/usercases/ssh_usecase.py +110 -0
- bayes/usercases/switch_usercase.py +19 -0
- bayes/utils.py +163 -0
- openbayes_cli-0.4.5.dist-info/METADATA +62 -0
- openbayes_cli-0.4.5.dist-info/RECORD +55 -0
- openbayes_cli-0.4.5.dist-info/WHEEL +4 -0
- 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: []
|
|
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
|