bluer-objects 6.104.1__py3-none-any.whl → 6.464.1__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.
- bluer_objects/.abcli/abcli.sh +6 -0
- bluer_objects/.abcli/alias.sh +13 -6
- bluer_objects/.abcli/assets/cd.sh +20 -0
- bluer_objects/.abcli/assets/mv.sh +34 -0
- bluer_objects/.abcli/assets/publish.sh +40 -0
- bluer_objects/.abcli/assets.sh +15 -0
- bluer_objects/.abcli/create_test_asset.sh +10 -0
- bluer_objects/.abcli/download.sh +3 -1
- bluer_objects/.abcli/file.sh +15 -4
- bluer_objects/.abcli/gif.sh +18 -0
- bluer_objects/.abcli/host.sh +23 -7
- bluer_objects/.abcli/ls.sh +19 -8
- bluer_objects/.abcli/metadata/download.sh +9 -0
- bluer_objects/.abcli/metadata/edit.sh +15 -0
- bluer_objects/.abcli/metadata/upload.sh +9 -0
- bluer_objects/.abcli/mlflow/browse.sh +2 -0
- bluer_objects/.abcli/mlflow/deploy.sh +21 -5
- bluer_objects/.abcli/mlflow/lock/lock.sh +11 -0
- bluer_objects/.abcli/mlflow/lock/unlock.sh +12 -0
- bluer_objects/.abcli/mlflow/lock.sh +15 -0
- bluer_objects/.abcli/mlflow/tags/search.sh +1 -5
- bluer_objects/.abcli/mlflow.sh +0 -2
- bluer_objects/.abcli/pdf/convert.sh +92 -0
- bluer_objects/.abcli/pdf.sh +15 -0
- bluer_objects/.abcli/storage/clear.sh +2 -0
- bluer_objects/.abcli/tests/clone.sh +2 -3
- bluer_objects/.abcli/tests/create_test_asset.sh +16 -0
- bluer_objects/.abcli/tests/file.sh +64 -0
- bluer_objects/.abcli/tests/gif.sh +3 -3
- bluer_objects/.abcli/tests/help.sh +23 -7
- bluer_objects/.abcli/tests/ls.sh +11 -4
- bluer_objects/.abcli/tests/metadata.sh +35 -0
- bluer_objects/.abcli/tests/mlflow_lock.sh +30 -0
- bluer_objects/.abcli/tests/mlflow_tags.sh +1 -1
- bluer_objects/.abcli/tests/open.sh +11 -0
- bluer_objects/.abcli/tests/open_gif_open.sh +14 -0
- bluer_objects/.abcli/tests/pdf.sh +39 -0
- bluer_objects/.abcli/tests/storage_clear.sh +11 -0
- bluer_objects/.abcli/tests/storage_public_upload.sh +25 -0
- bluer_objects/.abcli/tests/storage_status.sh +12 -0
- bluer_objects/.abcli/tests/{storage.sh → storage_upload_download.sh} +26 -8
- bluer_objects/.abcli/upload.sh +26 -2
- bluer_objects/README/__init__.py +7 -22
- bluer_objects/README/alias.py +67 -0
- bluer_objects/README/build/__init__.py +0 -0
- bluer_objects/README/build/aliases.py +23 -0
- bluer_objects/README/build/docs.py +23 -0
- bluer_objects/README/build/modules.py +9 -0
- bluer_objects/README/consts.py +44 -0
- bluer_objects/README/functions.py +154 -204
- bluer_objects/README/items.py +78 -6
- bluer_objects/README/process/__init__.py +0 -0
- bluer_objects/README/process/assets.py +36 -0
- bluer_objects/README/process/details.py +20 -0
- bluer_objects/README/process/envs.py +23 -0
- bluer_objects/README/process/help.py +27 -0
- bluer_objects/README/process/include.py +40 -0
- bluer_objects/README/process/legacy.py +21 -0
- bluer_objects/README/process/mermaid.py +20 -0
- bluer_objects/README/process/national_internet.py +55 -0
- bluer_objects/README/process/objects.py +32 -0
- bluer_objects/README/process/signature.py +35 -0
- bluer_objects/README/process/title.py +44 -0
- bluer_objects/README/process/variables.py +12 -0
- bluer_objects/__init__.py +1 -1
- bluer_objects/assets/__init__.py +0 -0
- bluer_objects/assets/__main__.py +57 -0
- bluer_objects/assets/functions.py +62 -0
- bluer_objects/config.env +13 -1
- bluer_objects/env.py +27 -1
- bluer_objects/file/__main__.py +52 -7
- bluer_objects/file/functions.py +21 -4
- bluer_objects/file/load.py +2 -9
- bluer_objects/file/save.py +17 -24
- bluer_objects/graphics/__main__.py +7 -0
- bluer_objects/graphics/gif.py +11 -7
- bluer_objects/graphics/screen.py +9 -8
- bluer_objects/help/assets.py +93 -0
- bluer_objects/help/create_test_asset.py +22 -0
- bluer_objects/help/download.py +17 -3
- bluer_objects/help/file.py +59 -0
- bluer_objects/help/functions.py +9 -1
- bluer_objects/help/gif.py +25 -0
- bluer_objects/help/host.py +6 -4
- bluer_objects/help/ls.py +26 -3
- bluer_objects/help/metadata.py +51 -0
- bluer_objects/help/mlflow/__init__.py +23 -2
- bluer_objects/help/mlflow/cache.py +2 -4
- bluer_objects/help/mlflow/lock.py +52 -0
- bluer_objects/help/mlflow/tags.py +34 -23
- bluer_objects/help/pdf.py +67 -0
- bluer_objects/help/upload.py +10 -3
- bluer_objects/host/functions.py +4 -1
- bluer_objects/logger/confusion_matrix.py +76 -0
- bluer_objects/logger/image.py +110 -0
- bluer_objects/logger/stitch.py +107 -0
- bluer_objects/markdown.py +8 -6
- bluer_objects/metadata/__init__.py +1 -0
- bluer_objects/metadata/flatten.py +27 -0
- bluer_objects/mlflow/__init__.py +1 -1
- bluer_objects/mlflow/__main__.py +49 -31
- bluer_objects/mlflow/lock/__init__.py +1 -0
- bluer_objects/mlflow/lock/__main__.py +58 -0
- bluer_objects/mlflow/lock/functions.py +121 -0
- bluer_objects/mlflow/logging.py +53 -41
- bluer_objects/mlflow/models.py +7 -0
- bluer_objects/mlflow/objects.py +7 -0
- bluer_objects/mlflow/runs.py +10 -1
- bluer_objects/mlflow/serverless/__init__.py +3 -0
- bluer_objects/mlflow/serverless/api.py +88 -0
- bluer_objects/mlflow/serverless/read.py +19 -0
- bluer_objects/mlflow/serverless/search.py +35 -0
- bluer_objects/mlflow/serverless/write.py +42 -0
- bluer_objects/mlflow/tags.py +59 -9
- bluer_objects/objects.py +3 -1
- bluer_objects/pdf/__init__.py +1 -0
- bluer_objects/pdf/__main__.py +78 -0
- bluer_objects/pdf/convert/__init__.py +0 -0
- bluer_objects/pdf/convert/batch.py +54 -0
- bluer_objects/pdf/convert/combination.py +32 -0
- bluer_objects/pdf/convert/convert.py +110 -0
- bluer_objects/pdf/convert/image.py +53 -0
- bluer_objects/pdf/convert/md.py +97 -0
- bluer_objects/pdf/convert/missing.py +96 -0
- bluer_objects/pdf/convert/pdf.py +37 -0
- bluer_objects/sample.env +6 -0
- bluer_objects/storage/WebDAV.py +11 -7
- bluer_objects/storage/WebDAVrequest.py +360 -0
- bluer_objects/storage/WebDAVzip.py +26 -29
- bluer_objects/storage/__init__.py +28 -1
- bluer_objects/storage/__main__.py +40 -6
- bluer_objects/storage/base.py +84 -5
- bluer_objects/storage/policies.py +7 -0
- bluer_objects/storage/s3.py +367 -0
- bluer_objects/testing/__main__.py +6 -0
- bluer_objects/tests/test_README_consts.py +71 -0
- bluer_objects/tests/test_README_items.py +128 -0
- bluer_objects/tests/test_alias.py +33 -0
- bluer_objects/tests/test_env.py +42 -7
- bluer_objects/tests/test_file_download.py +30 -0
- bluer_objects/tests/test_file_load_save.py +1 -2
- bluer_objects/tests/test_file_load_save_text.py +46 -0
- bluer_objects/tests/test_graphics_gif.py +2 -0
- bluer_objects/tests/test_log_image_grid.py +29 -0
- bluer_objects/tests/test_logger_confusion_matrix.py +18 -0
- bluer_objects/tests/test_logger_matrix.py +2 -2
- bluer_objects/tests/test_logger_stitch_images.py +47 -0
- bluer_objects/tests/test_metadata.py +12 -6
- bluer_objects/tests/test_metadata_flatten.py +109 -0
- bluer_objects/tests/test_mlflow.py +114 -5
- bluer_objects/tests/test_mlflow_lock.py +26 -0
- bluer_objects/tests/test_objects.py +2 -0
- bluer_objects/tests/test_shell.py +34 -0
- bluer_objects/tests/test_storage.py +8 -21
- bluer_objects/tests/test_storage_base.py +39 -0
- bluer_objects/tests/test_storage_s3.py +67 -0
- bluer_objects/tests/test_storage_webdav_request.py +75 -0
- bluer_objects/tests/test_storage_webdav_zip.py +42 -0
- bluer_objects/tests/test_web_is_accessible.py +11 -0
- {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/METADATA +20 -11
- bluer_objects-6.464.1.dist-info/RECORD +228 -0
- {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/WHEEL +1 -1
- bluer_objects/.abcli/storage/download_file.sh +0 -9
- bluer_objects/.abcli/storage/exists.sh +0 -8
- bluer_objects/.abcli/storage/list.sh +0 -8
- bluer_objects/.abcli/storage/rm.sh +0 -11
- bluer_objects/.abcli/tests/mlflow_test.sh +0 -7
- bluer_objects-6.104.1.dist-info/RECORD +0 -143
- {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/licenses/LICENSE +0 -0
- {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/top_level.txt +0 -0
bluer_objects/mlflow/__main__.py
CHANGED
|
@@ -24,7 +24,6 @@ from bluer_objects.mlflow.runs import (
|
|
|
24
24
|
start_run,
|
|
25
25
|
)
|
|
26
26
|
from bluer_objects.mlflow.tags import (
|
|
27
|
-
create_filter_string,
|
|
28
27
|
get_tags,
|
|
29
28
|
search,
|
|
30
29
|
set_tags,
|
|
@@ -42,7 +41,23 @@ parser.add_argument(
|
|
|
42
41
|
"task",
|
|
43
42
|
type=str,
|
|
44
43
|
default="",
|
|
45
|
-
help="
|
|
44
|
+
help=" | ".join(
|
|
45
|
+
[
|
|
46
|
+
"clone_tags",
|
|
47
|
+
"get_id",
|
|
48
|
+
"get_run_id",
|
|
49
|
+
"get_tags",
|
|
50
|
+
"list_registered_models",
|
|
51
|
+
"log_artifacts",
|
|
52
|
+
"log_run",
|
|
53
|
+
"rm",
|
|
54
|
+
"search",
|
|
55
|
+
"set_tags",
|
|
56
|
+
"start_end_run",
|
|
57
|
+
"test",
|
|
58
|
+
"transition",
|
|
59
|
+
]
|
|
60
|
+
),
|
|
46
61
|
)
|
|
47
62
|
parser.add_argument(
|
|
48
63
|
"--count",
|
|
@@ -115,13 +130,13 @@ parser.add_argument(
|
|
|
115
130
|
"--log",
|
|
116
131
|
type=int,
|
|
117
132
|
default=1,
|
|
118
|
-
help="0|1",
|
|
133
|
+
help="0 | 1",
|
|
119
134
|
)
|
|
120
135
|
parser.add_argument(
|
|
121
|
-
"--
|
|
136
|
+
"--server_style",
|
|
122
137
|
type=int,
|
|
123
138
|
default=0,
|
|
124
|
-
help="0|1",
|
|
139
|
+
help="0 | 1",
|
|
125
140
|
)
|
|
126
141
|
parser.add_argument(
|
|
127
142
|
"--start_end",
|
|
@@ -162,6 +177,12 @@ parser.add_argument(
|
|
|
162
177
|
default="",
|
|
163
178
|
help="",
|
|
164
179
|
)
|
|
180
|
+
parser.add_argument(
|
|
181
|
+
"--verbose",
|
|
182
|
+
type=int,
|
|
183
|
+
default=0,
|
|
184
|
+
help="0 | 1",
|
|
185
|
+
)
|
|
165
186
|
args = parser.parse_args()
|
|
166
187
|
|
|
167
188
|
delim = " " if args.delim == "space" else args.delim
|
|
@@ -171,9 +192,6 @@ if args.task == "clone_tags":
|
|
|
171
192
|
success, tags = get_tags(args.source_objects)
|
|
172
193
|
if success:
|
|
173
194
|
success = set_tags(args.destination_object, tags)
|
|
174
|
-
elif args.task == "create_filter_string":
|
|
175
|
-
success = True
|
|
176
|
-
print(create_filter_string(args.tags))
|
|
177
195
|
elif args.task == "rm":
|
|
178
196
|
success = reduce(
|
|
179
197
|
lambda x, y: x and y,
|
|
@@ -188,7 +206,10 @@ elif args.task == "rm":
|
|
|
188
206
|
True,
|
|
189
207
|
)
|
|
190
208
|
elif args.task == "get_tags":
|
|
191
|
-
success, tags = get_tags(
|
|
209
|
+
success, tags = get_tags(
|
|
210
|
+
args.object_name,
|
|
211
|
+
verbose=args.verbose == 1,
|
|
212
|
+
)
|
|
192
213
|
print(tags if not args.tag else tags.get(args.tag, args.default))
|
|
193
214
|
elif args.task == "get_id":
|
|
194
215
|
success, id = get_id(args.object_name)
|
|
@@ -217,36 +238,33 @@ elif args.task == "log_artifacts":
|
|
|
217
238
|
elif args.task == "log_run":
|
|
218
239
|
success = log_run(args.object_name)
|
|
219
240
|
elif args.task == "search":
|
|
220
|
-
success =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
create_filter_string(args.tags)
|
|
224
|
-
if args.explicit_query == 0
|
|
225
|
-
else "" if args.filter_string == "-" else args.filter_string
|
|
241
|
+
success, list_of_objects = search(
|
|
242
|
+
filter_string=args.tags,
|
|
243
|
+
server_style=args.server_style == 1,
|
|
226
244
|
)
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
list_of_objects = list_of_objects[args.offset :]
|
|
246
|
+
if success:
|
|
247
|
+
list_of_objects = list_of_objects[args.offset :]
|
|
231
248
|
|
|
232
|
-
|
|
233
|
-
|
|
249
|
+
if args.count != -1:
|
|
250
|
+
list_of_objects = list_of_objects[: args.count]
|
|
234
251
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
252
|
+
if args.log:
|
|
253
|
+
logger.info(
|
|
254
|
+
"{:,} {}.".format(
|
|
255
|
+
len(list_of_objects),
|
|
256
|
+
args.item_name_plural,
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
for index, object_name in enumerate(list_of_objects):
|
|
260
|
+
logger.info(f"#{index+1: 4d} - {object_name}")
|
|
261
|
+
else:
|
|
262
|
+
print(delim.join(list_of_objects))
|
|
246
263
|
elif args.task == "set_tags":
|
|
247
264
|
success = set_tags(
|
|
248
265
|
args.object_name,
|
|
249
266
|
args.tags,
|
|
267
|
+
verbose=args.verbose == 1,
|
|
250
268
|
)
|
|
251
269
|
elif args.task == "transition":
|
|
252
270
|
success = transition(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from blueness import module
|
|
4
|
+
from blueness.argparse.generic import sys_exit
|
|
5
|
+
|
|
6
|
+
from bluer_objects import NAME
|
|
7
|
+
from bluer_objects.mlflow.lock.functions import lock, unlock
|
|
8
|
+
from bluer_objects.logger import logger
|
|
9
|
+
|
|
10
|
+
NAME = module.name(__file__, NAME)
|
|
11
|
+
|
|
12
|
+
parser = argparse.ArgumentParser(NAME)
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"task",
|
|
15
|
+
type=str,
|
|
16
|
+
help="lock | unlock",
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--object_name",
|
|
20
|
+
type=str,
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--lock",
|
|
24
|
+
type=str,
|
|
25
|
+
default="lock",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--timeout",
|
|
29
|
+
type=int,
|
|
30
|
+
default=-1,
|
|
31
|
+
help="in seconds",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--verbose",
|
|
35
|
+
type=int,
|
|
36
|
+
default=1,
|
|
37
|
+
help="0 | 1",
|
|
38
|
+
)
|
|
39
|
+
args = parser.parse_args()
|
|
40
|
+
|
|
41
|
+
success = False
|
|
42
|
+
if args.task == "lock":
|
|
43
|
+
success = lock(
|
|
44
|
+
object_name=args.object_name,
|
|
45
|
+
lock_name=args.lock,
|
|
46
|
+
timeout=args.timeout,
|
|
47
|
+
verbose=args.verbose == 1,
|
|
48
|
+
)
|
|
49
|
+
elif args.task == "unlock":
|
|
50
|
+
success = unlock(
|
|
51
|
+
object_name=args.object_name,
|
|
52
|
+
lock_name=args.lock,
|
|
53
|
+
verbose=args.verbose == 1,
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
success = None
|
|
57
|
+
|
|
58
|
+
sys_exit(logger, NAME, args.task, success)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from blueness import module
|
|
4
|
+
from bluer_options import string
|
|
5
|
+
|
|
6
|
+
from bluer_objects import NAME, env
|
|
7
|
+
from bluer_objects.mlflow.tags import get_tags, set_tags
|
|
8
|
+
from bluer_objects.logger import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
NAME = module.name(__file__, NAME)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def lock(
|
|
15
|
+
object_name: str,
|
|
16
|
+
lock_name: str = "lock",
|
|
17
|
+
timeout: int = -1,
|
|
18
|
+
verbose: bool = True,
|
|
19
|
+
) -> bool:
|
|
20
|
+
logger.info(
|
|
21
|
+
"{}.lock: {}.{}{}".format(
|
|
22
|
+
NAME,
|
|
23
|
+
object_name,
|
|
24
|
+
lock_name,
|
|
25
|
+
"" if timeout == -1 else " @ {}".format(string.pretty_duration(timeout)),
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
start_time = time.time()
|
|
30
|
+
while True:
|
|
31
|
+
if time.time() - start_time > timeout > 0:
|
|
32
|
+
if verbose:
|
|
33
|
+
logger.warning(
|
|
34
|
+
"{}.lock: {}.{} timeout.".format(
|
|
35
|
+
NAME,
|
|
36
|
+
object_name,
|
|
37
|
+
lock_name,
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
success, list_of_tags = get_tags(object_name=object_name)
|
|
43
|
+
if not success:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
lock_value = list_of_tags.get(lock_name, "")
|
|
47
|
+
if lock_value:
|
|
48
|
+
if verbose:
|
|
49
|
+
logger.warning(
|
|
50
|
+
"{}.lock: {}.{} is locked by {}.".format(
|
|
51
|
+
NAME,
|
|
52
|
+
object_name,
|
|
53
|
+
lock_name,
|
|
54
|
+
lock_value,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
time.sleep(env.MLFLOW_LOCK_WAIT_FOR_CLEARANCE)
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
lock_value = string.random()
|
|
61
|
+
if not set_tags(
|
|
62
|
+
object_name=object_name,
|
|
63
|
+
tags={lock_name: lock_value},
|
|
64
|
+
log=verbose,
|
|
65
|
+
icon="🔒",
|
|
66
|
+
):
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
time.sleep(env.MLFLOW_LOCK_WAIT_FOR_EXCLUSIVITY)
|
|
70
|
+
|
|
71
|
+
success, list_of_tags = get_tags(object_name=object_name)
|
|
72
|
+
if not success:
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
lock_value_read = list_of_tags.get(lock_name, "")
|
|
76
|
+
if lock_value_read != lock_value:
|
|
77
|
+
if verbose:
|
|
78
|
+
logger.warning(
|
|
79
|
+
"{}.lock: {}.{} is relocked by {} != {}.".format(
|
|
80
|
+
NAME,
|
|
81
|
+
object_name,
|
|
82
|
+
lock_name,
|
|
83
|
+
lock_value_read,
|
|
84
|
+
lock_value,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
time.sleep(env.MLFLOW_LOCK_WAIT_FOR_CLEARANCE)
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
logger.info(
|
|
93
|
+
"{}.lock: {}.{} is locked by {}.".format(
|
|
94
|
+
NAME,
|
|
95
|
+
object_name,
|
|
96
|
+
lock_name,
|
|
97
|
+
lock_value,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def unlock(
|
|
104
|
+
object_name: str,
|
|
105
|
+
lock_name: str = "lock",
|
|
106
|
+
verbose: bool = True,
|
|
107
|
+
) -> bool:
|
|
108
|
+
logger.info(
|
|
109
|
+
"{}.unlock: {}.{}".format(
|
|
110
|
+
NAME,
|
|
111
|
+
object_name,
|
|
112
|
+
lock_name,
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return set_tags(
|
|
117
|
+
object_name=object_name,
|
|
118
|
+
tags={lock_name: ""},
|
|
119
|
+
log=verbose,
|
|
120
|
+
icon="🔒",
|
|
121
|
+
)
|
bluer_objects/mlflow/logging.py
CHANGED
|
@@ -6,7 +6,7 @@ import mlflow
|
|
|
6
6
|
from blueness import module
|
|
7
7
|
from bluer_options.logger import crash_report
|
|
8
8
|
|
|
9
|
-
from bluer_objects import file, objects, NAME
|
|
9
|
+
from bluer_objects import file, objects, NAME, env
|
|
10
10
|
from bluer_objects.mlflow.runs import start_run, end_run
|
|
11
11
|
from bluer_objects.logger import logger
|
|
12
12
|
|
|
@@ -17,65 +17,77 @@ def log_artifacts(
|
|
|
17
17
|
object_name: str,
|
|
18
18
|
model_name: str = "",
|
|
19
19
|
) -> bool:
|
|
20
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
21
|
+
return True
|
|
22
|
+
|
|
20
23
|
if not start_run(object_name):
|
|
21
24
|
return False
|
|
22
25
|
|
|
23
26
|
object_path = objects.object_path(object_name, create=True)
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if env.MLFLOW_LOG_ARTIFACTS:
|
|
29
|
+
try:
|
|
30
|
+
mlflow.log_artifacts(object_path)
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
logger.info("⬆️ {}".format(object_name))
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
# https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.register_model
|
|
35
|
+
# https://stackoverflow.com/a/71447758/17619982
|
|
36
|
+
if model_name:
|
|
37
|
+
mv = mlflow.register_model(
|
|
38
|
+
"runs:/{}".format(mlflow.active_run().info.run_id),
|
|
39
|
+
model_name,
|
|
40
|
+
await_registration_for=0,
|
|
41
|
+
)
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
logger.info("*️⃣ {} -> {}.{}".format(object_name, mv.name, mv.version))
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
except:
|
|
46
|
+
crash_report(f"{NAME}.log_artifacts({object_name})")
|
|
47
|
+
return False
|
|
48
|
+
else:
|
|
49
|
+
logger.info("skipped log artifacts.")
|
|
44
50
|
|
|
45
51
|
return end_run(object_name)
|
|
46
52
|
|
|
47
53
|
|
|
48
54
|
def log_run(object_name: str) -> bool:
|
|
55
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
56
|
+
return True
|
|
57
|
+
|
|
49
58
|
if not start_run(object_name):
|
|
50
59
|
return False
|
|
51
60
|
|
|
52
61
|
object_path = objects.object_path(object_name, create=True)
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
filename_name = file.name(filename)
|
|
61
|
-
|
|
62
|
-
counts[len(filename_name)] = counts.get(len(filename_name), 0) + 1
|
|
63
|
-
|
|
64
|
-
if any(
|
|
65
|
-
[
|
|
66
|
-
file.size(filename) > 10 * 1024 * 1024,
|
|
67
|
-
filename_name.startswith("thumbnail"),
|
|
68
|
-
counts[len(filename_name)] > 20,
|
|
69
|
-
]
|
|
63
|
+
if env.MLFLOW_LOG_ARTIFACTS:
|
|
64
|
+
counts: Dict[str, int] = {}
|
|
65
|
+
skipped_count = 0
|
|
66
|
+
for extension in "dot,gif,jpeg,jpg,json,png,sh,xml,yaml".split(","):
|
|
67
|
+
for filename in glob.glob(
|
|
68
|
+
os.path.join(object_path, f"*.{extension}"),
|
|
70
69
|
):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
filename_name = file.name(filename)
|
|
71
|
+
|
|
72
|
+
counts[len(filename_name)] = counts.get(len(filename_name), 0) + 1
|
|
73
|
+
|
|
74
|
+
if any(
|
|
75
|
+
[
|
|
76
|
+
file.size(filename) > 10 * 1024 * 1024,
|
|
77
|
+
filename_name.startswith("thumbnail"),
|
|
78
|
+
counts[len(filename_name)] > 20,
|
|
79
|
+
]
|
|
80
|
+
):
|
|
81
|
+
logger.info(f"skipping {filename}")
|
|
82
|
+
skipped_count += 1
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
mlflow.log_artifact(filename)
|
|
86
|
+
logger.info(f"⬆️ {filename}")
|
|
87
|
+
|
|
88
|
+
if skipped_count:
|
|
89
|
+
logger.info(f"skipped {skipped_count:,} file(s).")
|
|
90
|
+
else:
|
|
91
|
+
logger.info("skipped log artifacts.")
|
|
80
92
|
|
|
81
93
|
return end_run(object_name)
|
bluer_objects/mlflow/models.py
CHANGED
|
@@ -5,6 +5,7 @@ from blueness import module
|
|
|
5
5
|
from bluer_options.logger import crash_report
|
|
6
6
|
|
|
7
7
|
from bluer_objects import NAME
|
|
8
|
+
from bluer_objects import env
|
|
8
9
|
from bluer_objects.logger import logger
|
|
9
10
|
|
|
10
11
|
NAME = module.name(__file__, NAME)
|
|
@@ -14,6 +15,9 @@ def list_registered_models() -> Tuple[
|
|
|
14
15
|
bool,
|
|
15
16
|
List[str],
|
|
16
17
|
]:
|
|
18
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
19
|
+
return True, []
|
|
20
|
+
|
|
17
21
|
try:
|
|
18
22
|
client = MlflowClient()
|
|
19
23
|
return True, [dict(rm)["name"] for rm in client.search_registered_models()]
|
|
@@ -29,6 +33,9 @@ def transition(
|
|
|
29
33
|
stage_name: str,
|
|
30
34
|
description: str,
|
|
31
35
|
) -> bool:
|
|
36
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
37
|
+
return True
|
|
38
|
+
|
|
32
39
|
logger.info(
|
|
33
40
|
'{}.transition: {}(#{}) -> {} - "{}")'.format(
|
|
34
41
|
NAME,
|
bluer_objects/mlflow/objects.py
CHANGED
|
@@ -6,6 +6,7 @@ from blueness import module
|
|
|
6
6
|
from bluer_options.logger import crash_report
|
|
7
7
|
|
|
8
8
|
from bluer_objects import NAME
|
|
9
|
+
from bluer_objects import env
|
|
9
10
|
from bluer_objects.env import ABCLI_MLFLOW_EXPERIMENT_PREFIX
|
|
10
11
|
from bluer_objects.logger import logger
|
|
11
12
|
|
|
@@ -16,6 +17,9 @@ def get_id(
|
|
|
16
17
|
object_name: str,
|
|
17
18
|
create: bool = False,
|
|
18
19
|
) -> Tuple[bool, str]:
|
|
20
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
21
|
+
return True, "serverless-id"
|
|
22
|
+
|
|
19
23
|
experiment_name = to_experiment_name(object_name)
|
|
20
24
|
|
|
21
25
|
try:
|
|
@@ -38,6 +42,9 @@ def rm(
|
|
|
38
42
|
object_name: str,
|
|
39
43
|
is_id: bool = False,
|
|
40
44
|
) -> bool:
|
|
45
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
46
|
+
return True
|
|
47
|
+
|
|
41
48
|
if is_id:
|
|
42
49
|
experiment_id = object_name
|
|
43
50
|
else:
|
bluer_objects/mlflow/runs.py
CHANGED
|
@@ -5,10 +5,10 @@ from mlflow.tracking import MlflowClient
|
|
|
5
5
|
from mlflow.entities import ViewType
|
|
6
6
|
|
|
7
7
|
from blueness import module
|
|
8
|
-
from bluer_options import string
|
|
9
8
|
from bluer_options.logger import crash_report
|
|
10
9
|
|
|
11
10
|
from bluer_objects import NAME
|
|
11
|
+
from bluer_objects import env
|
|
12
12
|
from bluer_objects.mlflow.objects import get_id
|
|
13
13
|
from bluer_objects.mlflow.tags import get_tags
|
|
14
14
|
from bluer_objects.logger import logger
|
|
@@ -19,6 +19,9 @@ NAME = module.name(__file__, NAME)
|
|
|
19
19
|
def end_run(
|
|
20
20
|
object_name: str,
|
|
21
21
|
) -> bool:
|
|
22
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
23
|
+
return True
|
|
24
|
+
|
|
22
25
|
try:
|
|
23
26
|
mlflow.end_run()
|
|
24
27
|
logger.info("⏹️ {}".format(object_name))
|
|
@@ -36,6 +39,9 @@ def get_run_id(
|
|
|
36
39
|
create: bool = False,
|
|
37
40
|
is_id: bool = False,
|
|
38
41
|
) -> Tuple[bool, List[str]]:
|
|
42
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
43
|
+
return True, []
|
|
44
|
+
|
|
39
45
|
if is_id:
|
|
40
46
|
experiment_id = object_name
|
|
41
47
|
else:
|
|
@@ -62,6 +68,9 @@ def get_run_id(
|
|
|
62
68
|
def start_run(
|
|
63
69
|
object_name: str,
|
|
64
70
|
) -> bool:
|
|
71
|
+
if env.MLFLOW_IS_SERVERLESS:
|
|
72
|
+
return True
|
|
73
|
+
|
|
65
74
|
success, experiment_id = get_id(object_name, create=True)
|
|
66
75
|
if not success:
|
|
67
76
|
return False
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import Dict, Tuple
|
|
2
|
+
|
|
3
|
+
from blueness import module
|
|
4
|
+
|
|
5
|
+
from bluer_objects import NAME
|
|
6
|
+
from bluer_objects import file
|
|
7
|
+
from bluer_objects import objects
|
|
8
|
+
from bluer_objects import storage
|
|
9
|
+
from bluer_objects.logger import logger
|
|
10
|
+
|
|
11
|
+
NAME = module.name(__file__, NAME)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def write(
|
|
15
|
+
object_name: str,
|
|
16
|
+
filename: str,
|
|
17
|
+
data: Dict,
|
|
18
|
+
log: bool = False,
|
|
19
|
+
) -> bool:
|
|
20
|
+
if log:
|
|
21
|
+
logger.info(f"{NAME}.update({object_name}/{filename})")
|
|
22
|
+
|
|
23
|
+
if not storage.download(
|
|
24
|
+
object_name=object_name,
|
|
25
|
+
filename=filename,
|
|
26
|
+
log=log,
|
|
27
|
+
):
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
full_filename = objects.path_of(
|
|
31
|
+
object_name=object_name,
|
|
32
|
+
filename=filename,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
_, current_data = file.load_yaml(
|
|
36
|
+
full_filename,
|
|
37
|
+
ignore_error=True,
|
|
38
|
+
default={},
|
|
39
|
+
)
|
|
40
|
+
if not isinstance(current_data, dict):
|
|
41
|
+
logger.error(
|
|
42
|
+
"dict expected, {} received".format(
|
|
43
|
+
current_data.__class__.__name__,
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
current_data.update(data)
|
|
49
|
+
|
|
50
|
+
if not file.save_yaml(
|
|
51
|
+
full_filename,
|
|
52
|
+
current_data,
|
|
53
|
+
log=log,
|
|
54
|
+
):
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
if not storage.upload(
|
|
58
|
+
object_name=object_name,
|
|
59
|
+
filename=filename,
|
|
60
|
+
log=log,
|
|
61
|
+
):
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def read(
|
|
68
|
+
object_name: str,
|
|
69
|
+
filename: str,
|
|
70
|
+
verbose: bool = False,
|
|
71
|
+
) -> Tuple[bool, Dict]:
|
|
72
|
+
if not storage.download(
|
|
73
|
+
object_name=object_name,
|
|
74
|
+
filename=filename,
|
|
75
|
+
log=verbose,
|
|
76
|
+
):
|
|
77
|
+
return True, {}
|
|
78
|
+
|
|
79
|
+
_, data = file.load_yaml(
|
|
80
|
+
objects.path_of(
|
|
81
|
+
object_name=object_name,
|
|
82
|
+
filename=filename,
|
|
83
|
+
),
|
|
84
|
+
ignore_error=True,
|
|
85
|
+
default={},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return True, data
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Tuple, Dict
|
|
2
|
+
|
|
3
|
+
from blueness import module
|
|
4
|
+
|
|
5
|
+
from bluer_objects import NAME
|
|
6
|
+
from bluer_objects.mlflow.serverless import api
|
|
7
|
+
|
|
8
|
+
NAME = module.name(__file__, NAME)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_tags(
|
|
12
|
+
object_name: str,
|
|
13
|
+
verbose: bool = False,
|
|
14
|
+
) -> Tuple[bool, Dict[str, str]]:
|
|
15
|
+
return api.read(
|
|
16
|
+
object_name="_serverless_objects",
|
|
17
|
+
filename=f"{object_name}.yaml",
|
|
18
|
+
verbose=verbose,
|
|
19
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import List, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from bluer_options.options import Options
|
|
4
|
+
|
|
5
|
+
from bluer_objects.mlflow.serverless import api
|
|
6
|
+
from bluer_objects.logger import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def search(
|
|
10
|
+
filter_string: Union[str, dict],
|
|
11
|
+
log: bool = False,
|
|
12
|
+
) -> Tuple[bool, List[str]]:
|
|
13
|
+
query = Options(filter_string)
|
|
14
|
+
|
|
15
|
+
output: Union[List[str], None] = None
|
|
16
|
+
for key, value in query.items():
|
|
17
|
+
success, dict_of_objects = api.read(
|
|
18
|
+
object_name="_serverless_keys",
|
|
19
|
+
filename=f"{key}.yaml",
|
|
20
|
+
verbose=log,
|
|
21
|
+
)
|
|
22
|
+
if not success:
|
|
23
|
+
return False, []
|
|
24
|
+
|
|
25
|
+
output = [
|
|
26
|
+
object_name
|
|
27
|
+
for object_name in (dict_of_objects if output is None else output)
|
|
28
|
+
if dict_of_objects.get(
|
|
29
|
+
object_name,
|
|
30
|
+
False if isinstance(value, bool) else None,
|
|
31
|
+
)
|
|
32
|
+
== value
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
return True, [] if output is None else output
|