digitalhub 0.7.0b2__py3-none-any.whl → 0.8.0__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.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +63 -93
- digitalhub/client/__init__.py +0 -0
- digitalhub/client/_base/__init__.py +0 -0
- digitalhub/client/_base/client.py +56 -0
- digitalhub/client/api.py +63 -0
- digitalhub/client/builder.py +50 -0
- digitalhub/client/dhcore/__init__.py +0 -0
- digitalhub/client/dhcore/client.py +669 -0
- digitalhub/client/dhcore/env.py +21 -0
- digitalhub/client/dhcore/models.py +46 -0
- digitalhub/client/dhcore/utils.py +111 -0
- digitalhub/client/local/__init__.py +0 -0
- digitalhub/client/local/client.py +533 -0
- digitalhub/context/__init__.py +0 -0
- digitalhub/context/api.py +93 -0
- digitalhub/context/builder.py +94 -0
- digitalhub/context/context.py +136 -0
- digitalhub/datastores/__init__.py +0 -0
- digitalhub/datastores/_base/__init__.py +0 -0
- digitalhub/datastores/_base/datastore.py +85 -0
- digitalhub/datastores/api.py +37 -0
- digitalhub/datastores/builder.py +110 -0
- digitalhub/datastores/local/__init__.py +0 -0
- digitalhub/datastores/local/datastore.py +50 -0
- digitalhub/datastores/remote/__init__.py +0 -0
- digitalhub/datastores/remote/datastore.py +31 -0
- digitalhub/datastores/s3/__init__.py +0 -0
- digitalhub/datastores/s3/datastore.py +46 -0
- digitalhub/datastores/sql/__init__.py +0 -0
- digitalhub/datastores/sql/datastore.py +68 -0
- digitalhub/entities/__init__.py +0 -0
- digitalhub/entities/_base/__init__.py +0 -0
- digitalhub/entities/_base/_base/__init__.py +0 -0
- digitalhub/entities/_base/_base/entity.py +82 -0
- digitalhub/entities/_base/api_utils.py +620 -0
- digitalhub/entities/_base/context/__init__.py +0 -0
- digitalhub/entities/_base/context/entity.py +118 -0
- digitalhub/entities/_base/crud.py +468 -0
- digitalhub/entities/_base/entity/__init__.py +0 -0
- digitalhub/entities/_base/entity/_constructors/__init__.py +0 -0
- digitalhub/entities/_base/entity/_constructors/metadata.py +44 -0
- digitalhub/entities/_base/entity/_constructors/name.py +31 -0
- digitalhub/entities/_base/entity/_constructors/spec.py +33 -0
- digitalhub/entities/_base/entity/_constructors/status.py +52 -0
- digitalhub/entities/_base/entity/_constructors/uuid.py +26 -0
- digitalhub/entities/_base/entity/builder.py +175 -0
- digitalhub/entities/_base/entity/entity.py +106 -0
- digitalhub/entities/_base/entity/metadata.py +59 -0
- digitalhub/entities/_base/entity/spec.py +58 -0
- digitalhub/entities/_base/entity/status.py +43 -0
- digitalhub/entities/_base/executable/__init__.py +0 -0
- digitalhub/entities/_base/executable/entity.py +405 -0
- digitalhub/entities/_base/material/__init__.py +0 -0
- digitalhub/entities/_base/material/entity.py +214 -0
- digitalhub/entities/_base/material/spec.py +22 -0
- digitalhub/entities/_base/material/status.py +49 -0
- digitalhub/entities/_base/runtime_entity/__init__.py +0 -0
- digitalhub/entities/_base/runtime_entity/builder.py +106 -0
- digitalhub/entities/_base/unversioned/__init__.py +0 -0
- digitalhub/entities/_base/unversioned/builder.py +66 -0
- digitalhub/entities/_base/unversioned/entity.py +49 -0
- digitalhub/entities/_base/versioned/__init__.py +0 -0
- digitalhub/entities/_base/versioned/builder.py +68 -0
- digitalhub/entities/_base/versioned/entity.py +53 -0
- digitalhub/entities/artifact/__init__.py +0 -0
- digitalhub/entities/artifact/_base/__init__.py +0 -0
- digitalhub/entities/artifact/_base/builder.py +86 -0
- digitalhub/entities/artifact/_base/entity.py +39 -0
- digitalhub/entities/artifact/_base/spec.py +15 -0
- digitalhub/entities/artifact/_base/status.py +9 -0
- digitalhub/entities/artifact/artifact/__init__.py +0 -0
- digitalhub/entities/artifact/artifact/builder.py +18 -0
- digitalhub/entities/artifact/artifact/entity.py +32 -0
- digitalhub/entities/artifact/artifact/spec.py +27 -0
- digitalhub/entities/artifact/artifact/status.py +15 -0
- digitalhub/entities/artifact/crud.py +332 -0
- digitalhub/entities/builders.py +63 -0
- digitalhub/entities/dataitem/__init__.py +0 -0
- digitalhub/entities/dataitem/_base/__init__.py +0 -0
- digitalhub/entities/dataitem/_base/builder.py +86 -0
- digitalhub/entities/dataitem/_base/entity.py +75 -0
- digitalhub/entities/dataitem/_base/spec.py +15 -0
- digitalhub/entities/dataitem/_base/status.py +20 -0
- digitalhub/entities/dataitem/crud.py +372 -0
- digitalhub/entities/dataitem/dataitem/__init__.py +0 -0
- digitalhub/entities/dataitem/dataitem/builder.py +18 -0
- digitalhub/entities/dataitem/dataitem/entity.py +32 -0
- digitalhub/entities/dataitem/dataitem/spec.py +15 -0
- digitalhub/entities/dataitem/dataitem/status.py +9 -0
- digitalhub/entities/dataitem/iceberg/__init__.py +0 -0
- digitalhub/entities/dataitem/iceberg/builder.py +18 -0
- digitalhub/entities/dataitem/iceberg/entity.py +32 -0
- digitalhub/entities/dataitem/iceberg/spec.py +15 -0
- digitalhub/entities/dataitem/iceberg/status.py +9 -0
- digitalhub/entities/dataitem/table/__init__.py +0 -0
- digitalhub/entities/dataitem/table/builder.py +18 -0
- digitalhub/entities/dataitem/table/entity.py +146 -0
- digitalhub/entities/dataitem/table/models.py +62 -0
- digitalhub/entities/dataitem/table/spec.py +25 -0
- digitalhub/entities/dataitem/table/status.py +9 -0
- digitalhub/entities/function/__init__.py +0 -0
- digitalhub/entities/function/_base/__init__.py +0 -0
- digitalhub/entities/function/_base/builder.py +79 -0
- digitalhub/entities/function/_base/entity.py +98 -0
- digitalhub/entities/function/_base/models.py +118 -0
- digitalhub/entities/function/_base/spec.py +15 -0
- digitalhub/entities/function/_base/status.py +9 -0
- digitalhub/entities/function/crud.py +279 -0
- digitalhub/entities/model/__init__.py +0 -0
- digitalhub/entities/model/_base/__init__.py +0 -0
- digitalhub/entities/model/_base/builder.py +86 -0
- digitalhub/entities/model/_base/entity.py +34 -0
- digitalhub/entities/model/_base/spec.py +49 -0
- digitalhub/entities/model/_base/status.py +9 -0
- digitalhub/entities/model/crud.py +331 -0
- digitalhub/entities/model/huggingface/__init__.py +0 -0
- digitalhub/entities/model/huggingface/builder.py +18 -0
- digitalhub/entities/model/huggingface/entity.py +32 -0
- digitalhub/entities/model/huggingface/spec.py +36 -0
- digitalhub/entities/model/huggingface/status.py +9 -0
- digitalhub/entities/model/mlflow/__init__.py +0 -0
- digitalhub/entities/model/mlflow/builder.py +18 -0
- digitalhub/entities/model/mlflow/entity.py +32 -0
- digitalhub/entities/model/mlflow/models.py +26 -0
- digitalhub/entities/model/mlflow/spec.py +44 -0
- digitalhub/entities/model/mlflow/status.py +9 -0
- digitalhub/entities/model/mlflow/utils.py +81 -0
- digitalhub/entities/model/model/__init__.py +0 -0
- digitalhub/entities/model/model/builder.py +18 -0
- digitalhub/entities/model/model/entity.py +32 -0
- digitalhub/entities/model/model/spec.py +15 -0
- digitalhub/entities/model/model/status.py +9 -0
- digitalhub/entities/model/sklearn/__init__.py +0 -0
- digitalhub/entities/model/sklearn/builder.py +18 -0
- digitalhub/entities/model/sklearn/entity.py +32 -0
- digitalhub/entities/model/sklearn/spec.py +15 -0
- digitalhub/entities/model/sklearn/status.py +9 -0
- digitalhub/entities/project/__init__.py +0 -0
- digitalhub/entities/project/_base/__init__.py +0 -0
- digitalhub/entities/project/_base/builder.py +128 -0
- digitalhub/entities/project/_base/entity.py +2078 -0
- digitalhub/entities/project/_base/spec.py +50 -0
- digitalhub/entities/project/_base/status.py +9 -0
- digitalhub/entities/project/crud.py +357 -0
- digitalhub/entities/run/__init__.py +0 -0
- digitalhub/entities/run/_base/__init__.py +0 -0
- digitalhub/entities/run/_base/builder.py +94 -0
- digitalhub/entities/run/_base/entity.py +307 -0
- digitalhub/entities/run/_base/spec.py +50 -0
- digitalhub/entities/run/_base/status.py +9 -0
- digitalhub/entities/run/crud.py +219 -0
- digitalhub/entities/secret/__init__.py +0 -0
- digitalhub/entities/secret/_base/__init__.py +0 -0
- digitalhub/entities/secret/_base/builder.py +81 -0
- digitalhub/entities/secret/_base/entity.py +74 -0
- digitalhub/entities/secret/_base/spec.py +35 -0
- digitalhub/entities/secret/_base/status.py +9 -0
- digitalhub/entities/secret/crud.py +290 -0
- digitalhub/entities/task/__init__.py +0 -0
- digitalhub/entities/task/_base/__init__.py +0 -0
- digitalhub/entities/task/_base/builder.py +91 -0
- digitalhub/entities/task/_base/entity.py +136 -0
- digitalhub/entities/task/_base/models.py +208 -0
- digitalhub/entities/task/_base/spec.py +53 -0
- digitalhub/entities/task/_base/status.py +9 -0
- digitalhub/entities/task/crud.py +228 -0
- digitalhub/entities/utils/__init__.py +0 -0
- digitalhub/entities/utils/api.py +346 -0
- digitalhub/entities/utils/entity_types.py +19 -0
- digitalhub/entities/utils/state.py +31 -0
- digitalhub/entities/utils/utils.py +202 -0
- digitalhub/entities/workflow/__init__.py +0 -0
- digitalhub/entities/workflow/_base/__init__.py +0 -0
- digitalhub/entities/workflow/_base/builder.py +79 -0
- digitalhub/entities/workflow/_base/entity.py +74 -0
- digitalhub/entities/workflow/_base/spec.py +15 -0
- digitalhub/entities/workflow/_base/status.py +9 -0
- digitalhub/entities/workflow/crud.py +278 -0
- digitalhub/factory/__init__.py +0 -0
- digitalhub/factory/api.py +277 -0
- digitalhub/factory/factory.py +268 -0
- digitalhub/factory/utils.py +90 -0
- digitalhub/readers/__init__.py +0 -0
- digitalhub/readers/_base/__init__.py +0 -0
- digitalhub/readers/_base/builder.py +26 -0
- digitalhub/readers/_base/reader.py +70 -0
- digitalhub/readers/api.py +80 -0
- digitalhub/readers/factory.py +133 -0
- digitalhub/readers/pandas/__init__.py +0 -0
- digitalhub/readers/pandas/builder.py +29 -0
- digitalhub/readers/pandas/reader.py +207 -0
- digitalhub/runtimes/__init__.py +0 -0
- digitalhub/runtimes/_base.py +102 -0
- digitalhub/runtimes/builder.py +32 -0
- digitalhub/stores/__init__.py +0 -0
- digitalhub/stores/_base/__init__.py +0 -0
- digitalhub/stores/_base/store.py +189 -0
- digitalhub/stores/api.py +54 -0
- digitalhub/stores/builder.py +211 -0
- digitalhub/stores/local/__init__.py +0 -0
- digitalhub/stores/local/store.py +230 -0
- digitalhub/stores/remote/__init__.py +0 -0
- digitalhub/stores/remote/store.py +143 -0
- digitalhub/stores/s3/__init__.py +0 -0
- digitalhub/stores/s3/store.py +563 -0
- digitalhub/stores/sql/__init__.py +0 -0
- digitalhub/stores/sql/store.py +328 -0
- digitalhub/utils/__init__.py +0 -0
- digitalhub/utils/data_utils.py +127 -0
- digitalhub/utils/exceptions.py +67 -0
- digitalhub/utils/file_utils.py +204 -0
- digitalhub/utils/generic_utils.py +183 -0
- digitalhub/utils/git_utils.py +148 -0
- digitalhub/utils/io_utils.py +116 -0
- digitalhub/utils/logger.py +17 -0
- digitalhub/utils/s3_utils.py +58 -0
- digitalhub/utils/uri_utils.py +56 -0
- {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/METADATA +30 -13
- digitalhub-0.8.0.dist-info/RECORD +231 -0
- {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/WHEEL +1 -1
- test/local/CRUD/test_artifacts.py +96 -0
- test/local/CRUD/test_dataitems.py +96 -0
- test/local/CRUD/test_models.py +95 -0
- test/test_crud_functions.py +1 -1
- test/test_crud_runs.py +1 -1
- test/test_crud_tasks.py +1 -1
- digitalhub-0.7.0b2.dist-info/RECORD +0 -14
- test/test_crud_artifacts.py +0 -96
- test/test_crud_dataitems.py +0 -96
- {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/top_level.txt +0 -0
- /test/{test_imports.py → local/imports/test_imports.py} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
from zipfile import ZipFile
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from requests import get as requests_get
|
|
12
|
+
from slugify import slugify
|
|
13
|
+
|
|
14
|
+
from digitalhub.utils.io_utils import read_text
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_timestamp() -> str:
|
|
18
|
+
"""
|
|
19
|
+
Get the current timestamp timezoned.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
str
|
|
24
|
+
The current timestamp.
|
|
25
|
+
"""
|
|
26
|
+
return datetime.now().astimezone().isoformat()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def decode_string(string: str) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Decode a string from base64.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
string : str
|
|
36
|
+
The string to decode.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
str
|
|
41
|
+
The string decoded from base64.
|
|
42
|
+
"""
|
|
43
|
+
return base64.b64decode(string).decode()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def encode_string(string: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Encode a string in base64.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
string : str
|
|
53
|
+
The string to encode.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
str
|
|
58
|
+
The string encoded in base64.
|
|
59
|
+
"""
|
|
60
|
+
return base64.b64encode(string.encode()).decode()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def encode_source(path: str) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Read a file and encode in base64 the content.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
path : str
|
|
70
|
+
The file path to read.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
str
|
|
75
|
+
The file content encoded in base64.
|
|
76
|
+
"""
|
|
77
|
+
return encode_string(read_text(path))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def requests_chunk_download(source: str, filename: Path) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Download a file in chunks.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
source : str
|
|
87
|
+
URL to download the file.
|
|
88
|
+
filename : Path
|
|
89
|
+
Path where to save the file.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
None
|
|
94
|
+
"""
|
|
95
|
+
with requests_get(source, stream=True) as r:
|
|
96
|
+
r.raise_for_status()
|
|
97
|
+
with filename.open("wb") as f:
|
|
98
|
+
for chunk in r.iter_content(chunk_size=8192):
|
|
99
|
+
f.write(chunk)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def extract_archive(path: Path, filename: Path) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Extract a zip archive.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
path : Path
|
|
109
|
+
Path where to extract the archive.
|
|
110
|
+
filename : Path
|
|
111
|
+
Path to the archive.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
None
|
|
116
|
+
"""
|
|
117
|
+
with ZipFile(filename, "r") as zip_file:
|
|
118
|
+
zip_file.extractall(path)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class MyEncoder(json.JSONEncoder):
|
|
122
|
+
"""
|
|
123
|
+
Custom JSON encoder to handle numpy types.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def default(self, obj: Any) -> Any:
|
|
127
|
+
"""
|
|
128
|
+
Convert numpy types to json.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
obj : Any
|
|
133
|
+
The object to convert.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
Any
|
|
138
|
+
The object converted to json.
|
|
139
|
+
"""
|
|
140
|
+
if isinstance(obj, (int, str, float, list, dict)):
|
|
141
|
+
return obj
|
|
142
|
+
elif isinstance(obj, (np.integer, np.int64)):
|
|
143
|
+
return int(obj)
|
|
144
|
+
elif isinstance(obj, (np.floating, np.float64)):
|
|
145
|
+
return float(obj)
|
|
146
|
+
elif isinstance(obj, np.ndarray):
|
|
147
|
+
return obj.tolist()
|
|
148
|
+
else:
|
|
149
|
+
return str(obj)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def dict_to_json(struct: dict) -> str:
|
|
153
|
+
"""
|
|
154
|
+
Convert a dict to json.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
struct : dict
|
|
159
|
+
The dict to convert.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
str
|
|
164
|
+
The json string.
|
|
165
|
+
"""
|
|
166
|
+
return json.dumps(struct, cls=MyEncoder)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def slugify_string(filename: str) -> str:
|
|
170
|
+
"""
|
|
171
|
+
Sanitize a filename.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
filename : str
|
|
176
|
+
The filename to sanitize.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
str
|
|
181
|
+
The sanitized filename.
|
|
182
|
+
"""
|
|
183
|
+
return slugify(filename, max_length=255)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import warnings
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from git import Repo
|
|
11
|
+
except ImportError as e:
|
|
12
|
+
if "Bad git executable." in e.args[0]:
|
|
13
|
+
warnings.warn("git is not installed. Please install git and try again.", RuntimeWarning)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def clone_repository(url: str, path: Path) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Clone git repository.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
url : str
|
|
23
|
+
URL of the repository.
|
|
24
|
+
path : Path
|
|
25
|
+
Path where to save the repository.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
None
|
|
30
|
+
"""
|
|
31
|
+
clean_path(path)
|
|
32
|
+
checkout_object = get_checkout_object(url)
|
|
33
|
+
url = add_credentials_git_remote_url(url)
|
|
34
|
+
repo = clone_from_url(url, path)
|
|
35
|
+
if checkout_object != "":
|
|
36
|
+
repo.git.checkout(checkout_object)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_checkout_object(url: str) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Get checkout object from url fragment.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
url : str
|
|
46
|
+
URL of the repository.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
str
|
|
51
|
+
Checkout object (branch, tag, commit).
|
|
52
|
+
"""
|
|
53
|
+
return urlparse(url).fragment
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def clean_path(path: Path) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Clean path from any files.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
path : Path
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
None
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_git_username_password_from_token(token: str) -> tuple[str, str]:
|
|
73
|
+
"""
|
|
74
|
+
Parse token to get username and password. The token
|
|
75
|
+
can be one of the following:
|
|
76
|
+
|
|
77
|
+
- GitHub/GitLab personal access token (github_pat_.../glpat...)
|
|
78
|
+
- GitHub/GitLab access token
|
|
79
|
+
- Other generic token
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
token : str
|
|
84
|
+
Token to parse.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
tuple[str, str]
|
|
89
|
+
Username and password.
|
|
90
|
+
"""
|
|
91
|
+
# Mutued from mlrun
|
|
92
|
+
if token.startswith("github_pat_") or token.startswith("glpat"):
|
|
93
|
+
username = "oauth2"
|
|
94
|
+
password = token
|
|
95
|
+
else:
|
|
96
|
+
username = token
|
|
97
|
+
password = "x-oauth-basic"
|
|
98
|
+
return username, password
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def add_credentials_git_remote_url(url: str) -> str:
|
|
102
|
+
"""
|
|
103
|
+
Add credentials to git remote url.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
url : str
|
|
108
|
+
URL of the repository.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
str
|
|
113
|
+
URL with credentials.
|
|
114
|
+
"""
|
|
115
|
+
url_obj = urlparse(url)
|
|
116
|
+
|
|
117
|
+
# Get credentials from environment variables
|
|
118
|
+
username = os.getenv("GIT_USERNAME")
|
|
119
|
+
password = os.getenv("GIT_PASSWORD")
|
|
120
|
+
token = os.getenv("GIT_TOKEN")
|
|
121
|
+
|
|
122
|
+
# Get credentials from token. Override username and password
|
|
123
|
+
if token is not None:
|
|
124
|
+
username, password = get_git_username_password_from_token(token)
|
|
125
|
+
|
|
126
|
+
# Add credentials to url if needed
|
|
127
|
+
if username is not None and password is not None:
|
|
128
|
+
return f"https://{username}:{password}@{url_obj.hostname}{url_obj.path}"
|
|
129
|
+
return f"https://{url_obj.hostname}{url_obj.path}"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def clone_from_url(url: str, path: Path) -> Repo:
|
|
133
|
+
"""
|
|
134
|
+
Clone repository from url. Wraps git.Repo.clone_from.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
url : str
|
|
139
|
+
HTTP(S) URL of the repository.
|
|
140
|
+
path : Path
|
|
141
|
+
Path where to save the repository.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
Repo
|
|
146
|
+
Cloned repository.
|
|
147
|
+
"""
|
|
148
|
+
return Repo.clone_from(url=url, to_path=path)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
##############################
|
|
8
|
+
# Writers
|
|
9
|
+
##############################
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def write_yaml(filepath: str | Path, obj: dict | list[dict]) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Write a dict or a list of dict to a yaml file.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
filepath : str | Path
|
|
19
|
+
The yaml file path to write.
|
|
20
|
+
obj : dict
|
|
21
|
+
The dict to write.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
None
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(obj, list):
|
|
28
|
+
with open(filepath, "w", encoding="utf-8") as out_file:
|
|
29
|
+
yaml.dump_all(obj, out_file, sort_keys=False, default_flow_style=False)
|
|
30
|
+
else:
|
|
31
|
+
with open(filepath, "w", encoding="utf-8") as out_file:
|
|
32
|
+
yaml.dump(obj, out_file, sort_keys=False)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##############################
|
|
36
|
+
# Readers
|
|
37
|
+
##############################
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class NoDatesSafeLoader(yaml.SafeLoader):
|
|
41
|
+
"""
|
|
42
|
+
Loader implementation to exclude implicit resolvers.
|
|
43
|
+
|
|
44
|
+
Taken from https://stackoverflow.com/a/37958106
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def remove_implicit_resolver(cls, tag_to_remove: str) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Remove implicit resolvers for a particular tag
|
|
51
|
+
Takes care not to modify resolvers in super classes.
|
|
52
|
+
We want to load datetimes as strings, not dates, because we
|
|
53
|
+
go on to serialise as json which doesn't have the advanced types
|
|
54
|
+
of yaml, and leads to incompatibilities down the track.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
tag_to_remove : str
|
|
59
|
+
The tag to remove
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
None
|
|
64
|
+
"""
|
|
65
|
+
if "yaml_implicit_resolvers" not in cls.__dict__:
|
|
66
|
+
cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy()
|
|
67
|
+
|
|
68
|
+
for first_letter, mappings in cls.yaml_implicit_resolvers.items():
|
|
69
|
+
cls.yaml_implicit_resolvers[first_letter] = [
|
|
70
|
+
(tag, regexp) for tag, regexp in mappings if tag != tag_to_remove
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
NoDatesSafeLoader.remove_implicit_resolver("tag:yaml.org,2002:timestamp")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def read_yaml(filepath: str | Path) -> dict | list[dict]:
|
|
78
|
+
"""
|
|
79
|
+
Read a yaml file and return a dict or a list of dict.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
filepath : str | Path
|
|
84
|
+
The yaml file path to read.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
dict | list[dict]
|
|
89
|
+
The yaml file content.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
with open(filepath, "r", encoding="utf-8") as in_file:
|
|
93
|
+
data = yaml.load(in_file, Loader=NoDatesSafeLoader)
|
|
94
|
+
|
|
95
|
+
# If yaml contains multiple documents
|
|
96
|
+
except yaml.composer.ComposerError:
|
|
97
|
+
with open(filepath, "r", encoding="utf-8") as in_file:
|
|
98
|
+
data = list(yaml.load_all(in_file, Loader=NoDatesSafeLoader))
|
|
99
|
+
return data
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def read_text(filepath: str | Path) -> str:
|
|
103
|
+
"""
|
|
104
|
+
Read a file and return the text.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
filepath : str | Path
|
|
109
|
+
The file path to read.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
str
|
|
114
|
+
The file content.
|
|
115
|
+
"""
|
|
116
|
+
return Path(filepath).read_text(encoding="utf-8")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
# Create logger
|
|
6
|
+
LOGGER = logging.getLogger("digitalhub-core")
|
|
7
|
+
LOGGER.setLevel(logging.INFO)
|
|
8
|
+
|
|
9
|
+
# Create formatter
|
|
10
|
+
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
|
11
|
+
|
|
12
|
+
# Create console handler and set formatter
|
|
13
|
+
console_handler = logging.StreamHandler()
|
|
14
|
+
console_handler.setFormatter(formatter)
|
|
15
|
+
|
|
16
|
+
# Set console handler to the logger
|
|
17
|
+
LOGGER.addHandler(console_handler)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
from boto3 import client as boto3_client
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_bucket_and_key(path: str) -> tuple[str, str]:
|
|
11
|
+
"""
|
|
12
|
+
Get bucket and key from path.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
path : str
|
|
17
|
+
The source path to get the key from.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
tuple[str, str]
|
|
22
|
+
The bucket and key.
|
|
23
|
+
"""
|
|
24
|
+
parsed = urlparse(path)
|
|
25
|
+
return parsed.netloc, parsed.path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_s3_source(bucket: str, key: str, filename: Path) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Get S3 source.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
bucket : str
|
|
35
|
+
S3 bucket name.
|
|
36
|
+
key : str
|
|
37
|
+
S3 object key.
|
|
38
|
+
filename : Path
|
|
39
|
+
Path where to save the function source.
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
None
|
|
44
|
+
"""
|
|
45
|
+
s3 = boto3_client("s3", endpoint_url=os.getenv("S3_ENDPOINT_URL"))
|
|
46
|
+
s3.download_file(bucket, key, filename)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_s3_bucket() -> str | None:
|
|
50
|
+
"""
|
|
51
|
+
Function to get S3 bucket name.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
str
|
|
56
|
+
The S3 bucket name.
|
|
57
|
+
"""
|
|
58
|
+
return os.getenv("S3_BUCKET_NAME", "datalake")
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def map_uri_scheme(uri: str) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Map an URI scheme to a common scheme.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
uri : str
|
|
13
|
+
URI.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
str
|
|
18
|
+
Mapped scheme type.
|
|
19
|
+
|
|
20
|
+
Raises
|
|
21
|
+
------
|
|
22
|
+
ValueError
|
|
23
|
+
If the scheme is unknown.
|
|
24
|
+
"""
|
|
25
|
+
scheme = urlparse(uri).scheme
|
|
26
|
+
if scheme in [""]:
|
|
27
|
+
return "local"
|
|
28
|
+
if scheme in ["file", "local"]:
|
|
29
|
+
raise ValueError("For local path, do not use any scheme")
|
|
30
|
+
if scheme in ["http", "https"]:
|
|
31
|
+
return "remote"
|
|
32
|
+
if scheme in ["s3", "s3a", "s3n", "zip+s3"]:
|
|
33
|
+
return "s3"
|
|
34
|
+
if scheme in ["sql", "postgresql"]:
|
|
35
|
+
return "sql"
|
|
36
|
+
if scheme in ["git", "git+http", "git+https"]:
|
|
37
|
+
return "git"
|
|
38
|
+
raise ValueError(f"Unknown scheme '{scheme}'!")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def check_local_path(path: str) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Check if path is local.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
path : str
|
|
48
|
+
Path of some source.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
bool
|
|
53
|
+
True if path is local.
|
|
54
|
+
"""
|
|
55
|
+
scheme = map_uri_scheme(path)
|
|
56
|
+
return scheme == "local"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: digitalhub
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Python SDK for Digitalhub
|
|
5
5
|
Author-email: Fondazione Bruno Kessler <dslab@fbk.eu>, Matteo Martini <mmartini@fbk.eu>
|
|
6
6
|
License: Apache License
|
|
@@ -225,25 +225,42 @@ Keywords: data,dataops,kubernetes
|
|
|
225
225
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
226
226
|
Classifier: Programming Language :: Python :: 3.9
|
|
227
227
|
Classifier: Programming Language :: Python :: 3.10
|
|
228
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
228
229
|
Requires-Python: >=3.9
|
|
229
230
|
Description-Content-Type: text/markdown
|
|
230
231
|
License-File: LICENSE.txt
|
|
231
|
-
Requires-Dist:
|
|
232
|
-
|
|
233
|
-
Requires-Dist:
|
|
234
|
-
Requires-Dist:
|
|
235
|
-
|
|
236
|
-
Requires-Dist:
|
|
232
|
+
Requires-Dist: boto3
|
|
233
|
+
Requires-Dist: pydantic<2
|
|
234
|
+
Requires-Dist: sqlalchemy<2
|
|
235
|
+
Requires-Dist: pyarrow
|
|
236
|
+
Requires-Dist: numpy<2
|
|
237
|
+
Requires-Dist: requests
|
|
238
|
+
Requires-Dist: PyYAML
|
|
239
|
+
Requires-Dist: python-dotenv
|
|
240
|
+
Requires-Dist: GitPython>=3
|
|
241
|
+
Requires-Dist: psycopg2-binary
|
|
242
|
+
Requires-Dist: python-slugify
|
|
243
|
+
Provides-Extra: dev
|
|
244
|
+
Requires-Dist: black; extra == "dev"
|
|
245
|
+
Requires-Dist: pytest; extra == "dev"
|
|
246
|
+
Requires-Dist: bumpver; extra == "dev"
|
|
247
|
+
Requires-Dist: ruff; extra == "dev"
|
|
248
|
+
Requires-Dist: moto; extra == "dev"
|
|
249
|
+
Provides-Extra: docs
|
|
250
|
+
Requires-Dist: Sphinx>=7; extra == "docs"
|
|
251
|
+
Requires-Dist: pydata-sphinx-theme>=0.15; extra == "docs"
|
|
252
|
+
Requires-Dist: numpydoc>=1.6; extra == "docs"
|
|
237
253
|
Provides-Extra: full
|
|
238
|
-
Requires-Dist:
|
|
239
|
-
Requires-Dist:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
254
|
+
Requires-Dist: pandas<2.2,>=1.2; extra == "full"
|
|
255
|
+
Requires-Dist: mlflow; extra == "full"
|
|
256
|
+
Provides-Extra: mlflow
|
|
257
|
+
Requires-Dist: mlflow; extra == "mlflow"
|
|
258
|
+
Provides-Extra: pandas
|
|
259
|
+
Requires-Dist: pandas<2.2,>=1.2; extra == "pandas"
|
|
243
260
|
|
|
244
261
|
# Digitalhub Library
|
|
245
262
|
|
|
246
263
|
The Digitalhub SDK library is used to manage entities and executions in Digitalhub from Python.
|
|
247
264
|
It comes with a suite of tools to help you manage your projects and executions. It exposes CRUD methods to create, read, update and delete entities, and objects methods to excute functions or workflows, collect or store execution results and data.
|
|
248
265
|
|
|
249
|
-
A more detailed description of the library can be found in the [official documentation](https://scc-digitalhub.github.io/docs/)
|
|
266
|
+
A more detailed description of the library can be found in the [official documentation](https://scc-digitalhub.github.io/sdk-docs/).
|