digitalhub 0.8.0b0__py3-none-any.whl → 0.8.0b1__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.

Files changed (159) hide show
  1. digitalhub/__init__.py +62 -94
  2. digitalhub/client/__init__.py +0 -0
  3. digitalhub/client/builder.py +105 -0
  4. digitalhub/client/objects/__init__.py +0 -0
  5. digitalhub/client/objects/base.py +56 -0
  6. digitalhub/client/objects/dhcore.py +681 -0
  7. digitalhub/client/objects/local.py +533 -0
  8. digitalhub/context/__init__.py +0 -0
  9. digitalhub/context/builder.py +178 -0
  10. digitalhub/context/context.py +136 -0
  11. digitalhub/datastores/__init__.py +0 -0
  12. digitalhub/datastores/builder.py +134 -0
  13. digitalhub/datastores/objects/__init__.py +0 -0
  14. digitalhub/datastores/objects/base.py +85 -0
  15. digitalhub/datastores/objects/local.py +42 -0
  16. digitalhub/datastores/objects/remote.py +23 -0
  17. digitalhub/datastores/objects/s3.py +38 -0
  18. digitalhub/datastores/objects/sql.py +60 -0
  19. digitalhub/entities/__init__.py +0 -0
  20. digitalhub/entities/_base/__init__.py +0 -0
  21. digitalhub/entities/_base/api.py +346 -0
  22. digitalhub/entities/_base/base.py +82 -0
  23. digitalhub/entities/_base/crud.py +610 -0
  24. digitalhub/entities/_base/entity/__init__.py +0 -0
  25. digitalhub/entities/_base/entity/base.py +132 -0
  26. digitalhub/entities/_base/entity/context.py +118 -0
  27. digitalhub/entities/_base/entity/executable.py +380 -0
  28. digitalhub/entities/_base/entity/material.py +214 -0
  29. digitalhub/entities/_base/entity/unversioned.py +87 -0
  30. digitalhub/entities/_base/entity/versioned.py +94 -0
  31. digitalhub/entities/_base/metadata.py +59 -0
  32. digitalhub/entities/_base/spec/__init__.py +0 -0
  33. digitalhub/entities/_base/spec/base.py +58 -0
  34. digitalhub/entities/_base/spec/material.py +22 -0
  35. digitalhub/entities/_base/state.py +31 -0
  36. digitalhub/entities/_base/status/__init__.py +0 -0
  37. digitalhub/entities/_base/status/base.py +32 -0
  38. digitalhub/entities/_base/status/material.py +49 -0
  39. digitalhub/entities/_builders/__init__.py +0 -0
  40. digitalhub/entities/_builders/metadata.py +60 -0
  41. digitalhub/entities/_builders/name.py +31 -0
  42. digitalhub/entities/_builders/spec.py +43 -0
  43. digitalhub/entities/_builders/status.py +62 -0
  44. digitalhub/entities/_builders/uuid.py +33 -0
  45. digitalhub/entities/artifact/__init__.py +0 -0
  46. digitalhub/entities/artifact/builder.py +133 -0
  47. digitalhub/entities/artifact/crud.py +358 -0
  48. digitalhub/entities/artifact/entity/__init__.py +0 -0
  49. digitalhub/entities/artifact/entity/_base.py +39 -0
  50. digitalhub/entities/artifact/entity/artifact.py +9 -0
  51. digitalhub/entities/artifact/spec.py +39 -0
  52. digitalhub/entities/artifact/status.py +15 -0
  53. digitalhub/entities/dataitem/__init__.py +0 -0
  54. digitalhub/entities/dataitem/builder.py +144 -0
  55. digitalhub/entities/dataitem/crud.py +395 -0
  56. digitalhub/entities/dataitem/entity/__init__.py +0 -0
  57. digitalhub/entities/dataitem/entity/_base.py +75 -0
  58. digitalhub/entities/dataitem/entity/dataitem.py +9 -0
  59. digitalhub/entities/dataitem/entity/iceberg.py +7 -0
  60. digitalhub/entities/dataitem/entity/table.py +125 -0
  61. digitalhub/entities/dataitem/models.py +62 -0
  62. digitalhub/entities/dataitem/spec.py +61 -0
  63. digitalhub/entities/dataitem/status.py +38 -0
  64. digitalhub/entities/entity_types.py +19 -0
  65. digitalhub/entities/function/__init__.py +0 -0
  66. digitalhub/entities/function/builder.py +86 -0
  67. digitalhub/entities/function/crud.py +305 -0
  68. digitalhub/entities/function/entity.py +101 -0
  69. digitalhub/entities/function/models.py +118 -0
  70. digitalhub/entities/function/spec.py +81 -0
  71. digitalhub/entities/function/status.py +9 -0
  72. digitalhub/entities/model/__init__.py +0 -0
  73. digitalhub/entities/model/builder.py +152 -0
  74. digitalhub/entities/model/crud.py +358 -0
  75. digitalhub/entities/model/entity/__init__.py +0 -0
  76. digitalhub/entities/model/entity/_base.py +34 -0
  77. digitalhub/entities/model/entity/huggingface.py +9 -0
  78. digitalhub/entities/model/entity/mlflow.py +90 -0
  79. digitalhub/entities/model/entity/model.py +9 -0
  80. digitalhub/entities/model/entity/sklearn.py +9 -0
  81. digitalhub/entities/model/models.py +26 -0
  82. digitalhub/entities/model/spec.py +146 -0
  83. digitalhub/entities/model/status.py +33 -0
  84. digitalhub/entities/project/__init__.py +0 -0
  85. digitalhub/entities/project/builder.py +82 -0
  86. digitalhub/entities/project/crud.py +350 -0
  87. digitalhub/entities/project/entity.py +2060 -0
  88. digitalhub/entities/project/spec.py +50 -0
  89. digitalhub/entities/project/status.py +9 -0
  90. digitalhub/entities/registries.py +48 -0
  91. digitalhub/entities/run/__init__.py +0 -0
  92. digitalhub/entities/run/builder.py +77 -0
  93. digitalhub/entities/run/crud.py +232 -0
  94. digitalhub/entities/run/entity.py +461 -0
  95. digitalhub/entities/run/spec.py +153 -0
  96. digitalhub/entities/run/status.py +114 -0
  97. digitalhub/entities/secret/__init__.py +0 -0
  98. digitalhub/entities/secret/builder.py +93 -0
  99. digitalhub/entities/secret/crud.py +294 -0
  100. digitalhub/entities/secret/entity.py +73 -0
  101. digitalhub/entities/secret/spec.py +35 -0
  102. digitalhub/entities/secret/status.py +9 -0
  103. digitalhub/entities/task/__init__.py +0 -0
  104. digitalhub/entities/task/builder.py +74 -0
  105. digitalhub/entities/task/crud.py +241 -0
  106. digitalhub/entities/task/entity.py +135 -0
  107. digitalhub/entities/task/models.py +199 -0
  108. digitalhub/entities/task/spec.py +51 -0
  109. digitalhub/entities/task/status.py +9 -0
  110. digitalhub/entities/utils.py +184 -0
  111. digitalhub/entities/workflow/__init__.py +0 -0
  112. digitalhub/entities/workflow/builder.py +91 -0
  113. digitalhub/entities/workflow/crud.py +304 -0
  114. digitalhub/entities/workflow/entity.py +77 -0
  115. digitalhub/entities/workflow/spec.py +15 -0
  116. digitalhub/entities/workflow/status.py +9 -0
  117. digitalhub/readers/__init__.py +0 -0
  118. digitalhub/readers/builder.py +54 -0
  119. digitalhub/readers/objects/__init__.py +0 -0
  120. digitalhub/readers/objects/base.py +70 -0
  121. digitalhub/readers/objects/pandas.py +207 -0
  122. digitalhub/readers/registry.py +15 -0
  123. digitalhub/registry/__init__.py +0 -0
  124. digitalhub/registry/models.py +87 -0
  125. digitalhub/registry/registry.py +74 -0
  126. digitalhub/registry/utils.py +150 -0
  127. digitalhub/runtimes/__init__.py +0 -0
  128. digitalhub/runtimes/base.py +164 -0
  129. digitalhub/runtimes/builder.py +53 -0
  130. digitalhub/runtimes/kind_registry.py +170 -0
  131. digitalhub/stores/__init__.py +0 -0
  132. digitalhub/stores/builder.py +257 -0
  133. digitalhub/stores/objects/__init__.py +0 -0
  134. digitalhub/stores/objects/base.py +189 -0
  135. digitalhub/stores/objects/local.py +230 -0
  136. digitalhub/stores/objects/remote.py +143 -0
  137. digitalhub/stores/objects/s3.py +563 -0
  138. digitalhub/stores/objects/sql.py +328 -0
  139. digitalhub/utils/__init__.py +0 -0
  140. digitalhub/utils/data_utils.py +127 -0
  141. digitalhub/utils/env_utils.py +123 -0
  142. digitalhub/utils/exceptions.py +55 -0
  143. digitalhub/utils/file_utils.py +204 -0
  144. digitalhub/utils/generic_utils.py +207 -0
  145. digitalhub/utils/git_utils.py +148 -0
  146. digitalhub/utils/io_utils.py +79 -0
  147. digitalhub/utils/logger.py +17 -0
  148. digitalhub/utils/uri_utils.py +56 -0
  149. {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/METADATA +27 -12
  150. digitalhub-0.8.0b1.dist-info/RECORD +161 -0
  151. test/test_crud_artifacts.py +1 -1
  152. test/test_crud_dataitems.py +1 -1
  153. test/test_crud_functions.py +1 -1
  154. test/test_crud_runs.py +1 -1
  155. test/test_crud_tasks.py +1 -1
  156. digitalhub-0.8.0b0.dist-info/RECORD +0 -14
  157. {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/LICENSE.txt +0 -0
  158. {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/WHEEL +0 -0
  159. {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,204 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from hashlib import sha256
5
+ from mimetypes import guess_type
6
+ from pathlib import Path
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ class FileInfo(BaseModel):
12
+ """
13
+ File info class.
14
+ """
15
+
16
+ path: str = None
17
+ name: str = None
18
+ content_type: str = None
19
+ size: int = None
20
+ hash: str = None
21
+ last_modified: str = None
22
+
23
+
24
+ def calculate_blob_hash(data_path: str) -> str:
25
+ """
26
+ Calculate the hash of a file.
27
+
28
+ Parameters
29
+ ----------
30
+ data_path : str
31
+ Path to the file.
32
+
33
+ Returns
34
+ -------
35
+ str
36
+ The hash of the file.
37
+ """
38
+ with open(data_path, "rb") as f:
39
+ data = f.read()
40
+ return f"sha256:{sha256(data).hexdigest()}"
41
+
42
+
43
+ def get_file_size(data_path: str) -> int:
44
+ """
45
+ Get the size of a file.
46
+
47
+ Parameters
48
+ ----------
49
+ data_path : str
50
+ Path to the file.
51
+
52
+ Returns
53
+ -------
54
+ int
55
+ The size of the file.
56
+ """
57
+ return Path(data_path).stat().st_size
58
+
59
+
60
+ def get_file_mime_type(data_path: str) -> str:
61
+ """
62
+ Get the mime type of a file.
63
+
64
+ Parameters
65
+ ----------
66
+ data_path : str
67
+ Path to the file.
68
+
69
+ Returns
70
+ -------
71
+ str
72
+ The mime type of the file.
73
+ """
74
+ return guess_type(data_path)[0]
75
+
76
+
77
+ def get_path_name(data_path: str) -> str:
78
+ """
79
+ Get the name of a file.
80
+
81
+ Parameters
82
+ ----------
83
+ data_path : str
84
+ Path to the file.
85
+
86
+ Returns
87
+ -------
88
+ str
89
+ The name of the file.
90
+ """
91
+ return Path(data_path).name
92
+
93
+
94
+ def get_last_modified(data_path: str) -> str:
95
+ """
96
+ Get the last modified date of a file.
97
+
98
+ Parameters
99
+ ----------
100
+ data_path : str
101
+ Path to the file.
102
+
103
+ Returns
104
+ -------
105
+ str
106
+ The last modified date of the file.
107
+ """
108
+ path = Path(data_path)
109
+ timestamp = path.stat().st_mtime
110
+ return datetime.fromtimestamp(timestamp).astimezone().isoformat()
111
+
112
+
113
+ def get_s3_path(src_path: str) -> str:
114
+ """
115
+ Get the S3 path of a file.
116
+
117
+ Parameters
118
+ ----------
119
+ src_path : str
120
+ Path to the file.
121
+
122
+ Returns
123
+ -------
124
+ str
125
+ The S3 path of the file.
126
+ """
127
+ return Path(src_path).as_uri()
128
+
129
+
130
+ def get_file_info_from_local(path: str, src_path: str) -> None | dict:
131
+ """
132
+ Get file info from path.
133
+
134
+ Parameters
135
+ ----------
136
+ path : str
137
+ Target path of the object.
138
+ src_path : str
139
+ Local path of some source.
140
+
141
+ Returns
142
+ -------
143
+ dict
144
+ File info.
145
+ """
146
+ try:
147
+ name = get_path_name(path)
148
+ content_type = get_file_mime_type(path)
149
+ size = get_file_size(path)
150
+ hash = calculate_blob_hash(path)
151
+ last_modified = get_last_modified(path)
152
+
153
+ return FileInfo(
154
+ path=src_path,
155
+ name=name,
156
+ content_type=content_type,
157
+ size=size,
158
+ hash=hash,
159
+ last_modified=last_modified,
160
+ ).dict()
161
+ except Exception:
162
+ return None
163
+
164
+
165
+ def get_file_info_from_s3(path: str, metadata: dict) -> None | dict:
166
+ """
167
+ Get file info from path.
168
+
169
+ Parameters
170
+ ----------
171
+ path : str
172
+ Object source path.
173
+ metadata : dict
174
+ Metadata of the object from S3.
175
+
176
+ Returns
177
+ -------
178
+ dict
179
+ File info.
180
+ """
181
+ try:
182
+ size = metadata["ContentLength"]
183
+ file_hash = metadata["ETag"][1:-1]
184
+
185
+ file_size_limit_multipart = 20 * 1024 * 1024
186
+ if size < file_size_limit_multipart:
187
+ file_hash = "md5:" + file_hash
188
+ else:
189
+ file_hash = "LiteralETag:" + file_hash
190
+
191
+ name = get_path_name(path)
192
+ content_type = metadata["ContentType"]
193
+ last_modified = metadata["LastModified"].isoformat()
194
+
195
+ return FileInfo(
196
+ path=path,
197
+ name=name,
198
+ content_type=content_type,
199
+ size=size,
200
+ hash=file_hash,
201
+ last_modified=last_modified,
202
+ ).dict()
203
+ except Exception:
204
+ return None
@@ -0,0 +1,207 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import json
5
+ import os
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ from typing import Any
9
+ from urllib.parse import urlparse
10
+ from zipfile import ZipFile
11
+
12
+ import numpy as np
13
+ from boto3 import client as boto3_client
14
+ from requests import get as requests_get
15
+
16
+ from digitalhub.utils.io_utils import read_text
17
+
18
+
19
+ def get_timestamp() -> str:
20
+ """
21
+ Get the current timestamp timezoned.
22
+
23
+ Returns
24
+ -------
25
+ str
26
+ The current timestamp.
27
+ """
28
+ return datetime.now().astimezone().isoformat()
29
+
30
+
31
+ def decode_string(string: str) -> str:
32
+ """
33
+ Decode a string from base64.
34
+
35
+ Parameters
36
+ ----------
37
+ string : str
38
+ The string to decode.
39
+
40
+ Returns
41
+ -------
42
+ str
43
+ The string decoded from base64.
44
+ """
45
+ return base64.b64decode(string).decode()
46
+
47
+
48
+ def encode_string(string: str) -> str:
49
+ """
50
+ Encode a string in base64.
51
+
52
+ Parameters
53
+ ----------
54
+ string : str
55
+ The string to encode.
56
+
57
+ Returns
58
+ -------
59
+ str
60
+ The string encoded in base64.
61
+ """
62
+ return base64.b64encode(string.encode()).decode()
63
+
64
+
65
+ def encode_source(path: str) -> str:
66
+ """
67
+ Read a file and encode in base64 the content.
68
+
69
+ Parameters
70
+ ----------
71
+ path : str
72
+ The file path to read.
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ The file content encoded in base64.
78
+ """
79
+ return encode_string(read_text(path))
80
+
81
+
82
+ def requests_chunk_download(source: str, filename: Path) -> None:
83
+ """
84
+ Download a file in chunks.
85
+
86
+ Parameters
87
+ ----------
88
+ source : str
89
+ URL to download the file.
90
+ filename : Path
91
+ Path where to save the file.
92
+
93
+ Returns
94
+ -------
95
+ None
96
+ """
97
+ with requests_get(source, stream=True) as r:
98
+ r.raise_for_status()
99
+ with filename.open("wb") as f:
100
+ for chunk in r.iter_content(chunk_size=8192):
101
+ f.write(chunk)
102
+
103
+
104
+ def extract_archive(path: Path, filename: Path) -> None:
105
+ """
106
+ Extract a zip archive.
107
+
108
+ Parameters
109
+ ----------
110
+ path : Path
111
+ Path where to extract the archive.
112
+ filename : Path
113
+ Path to the archive.
114
+
115
+ Returns
116
+ -------
117
+ None
118
+ """
119
+ with ZipFile(filename, "r") as zip_file:
120
+ zip_file.extractall(path)
121
+
122
+
123
+ def get_s3_source(bucket: str, key: str, filename: Path) -> None:
124
+ """
125
+ Get S3 source.
126
+
127
+ Parameters
128
+ ----------
129
+ bucket : str
130
+ S3 bucket name.
131
+ key : str
132
+ S3 object key.
133
+ filename : Path
134
+ Path where to save the function source.
135
+
136
+ Returns
137
+ -------
138
+ None
139
+ """
140
+ s3 = boto3_client("s3", endpoint_url=os.getenv("S3_ENDPOINT_URL"))
141
+ s3.download_file(bucket, key, filename)
142
+
143
+
144
+ def get_bucket_and_key(path: str) -> tuple[str, str]:
145
+ """
146
+ Get bucket and key from path.
147
+
148
+ Parameters
149
+ ----------
150
+ path : str
151
+ The source path to get the key from.
152
+
153
+ Returns
154
+ -------
155
+ tuple[str, str]
156
+ The bucket and key.
157
+ """
158
+ parsed = urlparse(path)
159
+ return parsed.netloc, parsed.path
160
+
161
+
162
+ class MyEncoder(json.JSONEncoder):
163
+ """
164
+ Custom JSON encoder to handle numpy types.
165
+ """
166
+
167
+ def default(self, obj: Any) -> Any:
168
+ """
169
+ Convert numpy types to json.
170
+
171
+ Parameters
172
+ ----------
173
+ obj : Any
174
+ The object to convert.
175
+
176
+ Returns
177
+ -------
178
+ Any
179
+ The object converted to json.
180
+ """
181
+ if isinstance(obj, (int, str, float, list, dict)):
182
+ return obj
183
+ elif isinstance(obj, (np.integer, np.int64)):
184
+ return int(obj)
185
+ elif isinstance(obj, (np.floating, np.float64)):
186
+ return float(obj)
187
+ elif isinstance(obj, np.ndarray):
188
+ return obj.tolist()
189
+ else:
190
+ return str(obj)
191
+
192
+
193
+ def dict_to_json(struct: dict) -> str:
194
+ """
195
+ Convert a dict to json.
196
+
197
+ Parameters
198
+ ----------
199
+ struct : dict
200
+ The dict to convert.
201
+
202
+ Returns
203
+ -------
204
+ str
205
+ The json string.
206
+ """
207
+ return json.dumps(struct, cls=MyEncoder)
@@ -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,79 @@
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
+ def read_yaml(filepath: str | Path) -> dict | list[dict]:
41
+ """
42
+ Read a yaml file and return a dict or a list of dict.
43
+
44
+ Parameters
45
+ ----------
46
+ filepath : str | Path
47
+ The yaml file path to read.
48
+
49
+ Returns
50
+ -------
51
+ dict | list[dict]
52
+ The yaml file content.
53
+ """
54
+ try:
55
+ with open(filepath, "r", encoding="utf-8") as in_file:
56
+ data = yaml.load(in_file, Loader=yaml.SafeLoader)
57
+
58
+ # If yaml contains multiple documents
59
+ except yaml.composer.ComposerError:
60
+ with open(filepath, "r", encoding="utf-8") as in_file:
61
+ data = list(yaml.load_all(in_file, Loader=yaml.SafeLoader))
62
+ return data
63
+
64
+
65
+ def read_text(filepath: str | Path) -> str:
66
+ """
67
+ Read a file and return the text.
68
+
69
+ Parameters
70
+ ----------
71
+ filepath : str | Path
72
+ The file path to read.
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ The file content.
78
+ """
79
+ 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,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"