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,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
|