wandb 0.13.10__py3-none-any.whl → 0.14.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- wandb/__init__.py +2 -3
- wandb/apis/__init__.py +1 -3
- wandb/apis/importers/__init__.py +4 -0
- wandb/apis/importers/base.py +312 -0
- wandb/apis/importers/mlflow.py +113 -0
- wandb/apis/internal.py +29 -2
- wandb/apis/normalize.py +6 -5
- wandb/apis/public.py +163 -180
- wandb/apis/reports/_templates.py +6 -12
- wandb/apis/reports/report.py +1 -1
- wandb/apis/reports/runset.py +1 -3
- wandb/apis/reports/util.py +12 -10
- wandb/beta/workflows.py +57 -34
- wandb/catboost/__init__.py +1 -2
- wandb/cli/cli.py +215 -133
- wandb/data_types.py +63 -56
- wandb/docker/__init__.py +78 -16
- wandb/docker/auth.py +21 -22
- wandb/env.py +0 -1
- wandb/errors/__init__.py +8 -116
- wandb/errors/term.py +1 -1
- wandb/fastai/__init__.py +1 -2
- wandb/filesync/dir_watcher.py +8 -5
- wandb/filesync/step_prepare.py +76 -75
- wandb/filesync/step_upload.py +1 -2
- wandb/integration/catboost/__init__.py +1 -3
- wandb/integration/catboost/catboost.py +8 -14
- wandb/integration/fastai/__init__.py +7 -13
- wandb/integration/gym/__init__.py +35 -4
- wandb/integration/keras/__init__.py +3 -3
- wandb/integration/keras/callbacks/metrics_logger.py +9 -8
- wandb/integration/keras/callbacks/model_checkpoint.py +9 -9
- wandb/integration/keras/callbacks/tables_builder.py +31 -19
- wandb/integration/kfp/kfp_patch.py +20 -17
- wandb/integration/kfp/wandb_logging.py +1 -2
- wandb/integration/lightgbm/__init__.py +21 -19
- wandb/integration/prodigy/prodigy.py +6 -7
- wandb/integration/sacred/__init__.py +9 -12
- wandb/integration/sagemaker/__init__.py +1 -3
- wandb/integration/sagemaker/auth.py +0 -1
- wandb/integration/sagemaker/config.py +1 -1
- wandb/integration/sagemaker/resources.py +1 -1
- wandb/integration/sb3/sb3.py +8 -4
- wandb/integration/tensorboard/__init__.py +1 -3
- wandb/integration/tensorboard/log.py +8 -8
- wandb/integration/tensorboard/monkeypatch.py +11 -9
- wandb/integration/tensorflow/__init__.py +1 -3
- wandb/integration/xgboost/__init__.py +4 -6
- wandb/integration/yolov8/__init__.py +7 -0
- wandb/integration/yolov8/yolov8.py +250 -0
- wandb/jupyter.py +31 -35
- wandb/lightgbm/__init__.py +1 -2
- wandb/old/settings.py +2 -2
- wandb/plot/bar.py +1 -2
- wandb/plot/confusion_matrix.py +1 -3
- wandb/plot/histogram.py +1 -2
- wandb/plot/line.py +1 -2
- wandb/plot/line_series.py +4 -4
- wandb/plot/pr_curve.py +17 -20
- wandb/plot/roc_curve.py +1 -3
- wandb/plot/scatter.py +1 -2
- wandb/proto/v3/wandb_server_pb2.py +85 -39
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_server_pb2.py +51 -39
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/sdk/__init__.py +1 -3
- wandb/sdk/backend/backend.py +1 -1
- wandb/sdk/data_types/_dtypes.py +38 -30
- wandb/sdk/data_types/base_types/json_metadata.py +1 -3
- wandb/sdk/data_types/base_types/media.py +17 -17
- wandb/sdk/data_types/base_types/wb_value.py +33 -26
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +91 -125
- wandb/sdk/data_types/helper_types/classes.py +1 -1
- wandb/sdk/data_types/helper_types/image_mask.py +12 -12
- wandb/sdk/data_types/histogram.py +5 -4
- wandb/sdk/data_types/html.py +1 -2
- wandb/sdk/data_types/image.py +11 -11
- wandb/sdk/data_types/molecule.py +3 -6
- wandb/sdk/data_types/object_3d.py +1 -2
- wandb/sdk/data_types/plotly.py +1 -2
- wandb/sdk/data_types/saved_model.py +10 -8
- wandb/sdk/data_types/video.py +1 -1
- wandb/sdk/integration_utils/data_logging.py +5 -5
- wandb/sdk/interface/artifacts.py +288 -266
- wandb/sdk/interface/interface.py +2 -3
- wandb/sdk/interface/interface_grpc.py +1 -1
- wandb/sdk/interface/interface_queue.py +1 -1
- wandb/sdk/interface/interface_relay.py +1 -1
- wandb/sdk/interface/interface_shared.py +1 -2
- wandb/sdk/interface/interface_sock.py +1 -1
- wandb/sdk/interface/message_future.py +1 -1
- wandb/sdk/interface/message_future_poll.py +1 -1
- wandb/sdk/interface/router.py +1 -1
- wandb/sdk/interface/router_queue.py +1 -1
- wandb/sdk/interface/router_relay.py +1 -1
- wandb/sdk/interface/router_sock.py +1 -1
- wandb/sdk/interface/summary_record.py +1 -1
- wandb/sdk/internal/artifacts.py +1 -1
- wandb/sdk/internal/datastore.py +2 -3
- wandb/sdk/internal/file_pusher.py +5 -3
- wandb/sdk/internal/file_stream.py +22 -19
- wandb/sdk/internal/handler.py +5 -4
- wandb/sdk/internal/internal.py +1 -1
- wandb/sdk/internal/internal_api.py +115 -55
- wandb/sdk/internal/job_builder.py +1 -3
- wandb/sdk/internal/profiler.py +1 -1
- wandb/sdk/internal/progress.py +4 -6
- wandb/sdk/internal/sample.py +1 -3
- wandb/sdk/internal/sender.py +28 -16
- wandb/sdk/internal/settings_static.py +5 -5
- wandb/sdk/internal/system/assets/__init__.py +1 -0
- wandb/sdk/internal/system/assets/cpu.py +3 -9
- wandb/sdk/internal/system/assets/disk.py +2 -4
- wandb/sdk/internal/system/assets/gpu.py +6 -18
- wandb/sdk/internal/system/assets/gpu_apple.py +2 -4
- wandb/sdk/internal/system/assets/interfaces.py +50 -22
- wandb/sdk/internal/system/assets/ipu.py +1 -3
- wandb/sdk/internal/system/assets/memory.py +7 -13
- wandb/sdk/internal/system/assets/network.py +4 -8
- wandb/sdk/internal/system/assets/open_metrics.py +283 -0
- wandb/sdk/internal/system/assets/tpu.py +1 -4
- wandb/sdk/internal/system/assets/trainium.py +26 -14
- wandb/sdk/internal/system/system_info.py +2 -3
- wandb/sdk/internal/system/system_monitor.py +52 -20
- wandb/sdk/internal/tb_watcher.py +12 -13
- wandb/sdk/launch/_project_spec.py +54 -65
- wandb/sdk/launch/agent/agent.py +374 -90
- wandb/sdk/launch/builder/abstract.py +61 -7
- wandb/sdk/launch/builder/build.py +81 -110
- wandb/sdk/launch/builder/docker_builder.py +181 -0
- wandb/sdk/launch/builder/kaniko_builder.py +419 -0
- wandb/sdk/launch/builder/noop.py +31 -12
- wandb/sdk/launch/builder/templates/_wandb_bootstrap.py +70 -20
- wandb/sdk/launch/environment/abstract.py +28 -0
- wandb/sdk/launch/environment/aws_environment.py +276 -0
- wandb/sdk/launch/environment/gcp_environment.py +271 -0
- wandb/sdk/launch/environment/local_environment.py +65 -0
- wandb/sdk/launch/github_reference.py +3 -8
- wandb/sdk/launch/launch.py +38 -29
- wandb/sdk/launch/launch_add.py +6 -8
- wandb/sdk/launch/loader.py +230 -0
- wandb/sdk/launch/registry/abstract.py +54 -0
- wandb/sdk/launch/registry/elastic_container_registry.py +163 -0
- wandb/sdk/launch/registry/google_artifact_registry.py +203 -0
- wandb/sdk/launch/registry/local_registry.py +62 -0
- wandb/sdk/launch/runner/abstract.py +1 -16
- wandb/sdk/launch/runner/{kubernetes.py → kubernetes_runner.py} +83 -95
- wandb/sdk/launch/runner/local_container.py +46 -22
- wandb/sdk/launch/runner/local_process.py +1 -4
- wandb/sdk/launch/runner/{aws.py → sagemaker_runner.py} +53 -212
- wandb/sdk/launch/runner/{gcp_vertex.py → vertex_runner.py} +38 -55
- wandb/sdk/launch/sweeps/__init__.py +3 -2
- wandb/sdk/launch/sweeps/scheduler.py +132 -39
- wandb/sdk/launch/sweeps/scheduler_sweep.py +80 -89
- wandb/sdk/launch/utils.py +101 -30
- wandb/sdk/launch/wandb_reference.py +2 -7
- wandb/sdk/lib/_settings_toposort_generate.py +166 -0
- wandb/sdk/lib/_settings_toposort_generated.py +201 -0
- wandb/sdk/lib/apikey.py +2 -4
- wandb/sdk/lib/config_util.py +4 -1
- wandb/sdk/lib/console.py +1 -3
- wandb/sdk/lib/deprecate.py +3 -3
- wandb/sdk/lib/file_stream_utils.py +7 -5
- wandb/sdk/lib/filenames.py +1 -1
- wandb/sdk/lib/filesystem.py +61 -5
- wandb/sdk/lib/git.py +1 -3
- wandb/sdk/lib/import_hooks.py +4 -7
- wandb/sdk/lib/ipython.py +8 -5
- wandb/sdk/lib/lazyloader.py +1 -3
- wandb/sdk/lib/mailbox.py +14 -4
- wandb/sdk/lib/proto_util.py +10 -5
- wandb/sdk/lib/redirect.py +15 -22
- wandb/sdk/lib/reporting.py +1 -3
- wandb/sdk/lib/retry.py +4 -5
- wandb/sdk/lib/runid.py +1 -3
- wandb/sdk/lib/server.py +15 -9
- wandb/sdk/lib/sock_client.py +1 -1
- wandb/sdk/lib/sparkline.py +1 -1
- wandb/sdk/lib/wburls.py +1 -1
- wandb/sdk/service/port_file.py +1 -2
- wandb/sdk/service/service.py +36 -13
- wandb/sdk/service/service_base.py +12 -1
- wandb/sdk/verify/verify.py +5 -7
- wandb/sdk/wandb_artifacts.py +142 -177
- wandb/sdk/wandb_config.py +5 -8
- wandb/sdk/wandb_helper.py +1 -1
- wandb/sdk/wandb_init.py +24 -13
- wandb/sdk/wandb_login.py +9 -9
- wandb/sdk/wandb_manager.py +39 -4
- wandb/sdk/wandb_metric.py +2 -6
- wandb/sdk/wandb_require.py +4 -15
- wandb/sdk/wandb_require_helpers.py +1 -9
- wandb/sdk/wandb_run.py +95 -141
- wandb/sdk/wandb_save.py +1 -3
- wandb/sdk/wandb_settings.py +149 -54
- wandb/sdk/wandb_setup.py +66 -46
- wandb/sdk/wandb_summary.py +13 -10
- wandb/sdk/wandb_sweep.py +6 -7
- wandb/sdk/wandb_watch.py +1 -1
- wandb/sklearn/calculate/confusion_matrix.py +1 -1
- wandb/sklearn/calculate/learning_curve.py +1 -1
- wandb/sklearn/calculate/summary_metrics.py +1 -3
- wandb/sklearn/plot/__init__.py +1 -1
- wandb/sklearn/plot/classifier.py +27 -18
- wandb/sklearn/plot/clusterer.py +4 -5
- wandb/sklearn/plot/regressor.py +4 -4
- wandb/sklearn/plot/shared.py +2 -2
- wandb/sync/__init__.py +1 -3
- wandb/sync/sync.py +4 -5
- wandb/testing/relay.py +11 -10
- wandb/trigger.py +1 -1
- wandb/util.py +106 -81
- wandb/viz.py +4 -4
- wandb/wandb_agent.py +50 -50
- wandb/wandb_controller.py +2 -3
- wandb/wandb_run.py +1 -2
- wandb/wandb_torch.py +1 -1
- wandb/xgboost/__init__.py +1 -2
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/METADATA +6 -2
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/RECORD +224 -209
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/WHEEL +1 -1
- wandb/sdk/launch/builder/docker.py +0 -80
- wandb/sdk/launch/builder/kaniko.py +0 -393
- wandb/sdk/launch/builder/loader.py +0 -32
- wandb/sdk/launch/runner/loader.py +0 -50
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/LICENSE +0 -0
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/entry_points.txt +0 -0
- {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/top_level.txt +0 -0
wandb/sdk/interface/artifacts.py
CHANGED
@@ -9,18 +9,22 @@ from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
10
10
|
ContextManager,
|
11
11
|
Dict,
|
12
|
+
Generator,
|
12
13
|
List,
|
14
|
+
Mapping,
|
13
15
|
Optional,
|
14
16
|
Sequence,
|
15
17
|
Tuple,
|
18
|
+
Type,
|
16
19
|
Union,
|
17
20
|
)
|
18
21
|
|
19
22
|
import wandb
|
20
23
|
from wandb import env, util
|
21
24
|
from wandb.data_types import WBValue
|
22
|
-
from wandb.sdk.lib import
|
23
|
-
from wandb.sdk.lib.hashutil import B64MD5, ETag, b64_to_hex_id
|
25
|
+
from wandb.sdk.lib.filesystem import StrPath, mkdir_exists_ok
|
26
|
+
from wandb.sdk.lib.hashutil import B64MD5, ETag, HexMD5, b64_to_hex_id
|
27
|
+
from wandb.util import FilePathStr, LogicalFilePathStr, URIStr
|
24
28
|
|
25
29
|
if TYPE_CHECKING:
|
26
30
|
# need this import for type annotations, but want to avoid circular dependency
|
@@ -45,37 +49,34 @@ class ArtifactManifest:
|
|
45
49
|
entries: Dict[str, "ArtifactManifestEntry"]
|
46
50
|
|
47
51
|
@classmethod
|
48
|
-
|
49
|
-
def from_manifest_json(cls, artifact, manifest_json) -> "ArtifactManifest":
|
52
|
+
def from_manifest_json(cls, manifest_json: Dict) -> "ArtifactManifest":
|
50
53
|
if "version" not in manifest_json:
|
51
54
|
raise ValueError("Invalid manifest format. Must contain version field.")
|
52
55
|
version = manifest_json["version"]
|
53
56
|
for sub in cls.__subclasses__():
|
54
57
|
if sub.version() == version:
|
55
|
-
return sub.from_manifest_json(
|
58
|
+
return sub.from_manifest_json(manifest_json)
|
56
59
|
raise ValueError("Invalid manifest version.")
|
57
60
|
|
58
61
|
@classmethod
|
59
|
-
def version(cls):
|
60
|
-
|
62
|
+
def version(cls) -> int:
|
63
|
+
raise NotImplementedError
|
61
64
|
|
62
65
|
def __init__(
|
63
66
|
self,
|
64
|
-
artifact,
|
65
67
|
storage_policy: "wandb_artifacts.WandbStoragePolicy",
|
66
|
-
entries=None,
|
68
|
+
entries: Optional[Mapping[str, "ArtifactManifestEntry"]] = None,
|
67
69
|
) -> None:
|
68
|
-
self.artifact = artifact
|
69
70
|
self.storage_policy = storage_policy
|
70
|
-
self.entries = entries
|
71
|
+
self.entries = dict(entries) if entries else {}
|
71
72
|
|
72
|
-
def to_manifest_json(self):
|
73
|
+
def to_manifest_json(self) -> Dict:
|
73
74
|
raise NotImplementedError
|
74
75
|
|
75
|
-
def digest(self):
|
76
|
+
def digest(self) -> HexMD5:
|
76
77
|
raise NotImplementedError
|
77
78
|
|
78
|
-
def add_entry(self, entry):
|
79
|
+
def add_entry(self, entry: "ArtifactManifestEntry") -> None:
|
79
80
|
if (
|
80
81
|
entry.path in self.entries
|
81
82
|
and entry.digest != self.entries[entry.path].digest
|
@@ -86,7 +87,7 @@ class ArtifactManifest:
|
|
86
87
|
def get_entry_by_path(self, path: str) -> Optional["ArtifactManifestEntry"]:
|
87
88
|
return self.entries.get(path)
|
88
89
|
|
89
|
-
def get_entries_in_directory(self, directory):
|
90
|
+
def get_entries_in_directory(self, directory: str) -> List["ArtifactManifestEntry"]:
|
90
91
|
return [
|
91
92
|
self.entries[entry_key]
|
92
93
|
for entry_key in self.entries
|
@@ -98,33 +99,30 @@ class ArtifactManifest:
|
|
98
99
|
|
99
100
|
@dataclass
|
100
101
|
class ArtifactManifestEntry:
|
101
|
-
path:
|
102
|
-
digest: Union[B64MD5,
|
103
|
-
ref: Optional[Union[
|
102
|
+
path: LogicalFilePathStr
|
103
|
+
digest: Union[B64MD5, URIStr, FilePathStr, ETag]
|
104
|
+
ref: Optional[Union[FilePathStr, URIStr]] = None
|
104
105
|
birth_artifact_id: Optional[str] = None
|
105
106
|
size: Optional[int] = None
|
106
107
|
extra: Dict = field(default_factory=dict)
|
107
108
|
local_path: Optional[str] = None
|
108
109
|
|
109
|
-
def __post_init__(self):
|
110
|
+
def __post_init__(self) -> None:
|
110
111
|
self.path = util.to_forward_slash_path(self.path)
|
111
|
-
|
112
|
-
self.extra = {}
|
112
|
+
self.extra = self.extra or {}
|
113
113
|
if self.local_path and self.size is None:
|
114
114
|
raise ValueError("size required when local_path specified")
|
115
115
|
|
116
116
|
def parent_artifact(self) -> "Artifact":
|
117
|
-
"""
|
118
|
-
Get the artifact to which this artifact entry belongs.
|
117
|
+
"""Get the artifact to which this artifact entry belongs.
|
119
118
|
|
120
119
|
Returns:
|
121
120
|
(Artifact): The parent artifact
|
122
121
|
"""
|
123
122
|
raise NotImplementedError
|
124
123
|
|
125
|
-
def download(self, root: Optional[str] = None) ->
|
126
|
-
"""
|
127
|
-
Downloads this artifact entry to the specified root path.
|
124
|
+
def download(self, root: Optional[str] = None) -> FilePathStr:
|
125
|
+
"""Download this artifact entry to the specified root path.
|
128
126
|
|
129
127
|
Arguments:
|
130
128
|
root: (str, optional) The root path in which to download this
|
@@ -137,10 +135,11 @@ class ArtifactManifestEntry:
|
|
137
135
|
raise NotImplementedError
|
138
136
|
|
139
137
|
def ref_target(self) -> str:
|
140
|
-
"""
|
141
|
-
|
138
|
+
"""Get the reference URL that is targeted by this artifact entry.
|
139
|
+
|
142
140
|
Returns:
|
143
141
|
(str): The reference URL of this artifact entry.
|
142
|
+
|
144
143
|
Raises:
|
145
144
|
ValueError: If this artifact entry was not a reference.
|
146
145
|
"""
|
@@ -149,9 +148,9 @@ class ArtifactManifestEntry:
|
|
149
148
|
return self.ref
|
150
149
|
|
151
150
|
def ref_url(self) -> str:
|
152
|
-
"""
|
153
|
-
|
154
|
-
by another artifact.
|
151
|
+
"""Get a URL to this artifact entry.
|
152
|
+
|
153
|
+
These URLs can be referenced by another artifact.
|
155
154
|
|
156
155
|
Returns:
|
157
156
|
(str): A URL representing this artifact entry.
|
@@ -166,186 +165,204 @@ class ArtifactManifestEntry:
|
|
166
165
|
raise NotImplementedError
|
167
166
|
|
168
167
|
|
168
|
+
class ArtifactStatusError(AttributeError):
|
169
|
+
"""Raised when an artifact is in an invalid state for the requested operation."""
|
170
|
+
|
171
|
+
def __init__(
|
172
|
+
self,
|
173
|
+
artifact: Optional["Artifact"] = None,
|
174
|
+
attr: Optional[str] = None,
|
175
|
+
msg: str = "Artifact is in an invalid state for the requested operation.",
|
176
|
+
):
|
177
|
+
object_name = artifact.__class__.__name__ if artifact else "Artifact"
|
178
|
+
method_id = f"{object_name}.{attr}" if attr else object_name
|
179
|
+
super().__init__(msg.format(artifact=artifact, attr=attr, method_id=method_id))
|
180
|
+
# Follow the same pattern as AttributeError.
|
181
|
+
self.obj = artifact
|
182
|
+
self.name = attr
|
183
|
+
|
184
|
+
|
185
|
+
class ArtifactNotLoggedError(ArtifactStatusError):
|
186
|
+
"""Raised for Artifact methods or attributes only available after logging."""
|
187
|
+
|
188
|
+
def __init__(
|
189
|
+
self, artifact: Optional["Artifact"] = None, attr: Optional[str] = None
|
190
|
+
):
|
191
|
+
super().__init__(
|
192
|
+
artifact,
|
193
|
+
attr,
|
194
|
+
"'{method_id}' used prior to logging artifact or while in offline mode. "
|
195
|
+
"Call wait() before accessing logged artifact properties.",
|
196
|
+
)
|
197
|
+
|
198
|
+
|
199
|
+
class ArtifactFinalizedError(ArtifactStatusError):
|
200
|
+
"""Raised for Artifact methods or attributes that can't be changed after logging."""
|
201
|
+
|
202
|
+
def __init__(
|
203
|
+
self, artifact: Optional["Artifact"] = None, attr: Optional[str] = None
|
204
|
+
):
|
205
|
+
super().__init__(
|
206
|
+
artifact,
|
207
|
+
attr,
|
208
|
+
"'{method_id}' used on logged artifact. Can't add to finalized artifact.",
|
209
|
+
)
|
210
|
+
|
211
|
+
|
169
212
|
class Artifact:
|
170
213
|
@property
|
171
214
|
def id(self) -> Optional[str]:
|
172
|
-
"""
|
173
|
-
Returns:
|
174
|
-
(str): The artifact's ID
|
175
|
-
"""
|
215
|
+
"""The artifact's ID."""
|
176
216
|
raise NotImplementedError
|
177
217
|
|
178
218
|
@property
|
179
219
|
def version(self) -> str:
|
180
|
-
"""
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
be 'v0'.
|
220
|
+
"""The version of this artifact.
|
221
|
+
|
222
|
+
For example, if this is the first version of an artifact, its `version` will be
|
223
|
+
'v0'.
|
185
224
|
"""
|
186
225
|
raise NotImplementedError
|
187
226
|
|
188
227
|
@property
|
189
228
|
def source_version(self) -> Optional[str]:
|
190
|
-
"""
|
191
|
-
|
192
|
-
|
193
|
-
a string with the format "v{number}".
|
229
|
+
"""The artifact's version index under its parent artifact collection.
|
230
|
+
|
231
|
+
A string with the format "v{number}".
|
194
232
|
"""
|
195
233
|
raise NotImplementedError
|
196
234
|
|
197
235
|
@property
|
198
236
|
def name(self) -> str:
|
199
|
-
"""
|
200
|
-
Returns:
|
201
|
-
(str): The artifact's name
|
202
|
-
"""
|
237
|
+
"""The artifact's name."""
|
203
238
|
raise NotImplementedError
|
204
239
|
|
205
240
|
@property
|
206
241
|
def type(self) -> str:
|
207
|
-
"""
|
208
|
-
Returns:
|
209
|
-
(str): The artifact's type
|
210
|
-
"""
|
242
|
+
"""The artifact's type."""
|
211
243
|
raise NotImplementedError
|
212
244
|
|
213
245
|
@property
|
214
246
|
def entity(self) -> str:
|
215
|
-
"""
|
216
|
-
Returns:
|
217
|
-
(str): The name of the entity this artifact belongs to.
|
218
|
-
"""
|
247
|
+
"""The name of the entity this artifact belongs to."""
|
219
248
|
raise NotImplementedError
|
220
249
|
|
221
250
|
@property
|
222
251
|
def project(self) -> str:
|
223
|
-
"""
|
224
|
-
Returns:
|
225
|
-
(str): The name of the project this artifact belongs to.
|
226
|
-
"""
|
252
|
+
"""The name of the project this artifact belongs to."""
|
227
253
|
raise NotImplementedError
|
228
254
|
|
229
255
|
@property
|
230
256
|
def manifest(self) -> ArtifactManifest:
|
231
|
-
"""
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
manifest.
|
257
|
+
"""The artifact's manifest.
|
258
|
+
|
259
|
+
The manifest lists all of its contents, and can't be changed once the artifact
|
260
|
+
has been logged.
|
236
261
|
"""
|
237
262
|
raise NotImplementedError
|
238
263
|
|
239
264
|
@property
|
240
265
|
def digest(self) -> str:
|
241
|
-
"""
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
then `log_artifact` is a no-op.
|
266
|
+
"""The logical digest of the artifact.
|
267
|
+
|
268
|
+
The digest is the checksum of the artifact's contents. If an artifact has the
|
269
|
+
same digest as the current `latest` version, then `log_artifact` is a no-op.
|
246
270
|
"""
|
247
271
|
raise NotImplementedError
|
248
272
|
|
249
273
|
@property
|
250
274
|
def state(self) -> str:
|
251
|
-
"""
|
252
|
-
Returns:
|
253
|
-
(str): The state of the artifact, which can be one of "PENDING",
|
254
|
-
"COMMITTED", or "DELETED".
|
255
|
-
"""
|
275
|
+
"""The status of the artifact. One of: "PENDING", "COMMITTED", or "DELETED"."""
|
256
276
|
raise NotImplementedError
|
257
277
|
|
258
278
|
@property
|
259
279
|
def size(self) -> int:
|
260
|
-
"""
|
280
|
+
"""The total size of the artifact in bytes.
|
281
|
+
|
261
282
|
Returns:
|
262
|
-
(int): The size in bytes of the artifact. Includes any references
|
263
|
-
|
283
|
+
(int): The size in bytes of the artifact. Includes any references tracked by
|
284
|
+
this artifact.
|
264
285
|
"""
|
265
286
|
raise NotImplementedError
|
266
287
|
|
267
288
|
@property
|
268
289
|
def commit_hash(self) -> str:
|
269
|
-
"""
|
290
|
+
"""The hash returned when this artifact was committed.
|
291
|
+
|
270
292
|
Returns:
|
271
|
-
(str): The artifact's commit hash which is used in http URLs
|
293
|
+
(str): The artifact's commit hash which is used in http URLs.
|
272
294
|
"""
|
273
295
|
raise NotImplementedError
|
274
296
|
|
275
297
|
@property
|
276
298
|
def description(self) -> Optional[str]:
|
277
|
-
"""
|
299
|
+
"""The artifact description.
|
300
|
+
|
278
301
|
Returns:
|
279
|
-
(str): Free text that offers a description of the artifact.
|
280
|
-
description is markdown rendered in the UI, so this is a good place
|
281
|
-
to put links, etc.
|
302
|
+
(str): Free text that offers a user-set description of the artifact.
|
282
303
|
"""
|
283
304
|
raise NotImplementedError
|
284
305
|
|
285
306
|
@description.setter
|
286
307
|
def description(self, desc: Optional[str]) -> None:
|
287
|
-
"""
|
308
|
+
"""The artifact description.
|
309
|
+
|
310
|
+
The description is markdown rendered in the UI, so this is a good place to put
|
311
|
+
links, etc.
|
312
|
+
|
288
313
|
Arguments:
|
289
|
-
desc: Free text that offers a description of the artifact.
|
290
|
-
description is markdown rendered in the UI, so this is a good place
|
291
|
-
to put links, etc.
|
314
|
+
desc: Free text that offers a description of the artifact.
|
292
315
|
"""
|
293
316
|
raise NotImplementedError
|
294
317
|
|
295
318
|
@property
|
296
319
|
def metadata(self) -> dict:
|
297
|
-
"""
|
320
|
+
"""User-defined artifact metadata.
|
321
|
+
|
298
322
|
Returns:
|
299
|
-
(dict): Structured data associated with the artifact
|
300
|
-
for example class distribution of a dataset. This will eventually be queryable
|
301
|
-
and plottable in the UI. There is a hard limit of 100 total keys.
|
323
|
+
(dict): Structured data associated with the artifact.
|
302
324
|
"""
|
303
325
|
raise NotImplementedError
|
304
326
|
|
305
327
|
@metadata.setter
|
306
328
|
def metadata(self, metadata: dict) -> None:
|
307
|
-
"""
|
329
|
+
"""User-defined artifact metadata.
|
330
|
+
|
331
|
+
Metadata set this way will eventually be queryable and plottable in the UI; e.g.
|
332
|
+
the class distribution of a dataset.
|
333
|
+
|
334
|
+
Note: There is currently a limit of 100 total keys.
|
335
|
+
|
308
336
|
Arguments:
|
309
|
-
metadata: (dict) Structured data associated with the artifact
|
310
|
-
for example class distribution of a dataset. This will eventually be queryable
|
311
|
-
and plottable in the UI. There is a hard limit of 100 total keys.
|
337
|
+
metadata: (dict) Structured data associated with the artifact.
|
312
338
|
"""
|
313
339
|
raise NotImplementedError
|
314
340
|
|
315
341
|
@property
|
316
342
|
def aliases(self) -> List[str]:
|
317
|
-
"""
|
318
|
-
|
319
|
-
|
320
|
-
mutable and calling `save()` will persist all alias changes.
|
343
|
+
"""The aliases associated with this artifact.
|
344
|
+
|
345
|
+
The list is mutable and calling `save()` will persist all alias changes.
|
321
346
|
"""
|
322
347
|
raise NotImplementedError
|
323
348
|
|
324
349
|
@aliases.setter
|
325
350
|
def aliases(self, aliases: List[str]) -> None:
|
326
|
-
"""
|
327
|
-
Arguments:
|
328
|
-
aliases: (list) The list of aliases associated with this artifact.
|
329
|
-
"""
|
351
|
+
"""The aliases associated with this artifact."""
|
330
352
|
raise NotImplementedError
|
331
353
|
|
332
354
|
def used_by(self) -> List["wandb.apis.public.Run"]:
|
333
|
-
"""
|
334
|
-
Returns:
|
335
|
-
(list): A list of the runs that have used this artifact.
|
336
|
-
"""
|
355
|
+
"""Get a list of the runs that have used this artifact."""
|
337
356
|
raise NotImplementedError
|
338
357
|
|
339
358
|
def logged_by(self) -> "wandb.apis.public.Run":
|
340
|
-
"""
|
341
|
-
Returns:
|
342
|
-
(Run): The run that first logged this artifact.
|
343
|
-
"""
|
359
|
+
"""Get the run that first logged this artifact."""
|
344
360
|
raise NotImplementedError
|
345
361
|
|
346
|
-
def new_file(
|
347
|
-
""
|
348
|
-
|
362
|
+
def new_file(
|
363
|
+
self, name: str, mode: str = "w", encoding: Optional[str] = None
|
364
|
+
) -> ContextManager[IO]:
|
365
|
+
"""Open a new temporary file that will be automatically added to the artifact.
|
349
366
|
|
350
367
|
Arguments:
|
351
368
|
name: (str) The name of the new file being added to the artifact.
|
@@ -363,6 +380,9 @@ class Artifact:
|
|
363
380
|
Returns:
|
364
381
|
(file): A new file object that can be written to. Upon closing,
|
365
382
|
the file will be automatically added to the artifact.
|
383
|
+
|
384
|
+
Raises:
|
385
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
366
386
|
"""
|
367
387
|
raise NotImplementedError
|
368
388
|
|
@@ -371,30 +391,31 @@ class Artifact:
|
|
371
391
|
local_path: str,
|
372
392
|
name: Optional[str] = None,
|
373
393
|
is_tmp: Optional[bool] = False,
|
374
|
-
):
|
375
|
-
"""
|
376
|
-
Adds a local file to the artifact.
|
394
|
+
) -> ArtifactManifestEntry:
|
395
|
+
"""Add a local file to the artifact.
|
377
396
|
|
378
397
|
Arguments:
|
379
398
|
local_path: (str) The path to the file being added.
|
380
|
-
name: (str, optional) The path within the artifact to use for the file being
|
381
|
-
to the basename of the file.
|
382
|
-
is_tmp: (bool, optional) If true, then the file is renamed deterministically
|
383
|
-
(default: False)
|
399
|
+
name: (str, optional) The path within the artifact to use for the file being
|
400
|
+
added. Defaults to the basename of the file.
|
401
|
+
is_tmp: (bool, optional) If true, then the file is renamed deterministically
|
402
|
+
to avoid collisions. (default: False)
|
384
403
|
|
385
404
|
Examples:
|
386
|
-
|
405
|
+
Add a file without an explicit name:
|
387
406
|
```
|
388
|
-
|
407
|
+
# Add as `file.txt'
|
408
|
+
artifact.add_file('path/to/file.txt')
|
389
409
|
```
|
390
410
|
|
391
|
-
|
411
|
+
Add a file with an explicit name:
|
392
412
|
```
|
393
|
-
|
413
|
+
# Add as 'new/path/file.txt'
|
414
|
+
artifact.add_file('path/to/file.txt', name='new/path/file.txt')
|
394
415
|
```
|
395
416
|
|
396
417
|
Raises:
|
397
|
-
|
418
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
398
419
|
|
399
420
|
Returns:
|
400
421
|
ArtifactManifestEntry: the added manifest entry
|
@@ -403,27 +424,28 @@ class Artifact:
|
|
403
424
|
raise NotImplementedError
|
404
425
|
|
405
426
|
def add_dir(self, local_path: str, name: Optional[str] = None) -> None:
|
406
|
-
"""
|
407
|
-
Adds a local directory to the artifact.
|
427
|
+
"""Add a local directory to the artifact.
|
408
428
|
|
409
429
|
Arguments:
|
410
430
|
local_path: (str) The path to the directory being added.
|
411
|
-
name: (str, optional) The path within the artifact to use for the directory
|
412
|
-
|
431
|
+
name: (str, optional) The path within the artifact to use for the directory
|
432
|
+
being added. Defaults to the root of the artifact.
|
413
433
|
|
414
434
|
Examples:
|
415
|
-
|
435
|
+
Add a directory without an explicit name:
|
416
436
|
```
|
417
|
-
|
437
|
+
# All files in `my_dir/` are added at the root of the artifact.
|
438
|
+
artifact.add_dir('my_dir/')
|
418
439
|
```
|
419
440
|
|
420
|
-
|
441
|
+
Add a directory and name it explicitly:
|
421
442
|
```
|
422
|
-
|
443
|
+
# All files in `my_dir/` are added under `destination/`.
|
444
|
+
artifact.add_dir('my_dir/', name='destination')
|
423
445
|
```
|
424
446
|
|
425
447
|
Raises:
|
426
|
-
|
448
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
427
449
|
|
428
450
|
Returns:
|
429
451
|
None
|
@@ -436,67 +458,73 @@ class Artifact:
|
|
436
458
|
name: Optional[str] = None,
|
437
459
|
checksum: bool = True,
|
438
460
|
max_objects: Optional[int] = None,
|
439
|
-
):
|
440
|
-
"""
|
441
|
-
|
442
|
-
references are NOT uploaded to W&B. However,
|
443
|
-
be used regardless of whether the
|
461
|
+
) -> Sequence[ArtifactManifestEntry]:
|
462
|
+
"""Add a reference denoted by a URI to the artifact.
|
463
|
+
|
464
|
+
Unlike adding files or directories, references are NOT uploaded to W&B. However,
|
465
|
+
artifact methods such as `download()` can be used regardless of whether the
|
466
|
+
artifact contains references or uploaded files.
|
444
467
|
|
445
|
-
By default, W&B offers special
|
446
|
-
handling for the following schemes:
|
468
|
+
By default, W&B offers special handling for the following schemes:
|
447
469
|
|
448
|
-
- http(s): The size and digest of the file will be inferred by the
|
449
|
-
|
450
|
-
- s3: The checksum and size will be pulled from the object metadata. If bucket
|
451
|
-
|
452
|
-
- gs: The checksum and size will be pulled from the object metadata. If bucket
|
453
|
-
|
454
|
-
- file: The checksum and size will be pulled from the file system. This scheme
|
455
|
-
|
456
|
-
|
470
|
+
- http(s): The size and digest of the file will be inferred by the
|
471
|
+
`Content-Length` and the `ETag` response headers returned by the server.
|
472
|
+
- s3: The checksum and size will be pulled from the object metadata. If bucket
|
473
|
+
versioning is enabled, then the version ID is also tracked.
|
474
|
+
- gs: The checksum and size will be pulled from the object metadata. If bucket
|
475
|
+
versioning is enabled, then the version ID is also tracked.
|
476
|
+
- file: The checksum and size will be pulled from the file system. This scheme
|
477
|
+
is useful if you have an NFS share or other externally mounted volume
|
478
|
+
containing files you wish to track but not necessarily upload.
|
457
479
|
|
458
|
-
For any other scheme, the digest is just a hash of the URI and the size is left
|
480
|
+
For any other scheme, the digest is just a hash of the URI and the size is left
|
481
|
+
blank.
|
459
482
|
|
460
483
|
Arguments:
|
461
|
-
uri: (str) The URI path of the reference to add. Can be an object returned
|
484
|
+
uri: (str) The URI path of the reference to add. Can be an object returned
|
485
|
+
from
|
462
486
|
Artifact.get_path to store a reference to another artifact's entry.
|
463
|
-
name: (str) The path within the artifact to place the contents of this
|
464
|
-
checksum: (bool, optional) Whether or not to checksum the
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
487
|
+
name: (str) The path within the artifact to place the contents of this
|
488
|
+
reference checksum: (bool, optional) Whether or not to checksum the
|
489
|
+
resource(s) located at the
|
490
|
+
reference URI. Checksumming is strongly recommended as it enables
|
491
|
+
automatic integrity validation, however it can be disabled to speed up
|
492
|
+
artifact creation. (default: True)
|
493
|
+
max_objects: (int, optional) The maximum number of objects to consider when
|
494
|
+
adding a
|
495
|
+
reference that points to directory or bucket store prefix. For S3 and
|
496
|
+
GCS, this limit is 10,000 by default but is uncapped for other URI
|
497
|
+
schemes. (default: None)
|
470
498
|
|
471
499
|
Raises:
|
472
|
-
|
500
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
473
501
|
|
474
502
|
Returns:
|
475
503
|
List[ArtifactManifestEntry]: The added manifest entries.
|
476
504
|
|
477
505
|
Examples:
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
506
|
+
Add an HTTP link:
|
507
|
+
```python
|
508
|
+
# Adds `file.txt` to the root of the artifact as a reference.
|
509
|
+
artifact.add_reference("http://myserver.com/file.txt")
|
510
|
+
```
|
483
511
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
512
|
+
Add an S3 prefix without an explicit name:
|
513
|
+
```python
|
514
|
+
# All objects under `prefix/` will be added at the root of the artifact.
|
515
|
+
artifact.add_reference("s3://mybucket/prefix")
|
516
|
+
```
|
489
517
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
518
|
+
Add a GCS prefix with an explicit name:
|
519
|
+
```python
|
520
|
+
# All objects under `prefix/` will be added under `path/` at the artifact root.
|
521
|
+
artifact.add_reference("gs://mybucket/prefix", name="path")
|
522
|
+
```
|
495
523
|
"""
|
496
524
|
raise NotImplementedError
|
497
525
|
|
498
|
-
def add(self, obj: WBValue, name: str):
|
499
|
-
"""
|
526
|
+
def add(self, obj: WBValue, name: str) -> ArtifactManifestEntry:
|
527
|
+
"""Add wandb.WBValue `obj` to the artifact.
|
500
528
|
|
501
529
|
```
|
502
530
|
obj = artifact.get(name)
|
@@ -511,6 +539,9 @@ class Artifact:
|
|
511
539
|
Returns:
|
512
540
|
ArtifactManifestEntry: the added manifest entry
|
513
541
|
|
542
|
+
Raises:
|
543
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
544
|
+
|
514
545
|
Examples:
|
515
546
|
Basic usage
|
516
547
|
```
|
@@ -521,7 +552,7 @@ class Artifact:
|
|
521
552
|
wandb.log_artifact(artifact)
|
522
553
|
```
|
523
554
|
|
524
|
-
|
555
|
+
Retrieve an object:
|
525
556
|
```
|
526
557
|
artifact = wandb.use_artifact('my_table:latest')
|
527
558
|
table = artifact.get("my_table")
|
@@ -530,17 +561,13 @@ class Artifact:
|
|
530
561
|
raise NotImplementedError
|
531
562
|
|
532
563
|
def get_path(self, name: str) -> ArtifactManifestEntry:
|
533
|
-
"""
|
534
|
-
Gets the path to the file located at the artifact relative `name`.
|
535
|
-
|
536
|
-
NOTE: This will raise an error unless the artifact has been fetched using
|
537
|
-
`use_artifact`, fetched using the API, or `wait()` has been called.
|
564
|
+
"""Get the path to the file located at the artifact relative `name`.
|
538
565
|
|
539
566
|
Arguments:
|
540
567
|
name: (str) The artifact relative name to get
|
541
568
|
|
542
569
|
Raises:
|
543
|
-
|
570
|
+
ArtifactNotLoggedError: if the artifact isn't logged or the run is offline
|
544
571
|
|
545
572
|
Examples:
|
546
573
|
Basic usage
|
@@ -563,17 +590,13 @@ class Artifact:
|
|
563
590
|
raise NotImplementedError
|
564
591
|
|
565
592
|
def get(self, name: str) -> WBValue:
|
566
|
-
"""
|
567
|
-
Gets the WBValue object located at the artifact relative `name`.
|
568
|
-
|
569
|
-
NOTE: This will raise an error unless the artifact has been fetched using
|
570
|
-
`use_artifact`, fetched using the API, or `wait()` has been called.
|
593
|
+
"""Get the WBValue object located at the artifact relative `name`.
|
571
594
|
|
572
595
|
Arguments:
|
573
596
|
name: (str) The artifact relative name to get
|
574
597
|
|
575
598
|
Raises:
|
576
|
-
|
599
|
+
ArtifactNotLoggedError: if the artifact isn't logged or the run is offline
|
577
600
|
|
578
601
|
Examples:
|
579
602
|
Basic usage
|
@@ -595,9 +618,8 @@ class Artifact:
|
|
595
618
|
|
596
619
|
def download(
|
597
620
|
self, root: Optional[str] = None, recursive: bool = False
|
598
|
-
) ->
|
599
|
-
"""
|
600
|
-
Downloads the contents of the artifact to the specified root directory.
|
621
|
+
) -> FilePathStr:
|
622
|
+
"""Download the contents of the artifact to the specified root directory.
|
601
623
|
|
602
624
|
NOTE: Any existing files at `root` are left untouched. Explicitly delete
|
603
625
|
root before calling `download` if you want the contents of `root` to exactly
|
@@ -614,8 +636,7 @@ class Artifact:
|
|
614
636
|
raise NotImplementedError
|
615
637
|
|
616
638
|
def checkout(self, root: Optional[str] = None) -> str:
|
617
|
-
"""
|
618
|
-
Replaces the specified root directory with the contents of the artifact.
|
639
|
+
"""Replace the specified root directory with the contents of the artifact.
|
619
640
|
|
620
641
|
WARNING: This will DELETE all files in `root` that are not included in the
|
621
642
|
artifact.
|
@@ -629,10 +650,7 @@ class Artifact:
|
|
629
650
|
raise NotImplementedError
|
630
651
|
|
631
652
|
def verify(self, root: Optional[str] = None) -> bool:
|
632
|
-
"""
|
633
|
-
Verify that the actual contents of an artifact at a specified directory
|
634
|
-
`root` match the expected contents of the artifact according to its
|
635
|
-
manifest.
|
653
|
+
"""Verify that the actual contents of an artifact match the manifest.
|
636
654
|
|
637
655
|
All files in the directory are checksummed and the checksums are then
|
638
656
|
cross-referenced against the artifact's manifest.
|
@@ -649,8 +667,7 @@ class Artifact:
|
|
649
667
|
raise NotImplementedError
|
650
668
|
|
651
669
|
def save(self) -> None:
|
652
|
-
"""
|
653
|
-
Persists any changes made to the artifact.
|
670
|
+
"""Persist any changes made to the artifact.
|
654
671
|
|
655
672
|
Returns:
|
656
673
|
None
|
@@ -658,8 +675,7 @@ class Artifact:
|
|
658
675
|
raise NotImplementedError
|
659
676
|
|
660
677
|
def link(self, target_path: str, aliases: Optional[List[str]] = None) -> None:
|
661
|
-
"""
|
662
|
-
Links this artifact to a portfolio (a promoted collection of artifacts), with aliases.
|
678
|
+
"""Link this artifact to a portfolio (a promoted collection of artifacts), with aliases.
|
663
679
|
|
664
680
|
Arguments:
|
665
681
|
target_path: (str) The path to the portfolio. It must take the form
|
@@ -673,8 +689,7 @@ class Artifact:
|
|
673
689
|
raise NotImplementedError
|
674
690
|
|
675
691
|
def delete(self) -> None:
|
676
|
-
"""
|
677
|
-
Deletes this artifact, cleaning up all files associated with it.
|
692
|
+
"""Delete this artifact, cleaning up all files associated with it.
|
678
693
|
|
679
694
|
NOTE: Deletion is permanent and CANNOT be undone.
|
680
695
|
|
@@ -684,8 +699,7 @@ class Artifact:
|
|
684
699
|
raise NotImplementedError
|
685
700
|
|
686
701
|
def wait(self) -> "Artifact":
|
687
|
-
"""
|
688
|
-
Waits for this artifact to finish logging, if needed.
|
702
|
+
"""Wait for this artifact to finish logging, if needed.
|
689
703
|
|
690
704
|
Returns:
|
691
705
|
Artifact
|
@@ -693,17 +707,13 @@ class Artifact:
|
|
693
707
|
raise NotImplementedError
|
694
708
|
|
695
709
|
def __getitem__(self, name: str) -> Optional[WBValue]:
|
696
|
-
"""
|
697
|
-
Gets the WBValue object located at the artifact relative `name`.
|
698
|
-
|
699
|
-
NOTE: This will raise an error unless the artifact has been fetched using
|
700
|
-
`use_artifact`, fetched using the API, or `wait()` has been called.
|
710
|
+
"""Get the WBValue object located at the artifact relative `name`.
|
701
711
|
|
702
712
|
Arguments:
|
703
713
|
name: (str) The artifact relative name to get
|
704
714
|
|
705
715
|
Raises:
|
706
|
-
|
716
|
+
ArtifactNotLoggedError: if the artifact isn't logged or the run is offline
|
707
717
|
|
708
718
|
Examples:
|
709
719
|
Basic usage
|
@@ -723,9 +733,8 @@ class Artifact:
|
|
723
733
|
"""
|
724
734
|
raise NotImplementedError
|
725
735
|
|
726
|
-
def __setitem__(self, name: str, item: WBValue):
|
727
|
-
"""
|
728
|
-
Adds `item` to the artifact at path `name`
|
736
|
+
def __setitem__(self, name: str, item: WBValue) -> "ArtifactManifestEntry":
|
737
|
+
"""Add `item` to the artifact at path `name`.
|
729
738
|
|
730
739
|
Arguments:
|
731
740
|
name: (str) The path within the artifact to add the object.
|
@@ -734,6 +743,9 @@ class Artifact:
|
|
734
743
|
Returns:
|
735
744
|
ArtifactManifestEntry: the added manifest entry
|
736
745
|
|
746
|
+
Raises:
|
747
|
+
ArtifactFinalizedError: if the artifact has already been finalized.
|
748
|
+
|
737
749
|
Examples:
|
738
750
|
Basic usage
|
739
751
|
```
|
@@ -760,22 +772,22 @@ class StorageLayout:
|
|
760
772
|
|
761
773
|
class StoragePolicy:
|
762
774
|
@classmethod
|
763
|
-
def lookup_by_name(cls, name):
|
775
|
+
def lookup_by_name(cls, name: str) -> Optional[Type["StoragePolicy"]]:
|
764
776
|
for sub in cls.__subclasses__():
|
765
777
|
if sub.name() == name:
|
766
778
|
return sub
|
767
779
|
return None
|
768
780
|
|
769
781
|
@classmethod
|
770
|
-
def name(cls):
|
771
|
-
|
782
|
+
def name(cls) -> str:
|
783
|
+
raise NotImplementedError
|
772
784
|
|
773
785
|
@classmethod
|
774
|
-
def from_config(cls, config):
|
775
|
-
|
786
|
+
def from_config(cls, config: Dict) -> "StoragePolicy":
|
787
|
+
raise NotImplementedError
|
776
788
|
|
777
|
-
def config(self):
|
778
|
-
|
789
|
+
def config(self) -> Dict:
|
790
|
+
raise NotImplementedError
|
779
791
|
|
780
792
|
def load_file(
|
781
793
|
self, artifact: Artifact, manifest_entry: ArtifactManifestEntry
|
@@ -793,8 +805,13 @@ class StoragePolicy:
|
|
793
805
|
raise NotImplementedError
|
794
806
|
|
795
807
|
def store_reference(
|
796
|
-
self,
|
797
|
-
|
808
|
+
self,
|
809
|
+
artifact: Artifact,
|
810
|
+
path: Union[URIStr, FilePathStr],
|
811
|
+
name: Optional[str] = None,
|
812
|
+
checksum: bool = True,
|
813
|
+
max_objects: Optional[int] = None,
|
814
|
+
) -> Sequence[ArtifactManifestEntry]:
|
798
815
|
raise NotImplementedError
|
799
816
|
|
800
817
|
def load_reference(
|
@@ -808,33 +825,36 @@ class StoragePolicy:
|
|
808
825
|
class StorageHandler:
|
809
826
|
@property
|
810
827
|
def scheme(self) -> str:
|
811
|
-
"""
|
828
|
+
"""The scheme this handler applies to.
|
829
|
+
|
812
830
|
:return: The scheme to which this handler applies.
|
813
831
|
:rtype: str
|
814
832
|
"""
|
815
|
-
|
833
|
+
raise NotImplementedError
|
816
834
|
|
817
835
|
def load_path(
|
818
836
|
self,
|
819
837
|
manifest_entry: ArtifactManifestEntry,
|
820
838
|
local: bool = False,
|
821
|
-
) -> Union[
|
822
|
-
"""
|
823
|
-
Loads the file or directory within the specified artifact given its
|
824
|
-
corresponding index entry.
|
839
|
+
) -> Union[URIStr, FilePathStr]:
|
840
|
+
"""Load a file or directory given the corresponding index entry.
|
825
841
|
|
826
842
|
:param manifest_entry: The index entry to load
|
827
843
|
:type manifest_entry: ArtifactManifestEntry
|
828
844
|
:return: A path to the file represented by `index_entry`
|
829
845
|
:rtype: str
|
830
846
|
"""
|
831
|
-
|
847
|
+
raise NotImplementedError
|
832
848
|
|
833
849
|
def store_path(
|
834
|
-
self,
|
850
|
+
self,
|
851
|
+
artifact: Artifact,
|
852
|
+
path: Union[URIStr, FilePathStr],
|
853
|
+
name: Optional[str] = None,
|
854
|
+
checksum: bool = True,
|
855
|
+
max_objects: Optional[int] = None,
|
835
856
|
) -> Sequence[ArtifactManifestEntry]:
|
836
|
-
"""
|
837
|
-
Stores the file or directory at the given path within the specified artifact.
|
857
|
+
"""Store the file or directory at the given path to the specified artifact.
|
838
858
|
|
839
859
|
:param path: The path to store
|
840
860
|
:type path: str
|
@@ -843,42 +863,41 @@ class StorageHandler:
|
|
843
863
|
:return: A list of manifest entries to store within the artifact
|
844
864
|
:rtype: list(ArtifactManifestEntry)
|
845
865
|
"""
|
846
|
-
|
866
|
+
raise NotImplementedError
|
847
867
|
|
848
868
|
|
849
869
|
class ArtifactsCache:
|
850
|
-
|
851
870
|
_TMP_PREFIX = "tmp"
|
852
871
|
|
853
|
-
def __init__(self, cache_dir):
|
872
|
+
def __init__(self, cache_dir: StrPath) -> None:
|
854
873
|
self._cache_dir = cache_dir
|
855
|
-
|
874
|
+
mkdir_exists_ok(self._cache_dir)
|
856
875
|
self._md5_obj_dir = os.path.join(self._cache_dir, "obj", "md5")
|
857
876
|
self._etag_obj_dir = os.path.join(self._cache_dir, "obj", "etag")
|
858
|
-
self._artifacts_by_id = {}
|
877
|
+
self._artifacts_by_id: Dict[str, Artifact] = {}
|
859
878
|
self._random = random.Random()
|
860
879
|
self._random.seed()
|
861
|
-
self._artifacts_by_client_id = {}
|
880
|
+
self._artifacts_by_client_id: Dict[str, "wandb_artifacts.Artifact"] = {}
|
862
881
|
|
863
882
|
def check_md5_obj_path(
|
864
883
|
self, b64_md5: B64MD5, size: int
|
865
|
-
) -> Tuple[
|
884
|
+
) -> Tuple[FilePathStr, bool, "Opener"]:
|
866
885
|
hex_md5 = b64_to_hex_id(b64_md5)
|
867
886
|
path = os.path.join(self._cache_dir, "obj", "md5", hex_md5[:2], hex_md5[2:])
|
868
887
|
opener = self._cache_opener(path)
|
869
888
|
if os.path.isfile(path) and os.path.getsize(path) == size:
|
870
|
-
return path, True, opener
|
871
|
-
|
872
|
-
return path, False, opener
|
889
|
+
return FilePathStr(path), True, opener
|
890
|
+
mkdir_exists_ok(os.path.dirname(path))
|
891
|
+
return FilePathStr(path), False, opener
|
873
892
|
|
874
893
|
# TODO(spencerpearson): this method at least needs its signature changed.
|
875
894
|
# An ETag is not (necessarily) a checksum.
|
876
895
|
def check_etag_obj_path(
|
877
896
|
self,
|
878
|
-
url:
|
897
|
+
url: URIStr,
|
879
898
|
etag: ETag,
|
880
899
|
size: int,
|
881
|
-
) -> Tuple[
|
900
|
+
) -> Tuple[FilePathStr, bool, "Opener"]:
|
882
901
|
hexhash = hashlib.sha256(
|
883
902
|
hashlib.sha256(url.encode("utf-8")).digest()
|
884
903
|
+ hashlib.sha256(etag.encode("utf-8")).digest()
|
@@ -886,30 +905,34 @@ class ArtifactsCache:
|
|
886
905
|
path = os.path.join(self._cache_dir, "obj", "etag", hexhash[:2], hexhash[2:])
|
887
906
|
opener = self._cache_opener(path)
|
888
907
|
if os.path.isfile(path) and os.path.getsize(path) == size:
|
889
|
-
return path, True, opener
|
890
|
-
|
891
|
-
return path, False, opener
|
908
|
+
return FilePathStr(path), True, opener
|
909
|
+
mkdir_exists_ok(os.path.dirname(path))
|
910
|
+
return FilePathStr(path), False, opener
|
892
911
|
|
893
|
-
def get_artifact(self, artifact_id):
|
912
|
+
def get_artifact(self, artifact_id: str) -> Optional["Artifact"]:
|
894
913
|
return self._artifacts_by_id.get(artifact_id)
|
895
914
|
|
896
|
-
def store_artifact(self, artifact):
|
915
|
+
def store_artifact(self, artifact: "Artifact") -> None:
|
916
|
+
if not artifact.id:
|
917
|
+
raise ArtifactNotLoggedError(artifact, "store_artifact")
|
897
918
|
self._artifacts_by_id[artifact.id] = artifact
|
898
919
|
|
899
|
-
def get_client_artifact(
|
920
|
+
def get_client_artifact(
|
921
|
+
self, client_id: str
|
922
|
+
) -> Optional["wandb_artifacts.Artifact"]:
|
900
923
|
return self._artifacts_by_client_id.get(client_id)
|
901
924
|
|
902
|
-
def store_client_artifact(self, artifact):
|
925
|
+
def store_client_artifact(self, artifact: "wandb_artifacts.Artifact") -> None:
|
903
926
|
self._artifacts_by_client_id[artifact._client_id] = artifact
|
904
927
|
|
905
928
|
def cleanup(self, target_size: int) -> int:
|
906
|
-
bytes_reclaimed
|
907
|
-
paths
|
908
|
-
total_size
|
929
|
+
bytes_reclaimed = 0
|
930
|
+
paths = {}
|
931
|
+
total_size = 0
|
909
932
|
for root, _, files in os.walk(self._cache_dir):
|
910
933
|
for file in files:
|
911
934
|
try:
|
912
|
-
path = os.path.join(root, file)
|
935
|
+
path = str(os.path.join(root, file))
|
913
936
|
stat = os.stat(path)
|
914
937
|
|
915
938
|
if file.startswith(ArtifactsCache._TMP_PREFIX):
|
@@ -935,10 +958,9 @@ class ArtifactsCache:
|
|
935
958
|
bytes_reclaimed += stat.st_size
|
936
959
|
return bytes_reclaimed
|
937
960
|
|
938
|
-
def _cache_opener(self, path):
|
961
|
+
def _cache_opener(self, path: StrPath) -> "Opener":
|
939
962
|
@contextlib.contextmanager
|
940
|
-
def helper(mode="w"):
|
941
|
-
|
963
|
+
def helper(mode: str = "w") -> Generator[IO, None, None]:
|
942
964
|
if "a" in mode:
|
943
965
|
raise ValueError("Appending to cache files is not supported")
|
944
966
|
|
@@ -989,10 +1011,10 @@ def get_artifacts_cache() -> ArtifactsCache:
|
|
989
1011
|
return _artifacts_cache
|
990
1012
|
|
991
1013
|
|
992
|
-
def get_staging_dir() ->
|
1014
|
+
def get_staging_dir() -> FilePathStr:
|
993
1015
|
path = os.path.join(env.get_data_dir(), "artifacts", "staging")
|
994
|
-
|
995
|
-
return os.path.abspath(os.path.expanduser(path))
|
1016
|
+
mkdir_exists_ok(path)
|
1017
|
+
return FilePathStr(os.path.abspath(os.path.expanduser(path)))
|
996
1018
|
|
997
1019
|
|
998
1020
|
def get_new_staging_file() -> IO:
|