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.

Files changed (232) hide show
  1. digitalhub/__init__.py +63 -93
  2. digitalhub/client/__init__.py +0 -0
  3. digitalhub/client/_base/__init__.py +0 -0
  4. digitalhub/client/_base/client.py +56 -0
  5. digitalhub/client/api.py +63 -0
  6. digitalhub/client/builder.py +50 -0
  7. digitalhub/client/dhcore/__init__.py +0 -0
  8. digitalhub/client/dhcore/client.py +669 -0
  9. digitalhub/client/dhcore/env.py +21 -0
  10. digitalhub/client/dhcore/models.py +46 -0
  11. digitalhub/client/dhcore/utils.py +111 -0
  12. digitalhub/client/local/__init__.py +0 -0
  13. digitalhub/client/local/client.py +533 -0
  14. digitalhub/context/__init__.py +0 -0
  15. digitalhub/context/api.py +93 -0
  16. digitalhub/context/builder.py +94 -0
  17. digitalhub/context/context.py +136 -0
  18. digitalhub/datastores/__init__.py +0 -0
  19. digitalhub/datastores/_base/__init__.py +0 -0
  20. digitalhub/datastores/_base/datastore.py +85 -0
  21. digitalhub/datastores/api.py +37 -0
  22. digitalhub/datastores/builder.py +110 -0
  23. digitalhub/datastores/local/__init__.py +0 -0
  24. digitalhub/datastores/local/datastore.py +50 -0
  25. digitalhub/datastores/remote/__init__.py +0 -0
  26. digitalhub/datastores/remote/datastore.py +31 -0
  27. digitalhub/datastores/s3/__init__.py +0 -0
  28. digitalhub/datastores/s3/datastore.py +46 -0
  29. digitalhub/datastores/sql/__init__.py +0 -0
  30. digitalhub/datastores/sql/datastore.py +68 -0
  31. digitalhub/entities/__init__.py +0 -0
  32. digitalhub/entities/_base/__init__.py +0 -0
  33. digitalhub/entities/_base/_base/__init__.py +0 -0
  34. digitalhub/entities/_base/_base/entity.py +82 -0
  35. digitalhub/entities/_base/api_utils.py +620 -0
  36. digitalhub/entities/_base/context/__init__.py +0 -0
  37. digitalhub/entities/_base/context/entity.py +118 -0
  38. digitalhub/entities/_base/crud.py +468 -0
  39. digitalhub/entities/_base/entity/__init__.py +0 -0
  40. digitalhub/entities/_base/entity/_constructors/__init__.py +0 -0
  41. digitalhub/entities/_base/entity/_constructors/metadata.py +44 -0
  42. digitalhub/entities/_base/entity/_constructors/name.py +31 -0
  43. digitalhub/entities/_base/entity/_constructors/spec.py +33 -0
  44. digitalhub/entities/_base/entity/_constructors/status.py +52 -0
  45. digitalhub/entities/_base/entity/_constructors/uuid.py +26 -0
  46. digitalhub/entities/_base/entity/builder.py +175 -0
  47. digitalhub/entities/_base/entity/entity.py +106 -0
  48. digitalhub/entities/_base/entity/metadata.py +59 -0
  49. digitalhub/entities/_base/entity/spec.py +58 -0
  50. digitalhub/entities/_base/entity/status.py +43 -0
  51. digitalhub/entities/_base/executable/__init__.py +0 -0
  52. digitalhub/entities/_base/executable/entity.py +405 -0
  53. digitalhub/entities/_base/material/__init__.py +0 -0
  54. digitalhub/entities/_base/material/entity.py +214 -0
  55. digitalhub/entities/_base/material/spec.py +22 -0
  56. digitalhub/entities/_base/material/status.py +49 -0
  57. digitalhub/entities/_base/runtime_entity/__init__.py +0 -0
  58. digitalhub/entities/_base/runtime_entity/builder.py +106 -0
  59. digitalhub/entities/_base/unversioned/__init__.py +0 -0
  60. digitalhub/entities/_base/unversioned/builder.py +66 -0
  61. digitalhub/entities/_base/unversioned/entity.py +49 -0
  62. digitalhub/entities/_base/versioned/__init__.py +0 -0
  63. digitalhub/entities/_base/versioned/builder.py +68 -0
  64. digitalhub/entities/_base/versioned/entity.py +53 -0
  65. digitalhub/entities/artifact/__init__.py +0 -0
  66. digitalhub/entities/artifact/_base/__init__.py +0 -0
  67. digitalhub/entities/artifact/_base/builder.py +86 -0
  68. digitalhub/entities/artifact/_base/entity.py +39 -0
  69. digitalhub/entities/artifact/_base/spec.py +15 -0
  70. digitalhub/entities/artifact/_base/status.py +9 -0
  71. digitalhub/entities/artifact/artifact/__init__.py +0 -0
  72. digitalhub/entities/artifact/artifact/builder.py +18 -0
  73. digitalhub/entities/artifact/artifact/entity.py +32 -0
  74. digitalhub/entities/artifact/artifact/spec.py +27 -0
  75. digitalhub/entities/artifact/artifact/status.py +15 -0
  76. digitalhub/entities/artifact/crud.py +332 -0
  77. digitalhub/entities/builders.py +63 -0
  78. digitalhub/entities/dataitem/__init__.py +0 -0
  79. digitalhub/entities/dataitem/_base/__init__.py +0 -0
  80. digitalhub/entities/dataitem/_base/builder.py +86 -0
  81. digitalhub/entities/dataitem/_base/entity.py +75 -0
  82. digitalhub/entities/dataitem/_base/spec.py +15 -0
  83. digitalhub/entities/dataitem/_base/status.py +20 -0
  84. digitalhub/entities/dataitem/crud.py +372 -0
  85. digitalhub/entities/dataitem/dataitem/__init__.py +0 -0
  86. digitalhub/entities/dataitem/dataitem/builder.py +18 -0
  87. digitalhub/entities/dataitem/dataitem/entity.py +32 -0
  88. digitalhub/entities/dataitem/dataitem/spec.py +15 -0
  89. digitalhub/entities/dataitem/dataitem/status.py +9 -0
  90. digitalhub/entities/dataitem/iceberg/__init__.py +0 -0
  91. digitalhub/entities/dataitem/iceberg/builder.py +18 -0
  92. digitalhub/entities/dataitem/iceberg/entity.py +32 -0
  93. digitalhub/entities/dataitem/iceberg/spec.py +15 -0
  94. digitalhub/entities/dataitem/iceberg/status.py +9 -0
  95. digitalhub/entities/dataitem/table/__init__.py +0 -0
  96. digitalhub/entities/dataitem/table/builder.py +18 -0
  97. digitalhub/entities/dataitem/table/entity.py +146 -0
  98. digitalhub/entities/dataitem/table/models.py +62 -0
  99. digitalhub/entities/dataitem/table/spec.py +25 -0
  100. digitalhub/entities/dataitem/table/status.py +9 -0
  101. digitalhub/entities/function/__init__.py +0 -0
  102. digitalhub/entities/function/_base/__init__.py +0 -0
  103. digitalhub/entities/function/_base/builder.py +79 -0
  104. digitalhub/entities/function/_base/entity.py +98 -0
  105. digitalhub/entities/function/_base/models.py +118 -0
  106. digitalhub/entities/function/_base/spec.py +15 -0
  107. digitalhub/entities/function/_base/status.py +9 -0
  108. digitalhub/entities/function/crud.py +279 -0
  109. digitalhub/entities/model/__init__.py +0 -0
  110. digitalhub/entities/model/_base/__init__.py +0 -0
  111. digitalhub/entities/model/_base/builder.py +86 -0
  112. digitalhub/entities/model/_base/entity.py +34 -0
  113. digitalhub/entities/model/_base/spec.py +49 -0
  114. digitalhub/entities/model/_base/status.py +9 -0
  115. digitalhub/entities/model/crud.py +331 -0
  116. digitalhub/entities/model/huggingface/__init__.py +0 -0
  117. digitalhub/entities/model/huggingface/builder.py +18 -0
  118. digitalhub/entities/model/huggingface/entity.py +32 -0
  119. digitalhub/entities/model/huggingface/spec.py +36 -0
  120. digitalhub/entities/model/huggingface/status.py +9 -0
  121. digitalhub/entities/model/mlflow/__init__.py +0 -0
  122. digitalhub/entities/model/mlflow/builder.py +18 -0
  123. digitalhub/entities/model/mlflow/entity.py +32 -0
  124. digitalhub/entities/model/mlflow/models.py +26 -0
  125. digitalhub/entities/model/mlflow/spec.py +44 -0
  126. digitalhub/entities/model/mlflow/status.py +9 -0
  127. digitalhub/entities/model/mlflow/utils.py +81 -0
  128. digitalhub/entities/model/model/__init__.py +0 -0
  129. digitalhub/entities/model/model/builder.py +18 -0
  130. digitalhub/entities/model/model/entity.py +32 -0
  131. digitalhub/entities/model/model/spec.py +15 -0
  132. digitalhub/entities/model/model/status.py +9 -0
  133. digitalhub/entities/model/sklearn/__init__.py +0 -0
  134. digitalhub/entities/model/sklearn/builder.py +18 -0
  135. digitalhub/entities/model/sklearn/entity.py +32 -0
  136. digitalhub/entities/model/sklearn/spec.py +15 -0
  137. digitalhub/entities/model/sklearn/status.py +9 -0
  138. digitalhub/entities/project/__init__.py +0 -0
  139. digitalhub/entities/project/_base/__init__.py +0 -0
  140. digitalhub/entities/project/_base/builder.py +128 -0
  141. digitalhub/entities/project/_base/entity.py +2078 -0
  142. digitalhub/entities/project/_base/spec.py +50 -0
  143. digitalhub/entities/project/_base/status.py +9 -0
  144. digitalhub/entities/project/crud.py +357 -0
  145. digitalhub/entities/run/__init__.py +0 -0
  146. digitalhub/entities/run/_base/__init__.py +0 -0
  147. digitalhub/entities/run/_base/builder.py +94 -0
  148. digitalhub/entities/run/_base/entity.py +307 -0
  149. digitalhub/entities/run/_base/spec.py +50 -0
  150. digitalhub/entities/run/_base/status.py +9 -0
  151. digitalhub/entities/run/crud.py +219 -0
  152. digitalhub/entities/secret/__init__.py +0 -0
  153. digitalhub/entities/secret/_base/__init__.py +0 -0
  154. digitalhub/entities/secret/_base/builder.py +81 -0
  155. digitalhub/entities/secret/_base/entity.py +74 -0
  156. digitalhub/entities/secret/_base/spec.py +35 -0
  157. digitalhub/entities/secret/_base/status.py +9 -0
  158. digitalhub/entities/secret/crud.py +290 -0
  159. digitalhub/entities/task/__init__.py +0 -0
  160. digitalhub/entities/task/_base/__init__.py +0 -0
  161. digitalhub/entities/task/_base/builder.py +91 -0
  162. digitalhub/entities/task/_base/entity.py +136 -0
  163. digitalhub/entities/task/_base/models.py +208 -0
  164. digitalhub/entities/task/_base/spec.py +53 -0
  165. digitalhub/entities/task/_base/status.py +9 -0
  166. digitalhub/entities/task/crud.py +228 -0
  167. digitalhub/entities/utils/__init__.py +0 -0
  168. digitalhub/entities/utils/api.py +346 -0
  169. digitalhub/entities/utils/entity_types.py +19 -0
  170. digitalhub/entities/utils/state.py +31 -0
  171. digitalhub/entities/utils/utils.py +202 -0
  172. digitalhub/entities/workflow/__init__.py +0 -0
  173. digitalhub/entities/workflow/_base/__init__.py +0 -0
  174. digitalhub/entities/workflow/_base/builder.py +79 -0
  175. digitalhub/entities/workflow/_base/entity.py +74 -0
  176. digitalhub/entities/workflow/_base/spec.py +15 -0
  177. digitalhub/entities/workflow/_base/status.py +9 -0
  178. digitalhub/entities/workflow/crud.py +278 -0
  179. digitalhub/factory/__init__.py +0 -0
  180. digitalhub/factory/api.py +277 -0
  181. digitalhub/factory/factory.py +268 -0
  182. digitalhub/factory/utils.py +90 -0
  183. digitalhub/readers/__init__.py +0 -0
  184. digitalhub/readers/_base/__init__.py +0 -0
  185. digitalhub/readers/_base/builder.py +26 -0
  186. digitalhub/readers/_base/reader.py +70 -0
  187. digitalhub/readers/api.py +80 -0
  188. digitalhub/readers/factory.py +133 -0
  189. digitalhub/readers/pandas/__init__.py +0 -0
  190. digitalhub/readers/pandas/builder.py +29 -0
  191. digitalhub/readers/pandas/reader.py +207 -0
  192. digitalhub/runtimes/__init__.py +0 -0
  193. digitalhub/runtimes/_base.py +102 -0
  194. digitalhub/runtimes/builder.py +32 -0
  195. digitalhub/stores/__init__.py +0 -0
  196. digitalhub/stores/_base/__init__.py +0 -0
  197. digitalhub/stores/_base/store.py +189 -0
  198. digitalhub/stores/api.py +54 -0
  199. digitalhub/stores/builder.py +211 -0
  200. digitalhub/stores/local/__init__.py +0 -0
  201. digitalhub/stores/local/store.py +230 -0
  202. digitalhub/stores/remote/__init__.py +0 -0
  203. digitalhub/stores/remote/store.py +143 -0
  204. digitalhub/stores/s3/__init__.py +0 -0
  205. digitalhub/stores/s3/store.py +563 -0
  206. digitalhub/stores/sql/__init__.py +0 -0
  207. digitalhub/stores/sql/store.py +328 -0
  208. digitalhub/utils/__init__.py +0 -0
  209. digitalhub/utils/data_utils.py +127 -0
  210. digitalhub/utils/exceptions.py +67 -0
  211. digitalhub/utils/file_utils.py +204 -0
  212. digitalhub/utils/generic_utils.py +183 -0
  213. digitalhub/utils/git_utils.py +148 -0
  214. digitalhub/utils/io_utils.py +116 -0
  215. digitalhub/utils/logger.py +17 -0
  216. digitalhub/utils/s3_utils.py +58 -0
  217. digitalhub/utils/uri_utils.py +56 -0
  218. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/METADATA +30 -13
  219. digitalhub-0.8.0.dist-info/RECORD +231 -0
  220. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/WHEEL +1 -1
  221. test/local/CRUD/test_artifacts.py +96 -0
  222. test/local/CRUD/test_dataitems.py +96 -0
  223. test/local/CRUD/test_models.py +95 -0
  224. test/test_crud_functions.py +1 -1
  225. test/test_crud_runs.py +1 -1
  226. test/test_crud_tasks.py +1 -1
  227. digitalhub-0.7.0b2.dist-info/RECORD +0 -14
  228. test/test_crud_artifacts.py +0 -96
  229. test/test_crud_dataitems.py +0 -96
  230. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/LICENSE.txt +0 -0
  231. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/top_level.txt +0 -0
  232. /test/{test_imports.py → local/imports/test_imports.py} +0 -0
@@ -0,0 +1,211 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import typing
5
+
6
+ from pydantic import ValidationError
7
+
8
+ from digitalhub.stores._base.store import StoreParameters
9
+ from digitalhub.stores.local.store import LocalStore, LocalStoreConfig
10
+ from digitalhub.stores.remote.store import RemoteStore, RemoteStoreConfig
11
+ from digitalhub.stores.s3.store import S3Store, S3StoreConfig
12
+ from digitalhub.stores.sql.store import SqlStore, SQLStoreConfig
13
+ from digitalhub.utils.exceptions import StoreError
14
+ from digitalhub.utils.uri_utils import map_uri_scheme
15
+
16
+ if typing.TYPE_CHECKING:
17
+ from digitalhub.stores._base.store import Store
18
+
19
+
20
+ REGISTRY_STORES = {
21
+ "local": LocalStore,
22
+ "s3": S3Store,
23
+ "remote": RemoteStore,
24
+ "sql": SqlStore,
25
+ }
26
+
27
+
28
+ class StoreBuilder:
29
+ """
30
+ Store builder class.
31
+ """
32
+
33
+ def __init__(self) -> None:
34
+ self._instances: dict[str, Store] = {}
35
+ self._default: Store | None = None
36
+ self._def_scheme = "s3"
37
+
38
+ def build(self, store_cfg: StoreParameters) -> None:
39
+ """
40
+ Build a store instance and register it.
41
+ It overrides any existing instance.
42
+
43
+ Parameters
44
+ ----------
45
+ store_cfg : StoreParameters
46
+ Store configuration.
47
+
48
+ Returns
49
+ -------
50
+ None
51
+ """
52
+ scheme = map_uri_scheme(store_cfg.type)
53
+ self._instances[scheme] = self.build_store(store_cfg)
54
+
55
+ def get(self, uri: str) -> Store:
56
+ """
57
+ Get a store instance by URI.
58
+
59
+ Parameters
60
+ ----------
61
+ uri : str
62
+ URI to parse.
63
+
64
+ Returns
65
+ -------
66
+ Store
67
+ The store instance.
68
+ """
69
+ scheme = map_uri_scheme(uri)
70
+ if scheme not in self._instances:
71
+ store_cfg = get_env_store_config(scheme)
72
+ self._instances[scheme] = self.build_store(store_cfg)
73
+ return self._instances[scheme]
74
+
75
+ def default(self) -> Store:
76
+ """
77
+ Get the default store instance.
78
+
79
+ Returns
80
+ -------
81
+ Store
82
+ The default store instance.
83
+
84
+ Raises
85
+ ------
86
+ StoreError
87
+ If no default store is set.
88
+ """
89
+ if self._default is None:
90
+ store_cfg = get_env_store_config(self._def_scheme)
91
+ self._default = self.build_store(store_cfg)
92
+ return self._default
93
+
94
+ def build_store(self, cfg: StoreParameters) -> Store:
95
+ """
96
+ Build a store instance.
97
+
98
+ Parameters
99
+ ----------
100
+ cfg : StoreParameters
101
+ Store configuration.
102
+
103
+ Returns
104
+ -------
105
+ Store
106
+ The store instance.
107
+
108
+ Raises
109
+ ------
110
+ NotImplementedError
111
+ If the store type is not implemented.
112
+ """
113
+ try:
114
+ obj = REGISTRY_STORES[cfg.type](cfg.name, cfg.type, cfg.config)
115
+ if cfg.is_default and self._default is not None:
116
+ raise StoreError("Only one default store!")
117
+ return obj
118
+ except KeyError as e:
119
+ raise NotImplementedError from e
120
+
121
+ @staticmethod
122
+ def _check_config(config: StoreParameters | dict) -> StoreParameters:
123
+ """
124
+ Check the store configuration validity.
125
+
126
+ Parameters
127
+ ----------
128
+ config : StoreParameters | dict
129
+ The store configuration.
130
+
131
+ Returns
132
+ -------
133
+ StoreParameters
134
+ The store configuration.
135
+
136
+ Raises
137
+ ------
138
+ TypeError
139
+ If the config parameter is not a StoreParameters instance or a well-formed dictionary.
140
+ """
141
+ if not isinstance(config, StoreParameters):
142
+ try:
143
+ return StoreParameters(**config)
144
+ except TypeError as e:
145
+ raise StoreError("Invalid store configuration type.") from e
146
+ except ValidationError as e:
147
+ raise StoreError("Malformed store configuration parameters.") from e
148
+ return config
149
+
150
+
151
+ def get_env_store_config(scheme: str) -> StoreParameters:
152
+ """
153
+ Get a store configuration from the environment.
154
+
155
+ Parameters
156
+ ----------
157
+ scheme : str
158
+ URI scheme.
159
+
160
+ Returns
161
+ -------
162
+ StoreParameters
163
+ The store configuration based on the scheme.
164
+
165
+ Raises
166
+ ------
167
+ ValueError
168
+ If the scheme is not supported.
169
+ """
170
+ if scheme == "s3":
171
+ return StoreParameters(
172
+ name="s3",
173
+ type="s3",
174
+ config=S3StoreConfig(
175
+ endpoint_url=os.getenv("S3_ENDPOINT_URL"), # type: ignore
176
+ aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), # type: ignore
177
+ aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), # type: ignore
178
+ bucket_name=os.getenv("S3_BUCKET_NAME"), # type: ignore
179
+ ),
180
+ )
181
+ if scheme == "sql":
182
+ return StoreParameters(
183
+ name="sql",
184
+ type="sql",
185
+ config=SQLStoreConfig(
186
+ host=os.getenv("POSTGRES_HOST"), # type: ignore
187
+ port=os.getenv("POSTGRES_PORT"), # type: ignore
188
+ user=os.getenv("POSTGRES_USER"), # type: ignore
189
+ password=os.getenv("POSTGRES_PASSWORD"), # type: ignore
190
+ database=os.getenv("POSTGRES_DATABASE"), # type: ignore
191
+ pg_schema=os.getenv("POSTGRES_SCHEMA"), # type: ignore
192
+ ),
193
+ )
194
+ if scheme == "remote":
195
+ return StoreParameters(
196
+ name="remote",
197
+ type="remote",
198
+ config=RemoteStoreConfig(),
199
+ )
200
+ if scheme == "local":
201
+ return StoreParameters(
202
+ name="local",
203
+ type="local",
204
+ config=LocalStoreConfig(
205
+ path="tempsdk",
206
+ ),
207
+ )
208
+ raise ValueError(f"Unsupported scheme {scheme}")
209
+
210
+
211
+ store_builder = StoreBuilder()
File without changes
@@ -0,0 +1,230 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+
6
+ from digitalhub.stores._base.store import Store, StoreConfig
7
+ from digitalhub.utils.exceptions import StoreError
8
+ from digitalhub.utils.file_utils import get_file_info_from_local
9
+
10
+
11
+ class LocalStoreConfig(StoreConfig):
12
+ """
13
+ Local store configuration class.
14
+ """
15
+
16
+ path: str
17
+ """Local path."""
18
+
19
+
20
+ class LocalStore(Store):
21
+ """
22
+ Local store class. It implements the Store interface and provides methods to fetch and persist
23
+ artifacts on local filesystem based storage.
24
+ """
25
+
26
+ def __init__(self, name: str, store_type: str, config: LocalStoreConfig) -> None:
27
+ super().__init__(name, store_type)
28
+ self.config = config
29
+
30
+ ##############################
31
+ # IO methods
32
+ ##############################
33
+
34
+ def download(
35
+ self,
36
+ root: str,
37
+ dst: Path,
38
+ src: list[str],
39
+ overwrite: bool = False,
40
+ ) -> str:
41
+ """
42
+ Download artifacts from storage.
43
+
44
+ Parameters
45
+ ----------
46
+ root : str
47
+ The root path of the artifact.
48
+ dst : str
49
+ The destination of the artifact on local filesystem.
50
+ src : list[str]
51
+ List of sources.
52
+ overwrite : bool
53
+ Specify if overwrite existing file(s).
54
+
55
+ Returns
56
+ -------
57
+ str
58
+ Destination path of the downloaded artifact.
59
+ """
60
+ raise StoreError("Local store does not support download.")
61
+
62
+ def upload(self, src: str | list[str], dst: str | None = None) -> list[tuple[str, str]]:
63
+ """
64
+ Upload an artifact to storage.
65
+
66
+ Raises
67
+ ------
68
+ StoreError
69
+ This method is not implemented.
70
+ """
71
+ raise StoreError("Local store does not support upload.")
72
+
73
+ def get_file_info(self, paths: list[str]) -> list[dict]:
74
+ """
75
+ Method to get file metadata.
76
+
77
+ Parameters
78
+ ----------
79
+ paths : list
80
+ List of source paths.
81
+
82
+ Returns
83
+ -------
84
+ list[dict]
85
+ Returns files metadata.
86
+ """
87
+ return [get_file_info_from_local(p) for p in paths]
88
+
89
+ ##############################
90
+ # Private I/O methods
91
+ ##############################
92
+
93
+ def _get_src_dst_files(self, src: Path, dst: Path) -> list[str]:
94
+ """
95
+ Copy files from source to destination.
96
+
97
+ Parameters
98
+ ----------
99
+ src : Path
100
+ The source path.
101
+ dst : Path
102
+ The destination path.
103
+
104
+ Returns
105
+ -------
106
+ list[str]
107
+ Returns the list of destination and source paths of the
108
+ copied files.
109
+ """
110
+ return [self._get_src_dst_file(i, dst) for i in src.rglob("*") if i.is_file()]
111
+
112
+ def _get_src_dst_file(self, src: Path, dst: Path) -> str:
113
+ """
114
+ Copy file from source to destination.
115
+
116
+ Parameters
117
+ ----------
118
+ src : Path
119
+ The source path.
120
+ dst : Path
121
+ The destination path.
122
+
123
+ Returns
124
+ -------
125
+ str
126
+ """
127
+ dst_pth = self._copy_file(src, dst, True)
128
+ return str(dst_pth), str(src)
129
+
130
+ def _copy_dir(self, src: Path, dst: Path, overwrite: bool) -> list[str]:
131
+ """
132
+ Download file from source to destination.
133
+
134
+ Parameters
135
+ ----------
136
+ src : Path
137
+ The source path.
138
+ dst : Path
139
+ The destination path.
140
+
141
+ Returns
142
+ -------
143
+ list[str]
144
+ """
145
+ dst = self._rebuild_path(dst, src)
146
+ shutil.copytree(src, dst, dirs_exist_ok=overwrite)
147
+ return [str(i) for i in dst.rglob("*") if i.is_file()]
148
+
149
+ def _copy_file(self, src: Path, dst: Path, overwrite: bool) -> str:
150
+ """
151
+ Copy file from source to destination.
152
+
153
+ Parameters
154
+ ----------
155
+ src : Path
156
+ The source path.
157
+ dst : Path
158
+ The destination path.
159
+
160
+ Returns
161
+ -------
162
+ str
163
+ """
164
+ dst = self._rebuild_path(dst, src)
165
+ self._check_overwrite(dst, overwrite)
166
+ return str(shutil.copy2(src, dst))
167
+
168
+ def _rebuild_path(self, dst: Path, src: Path) -> Path:
169
+ """
170
+ Rebuild path.
171
+
172
+ Parameters
173
+ ----------
174
+ dst : Path
175
+ The destination path.
176
+ src : Path
177
+ The source path.
178
+
179
+ Returns
180
+ -------
181
+ Path
182
+ The rebuilt path.
183
+ """
184
+ if dst.is_dir():
185
+ if src.is_absolute():
186
+ raise StoreError("Source must be a relative path if the destination is a directory.")
187
+ dst = dst / src
188
+ self._build_path(dst)
189
+ return dst
190
+
191
+ ##############################
192
+ # Static methods
193
+ ##############################
194
+
195
+ @staticmethod
196
+ def is_partition_or_dir(path: str) -> bool:
197
+ """
198
+ Check if path is a directory or a partition.
199
+
200
+ Parameters
201
+ ----------
202
+ path : str
203
+ The path to check.
204
+
205
+ Returns
206
+ -------
207
+ bool
208
+ """
209
+ return Path(path).is_dir()
210
+
211
+ @staticmethod
212
+ def build_object_path(root: str, paths: str | list[str]) -> list[str]:
213
+ """
214
+ Method to build object path.
215
+
216
+ Parameters
217
+ ----------
218
+ root : str
219
+ The root of the object path.
220
+ paths : str | list[str]
221
+ The path to build.
222
+
223
+ Returns
224
+ -------
225
+ list[str]
226
+ Returns the path of the object.
227
+ """
228
+ if isinstance(paths, str):
229
+ paths = [paths]
230
+ return [str(Path(root) / path) for path in paths]
File without changes
@@ -0,0 +1,143 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import requests
6
+
7
+ from digitalhub.stores._base.store import Store, StoreConfig
8
+ from digitalhub.utils.exceptions import StoreError
9
+
10
+
11
+ class RemoteStoreConfig(StoreConfig):
12
+ """
13
+ Remote store configuration class.
14
+ """
15
+
16
+
17
+ class RemoteStore(Store):
18
+ """
19
+ HTTP store class. It implements the Store interface and provides methods to fetch
20
+ artifacts from remote HTTP based storage.
21
+ """
22
+
23
+ def __init__(self, name: str, store_type: str, config: RemoteStoreConfig) -> None:
24
+ super().__init__(name, store_type)
25
+ self.config = config
26
+
27
+ ##############################
28
+ # IO methods
29
+ ##############################
30
+
31
+ def download(
32
+ self,
33
+ root: str,
34
+ dst: Path,
35
+ src: list[str],
36
+ overwrite: bool = False,
37
+ ) -> str:
38
+ """
39
+ Download artifacts from storage.
40
+
41
+ Parameters
42
+ ----------
43
+ root : str
44
+ The root path of the artifact.
45
+ dst : str
46
+ The destination of the artifact on local filesystem.
47
+ src : list[str]
48
+ List of sources.
49
+ overwrite : bool
50
+ Specify if overwrite existing file(s).
51
+
52
+ Returns
53
+ -------
54
+ str
55
+ Destination path of the downloaded artifact.
56
+ """
57
+ # Handle destination
58
+ if dst is None:
59
+ dst = self._build_temp()
60
+ else:
61
+ self._check_local_dst(str(dst))
62
+
63
+ if dst.suffix == "":
64
+ dst = dst / "data.file"
65
+
66
+ self._check_overwrite(dst, overwrite)
67
+ self._build_path(dst)
68
+
69
+ return self._download_file(root, dst, overwrite)
70
+
71
+ def upload(self, src: str | list[str], dst: str | None = None) -> list[tuple[str, str]]:
72
+ """
73
+ Upload an artifact to storage.
74
+
75
+ Raises
76
+ ------
77
+ StoreError
78
+ This method is not implemented.
79
+ """
80
+ raise StoreError("Remote HTTP store does not support upload.")
81
+
82
+ def get_file_info(self, paths: list[str]) -> list[dict]:
83
+ """
84
+ Get file information from HTTP(s) storage.
85
+
86
+ Raises
87
+ ------
88
+ NotImplementedError
89
+ This method is not implemented.
90
+ """
91
+ raise NotImplementedError("Remote store does not support get_file_info.")
92
+
93
+ ##############################
94
+ # Private helper methods
95
+ ##############################
96
+
97
+ @staticmethod
98
+ def _check_head(src) -> None:
99
+ """
100
+ Check if the source exists.
101
+
102
+ Parameters
103
+ ----------
104
+ src : str
105
+ The source location.
106
+
107
+ Returns
108
+ -------
109
+ None
110
+
111
+ Raises
112
+ ------
113
+ HTTPError
114
+ If an error occurs while checking the source.
115
+ """
116
+ r = requests.head(src, timeout=60)
117
+ r.raise_for_status()
118
+
119
+ def _download_file(self, url: str, dst: Path, overwrite: bool) -> str:
120
+ """
121
+ Method to download a file from a given url.
122
+
123
+ Parameters
124
+ ----------
125
+ url : str
126
+ The url of the file to download.
127
+ dst : Path
128
+ The destination of the file.
129
+ overwrite : bool
130
+ Whether to overwrite existing files.
131
+
132
+ Returns
133
+ -------
134
+ str
135
+ The path of the downloaded file.
136
+ """
137
+ self._check_head(url)
138
+ with requests.get(url, stream=True, timeout=60) as r:
139
+ r.raise_for_status()
140
+ with open(dst, "wb") as f:
141
+ for chunk in r.iter_content(chunk_size=8192):
142
+ f.write(chunk)
143
+ return str(dst)
File without changes