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.
Files changed (228) hide show
  1. wandb/__init__.py +2 -3
  2. wandb/apis/__init__.py +1 -3
  3. wandb/apis/importers/__init__.py +4 -0
  4. wandb/apis/importers/base.py +312 -0
  5. wandb/apis/importers/mlflow.py +113 -0
  6. wandb/apis/internal.py +29 -2
  7. wandb/apis/normalize.py +6 -5
  8. wandb/apis/public.py +163 -180
  9. wandb/apis/reports/_templates.py +6 -12
  10. wandb/apis/reports/report.py +1 -1
  11. wandb/apis/reports/runset.py +1 -3
  12. wandb/apis/reports/util.py +12 -10
  13. wandb/beta/workflows.py +57 -34
  14. wandb/catboost/__init__.py +1 -2
  15. wandb/cli/cli.py +215 -133
  16. wandb/data_types.py +63 -56
  17. wandb/docker/__init__.py +78 -16
  18. wandb/docker/auth.py +21 -22
  19. wandb/env.py +0 -1
  20. wandb/errors/__init__.py +8 -116
  21. wandb/errors/term.py +1 -1
  22. wandb/fastai/__init__.py +1 -2
  23. wandb/filesync/dir_watcher.py +8 -5
  24. wandb/filesync/step_prepare.py +76 -75
  25. wandb/filesync/step_upload.py +1 -2
  26. wandb/integration/catboost/__init__.py +1 -3
  27. wandb/integration/catboost/catboost.py +8 -14
  28. wandb/integration/fastai/__init__.py +7 -13
  29. wandb/integration/gym/__init__.py +35 -4
  30. wandb/integration/keras/__init__.py +3 -3
  31. wandb/integration/keras/callbacks/metrics_logger.py +9 -8
  32. wandb/integration/keras/callbacks/model_checkpoint.py +9 -9
  33. wandb/integration/keras/callbacks/tables_builder.py +31 -19
  34. wandb/integration/kfp/kfp_patch.py +20 -17
  35. wandb/integration/kfp/wandb_logging.py +1 -2
  36. wandb/integration/lightgbm/__init__.py +21 -19
  37. wandb/integration/prodigy/prodigy.py +6 -7
  38. wandb/integration/sacred/__init__.py +9 -12
  39. wandb/integration/sagemaker/__init__.py +1 -3
  40. wandb/integration/sagemaker/auth.py +0 -1
  41. wandb/integration/sagemaker/config.py +1 -1
  42. wandb/integration/sagemaker/resources.py +1 -1
  43. wandb/integration/sb3/sb3.py +8 -4
  44. wandb/integration/tensorboard/__init__.py +1 -3
  45. wandb/integration/tensorboard/log.py +8 -8
  46. wandb/integration/tensorboard/monkeypatch.py +11 -9
  47. wandb/integration/tensorflow/__init__.py +1 -3
  48. wandb/integration/xgboost/__init__.py +4 -6
  49. wandb/integration/yolov8/__init__.py +7 -0
  50. wandb/integration/yolov8/yolov8.py +250 -0
  51. wandb/jupyter.py +31 -35
  52. wandb/lightgbm/__init__.py +1 -2
  53. wandb/old/settings.py +2 -2
  54. wandb/plot/bar.py +1 -2
  55. wandb/plot/confusion_matrix.py +1 -3
  56. wandb/plot/histogram.py +1 -2
  57. wandb/plot/line.py +1 -2
  58. wandb/plot/line_series.py +4 -4
  59. wandb/plot/pr_curve.py +17 -20
  60. wandb/plot/roc_curve.py +1 -3
  61. wandb/plot/scatter.py +1 -2
  62. wandb/proto/v3/wandb_server_pb2.py +85 -39
  63. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  64. wandb/proto/v4/wandb_server_pb2.py +51 -39
  65. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  66. wandb/sdk/__init__.py +1 -3
  67. wandb/sdk/backend/backend.py +1 -1
  68. wandb/sdk/data_types/_dtypes.py +38 -30
  69. wandb/sdk/data_types/base_types/json_metadata.py +1 -3
  70. wandb/sdk/data_types/base_types/media.py +17 -17
  71. wandb/sdk/data_types/base_types/wb_value.py +33 -26
  72. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +91 -125
  73. wandb/sdk/data_types/helper_types/classes.py +1 -1
  74. wandb/sdk/data_types/helper_types/image_mask.py +12 -12
  75. wandb/sdk/data_types/histogram.py +5 -4
  76. wandb/sdk/data_types/html.py +1 -2
  77. wandb/sdk/data_types/image.py +11 -11
  78. wandb/sdk/data_types/molecule.py +3 -6
  79. wandb/sdk/data_types/object_3d.py +1 -2
  80. wandb/sdk/data_types/plotly.py +1 -2
  81. wandb/sdk/data_types/saved_model.py +10 -8
  82. wandb/sdk/data_types/video.py +1 -1
  83. wandb/sdk/integration_utils/data_logging.py +5 -5
  84. wandb/sdk/interface/artifacts.py +288 -266
  85. wandb/sdk/interface/interface.py +2 -3
  86. wandb/sdk/interface/interface_grpc.py +1 -1
  87. wandb/sdk/interface/interface_queue.py +1 -1
  88. wandb/sdk/interface/interface_relay.py +1 -1
  89. wandb/sdk/interface/interface_shared.py +1 -2
  90. wandb/sdk/interface/interface_sock.py +1 -1
  91. wandb/sdk/interface/message_future.py +1 -1
  92. wandb/sdk/interface/message_future_poll.py +1 -1
  93. wandb/sdk/interface/router.py +1 -1
  94. wandb/sdk/interface/router_queue.py +1 -1
  95. wandb/sdk/interface/router_relay.py +1 -1
  96. wandb/sdk/interface/router_sock.py +1 -1
  97. wandb/sdk/interface/summary_record.py +1 -1
  98. wandb/sdk/internal/artifacts.py +1 -1
  99. wandb/sdk/internal/datastore.py +2 -3
  100. wandb/sdk/internal/file_pusher.py +5 -3
  101. wandb/sdk/internal/file_stream.py +22 -19
  102. wandb/sdk/internal/handler.py +5 -4
  103. wandb/sdk/internal/internal.py +1 -1
  104. wandb/sdk/internal/internal_api.py +115 -55
  105. wandb/sdk/internal/job_builder.py +1 -3
  106. wandb/sdk/internal/profiler.py +1 -1
  107. wandb/sdk/internal/progress.py +4 -6
  108. wandb/sdk/internal/sample.py +1 -3
  109. wandb/sdk/internal/sender.py +28 -16
  110. wandb/sdk/internal/settings_static.py +5 -5
  111. wandb/sdk/internal/system/assets/__init__.py +1 -0
  112. wandb/sdk/internal/system/assets/cpu.py +3 -9
  113. wandb/sdk/internal/system/assets/disk.py +2 -4
  114. wandb/sdk/internal/system/assets/gpu.py +6 -18
  115. wandb/sdk/internal/system/assets/gpu_apple.py +2 -4
  116. wandb/sdk/internal/system/assets/interfaces.py +50 -22
  117. wandb/sdk/internal/system/assets/ipu.py +1 -3
  118. wandb/sdk/internal/system/assets/memory.py +7 -13
  119. wandb/sdk/internal/system/assets/network.py +4 -8
  120. wandb/sdk/internal/system/assets/open_metrics.py +283 -0
  121. wandb/sdk/internal/system/assets/tpu.py +1 -4
  122. wandb/sdk/internal/system/assets/trainium.py +26 -14
  123. wandb/sdk/internal/system/system_info.py +2 -3
  124. wandb/sdk/internal/system/system_monitor.py +52 -20
  125. wandb/sdk/internal/tb_watcher.py +12 -13
  126. wandb/sdk/launch/_project_spec.py +54 -65
  127. wandb/sdk/launch/agent/agent.py +374 -90
  128. wandb/sdk/launch/builder/abstract.py +61 -7
  129. wandb/sdk/launch/builder/build.py +81 -110
  130. wandb/sdk/launch/builder/docker_builder.py +181 -0
  131. wandb/sdk/launch/builder/kaniko_builder.py +419 -0
  132. wandb/sdk/launch/builder/noop.py +31 -12
  133. wandb/sdk/launch/builder/templates/_wandb_bootstrap.py +70 -20
  134. wandb/sdk/launch/environment/abstract.py +28 -0
  135. wandb/sdk/launch/environment/aws_environment.py +276 -0
  136. wandb/sdk/launch/environment/gcp_environment.py +271 -0
  137. wandb/sdk/launch/environment/local_environment.py +65 -0
  138. wandb/sdk/launch/github_reference.py +3 -8
  139. wandb/sdk/launch/launch.py +38 -29
  140. wandb/sdk/launch/launch_add.py +6 -8
  141. wandb/sdk/launch/loader.py +230 -0
  142. wandb/sdk/launch/registry/abstract.py +54 -0
  143. wandb/sdk/launch/registry/elastic_container_registry.py +163 -0
  144. wandb/sdk/launch/registry/google_artifact_registry.py +203 -0
  145. wandb/sdk/launch/registry/local_registry.py +62 -0
  146. wandb/sdk/launch/runner/abstract.py +1 -16
  147. wandb/sdk/launch/runner/{kubernetes.py → kubernetes_runner.py} +83 -95
  148. wandb/sdk/launch/runner/local_container.py +46 -22
  149. wandb/sdk/launch/runner/local_process.py +1 -4
  150. wandb/sdk/launch/runner/{aws.py → sagemaker_runner.py} +53 -212
  151. wandb/sdk/launch/runner/{gcp_vertex.py → vertex_runner.py} +38 -55
  152. wandb/sdk/launch/sweeps/__init__.py +3 -2
  153. wandb/sdk/launch/sweeps/scheduler.py +132 -39
  154. wandb/sdk/launch/sweeps/scheduler_sweep.py +80 -89
  155. wandb/sdk/launch/utils.py +101 -30
  156. wandb/sdk/launch/wandb_reference.py +2 -7
  157. wandb/sdk/lib/_settings_toposort_generate.py +166 -0
  158. wandb/sdk/lib/_settings_toposort_generated.py +201 -0
  159. wandb/sdk/lib/apikey.py +2 -4
  160. wandb/sdk/lib/config_util.py +4 -1
  161. wandb/sdk/lib/console.py +1 -3
  162. wandb/sdk/lib/deprecate.py +3 -3
  163. wandb/sdk/lib/file_stream_utils.py +7 -5
  164. wandb/sdk/lib/filenames.py +1 -1
  165. wandb/sdk/lib/filesystem.py +61 -5
  166. wandb/sdk/lib/git.py +1 -3
  167. wandb/sdk/lib/import_hooks.py +4 -7
  168. wandb/sdk/lib/ipython.py +8 -5
  169. wandb/sdk/lib/lazyloader.py +1 -3
  170. wandb/sdk/lib/mailbox.py +14 -4
  171. wandb/sdk/lib/proto_util.py +10 -5
  172. wandb/sdk/lib/redirect.py +15 -22
  173. wandb/sdk/lib/reporting.py +1 -3
  174. wandb/sdk/lib/retry.py +4 -5
  175. wandb/sdk/lib/runid.py +1 -3
  176. wandb/sdk/lib/server.py +15 -9
  177. wandb/sdk/lib/sock_client.py +1 -1
  178. wandb/sdk/lib/sparkline.py +1 -1
  179. wandb/sdk/lib/wburls.py +1 -1
  180. wandb/sdk/service/port_file.py +1 -2
  181. wandb/sdk/service/service.py +36 -13
  182. wandb/sdk/service/service_base.py +12 -1
  183. wandb/sdk/verify/verify.py +5 -7
  184. wandb/sdk/wandb_artifacts.py +142 -177
  185. wandb/sdk/wandb_config.py +5 -8
  186. wandb/sdk/wandb_helper.py +1 -1
  187. wandb/sdk/wandb_init.py +24 -13
  188. wandb/sdk/wandb_login.py +9 -9
  189. wandb/sdk/wandb_manager.py +39 -4
  190. wandb/sdk/wandb_metric.py +2 -6
  191. wandb/sdk/wandb_require.py +4 -15
  192. wandb/sdk/wandb_require_helpers.py +1 -9
  193. wandb/sdk/wandb_run.py +95 -141
  194. wandb/sdk/wandb_save.py +1 -3
  195. wandb/sdk/wandb_settings.py +149 -54
  196. wandb/sdk/wandb_setup.py +66 -46
  197. wandb/sdk/wandb_summary.py +13 -10
  198. wandb/sdk/wandb_sweep.py +6 -7
  199. wandb/sdk/wandb_watch.py +1 -1
  200. wandb/sklearn/calculate/confusion_matrix.py +1 -1
  201. wandb/sklearn/calculate/learning_curve.py +1 -1
  202. wandb/sklearn/calculate/summary_metrics.py +1 -3
  203. wandb/sklearn/plot/__init__.py +1 -1
  204. wandb/sklearn/plot/classifier.py +27 -18
  205. wandb/sklearn/plot/clusterer.py +4 -5
  206. wandb/sklearn/plot/regressor.py +4 -4
  207. wandb/sklearn/plot/shared.py +2 -2
  208. wandb/sync/__init__.py +1 -3
  209. wandb/sync/sync.py +4 -5
  210. wandb/testing/relay.py +11 -10
  211. wandb/trigger.py +1 -1
  212. wandb/util.py +106 -81
  213. wandb/viz.py +4 -4
  214. wandb/wandb_agent.py +50 -50
  215. wandb/wandb_controller.py +2 -3
  216. wandb/wandb_run.py +1 -2
  217. wandb/wandb_torch.py +1 -1
  218. wandb/xgboost/__init__.py +1 -2
  219. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/METADATA +6 -2
  220. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/RECORD +224 -209
  221. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/WHEEL +1 -1
  222. wandb/sdk/launch/builder/docker.py +0 -80
  223. wandb/sdk/launch/builder/kaniko.py +0 -393
  224. wandb/sdk/launch/builder/loader.py +0 -32
  225. wandb/sdk/launch/runner/loader.py +0 -50
  226. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/LICENSE +0 -0
  227. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/entry_points.txt +0 -0
  228. {wandb-0.13.10.dist-info → wandb-0.14.0.dist-info}/top_level.txt +0 -0
@@ -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 filesystem
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
- # TODO: we don't need artifact here.
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(artifact, 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
- pass
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 or {}
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: util.LogicalFilePathStr
102
- digest: Union[B64MD5, util.URIStr, util.FilePathStr, ETag]
103
- ref: Optional[Union[util.FilePathStr, util.URIStr]] = None
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
- if self.extra is None:
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) -> util.FilePathStr:
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
- Gets the reference URL that this artifact entry targets.
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
- Gets a URL to this artifact entry such that it can be referenced
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
- Returns:
182
- (str): The version of this artifact. For example, if this
183
- is the first version of an artifact, its `version` will
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
- Returns:
192
- (str) The artifact's version index under its parent artifact collection. This will return
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
- Returns:
233
- (ArtifactManifest): The artifact's manifest, listing all of its contents.
234
- You cannot add more files to an artifact once you've retrieved its
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
- Returns:
243
- (str): The artifact's logical digest, a checksum of its contents. If
244
- an artifact has the same digest as the current `latest` version,
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
- tracked by this artifact.
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. The
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. The
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
- Returns:
319
- (list): A list of the aliases associated with this artifact. The list is
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(self, name: str, mode: str = "w", encoding: Optional[str] = None):
347
- """
348
- Open a new temporary file that will be automatically added to the artifact.
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 added. Defaults
381
- to the basename of the file.
382
- is_tmp: (bool, optional) If true, then the file is renamed deterministically to avoid collisions.
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
- Adding a file without an explicit name:
405
+ Add a file without an explicit name:
387
406
  ```
388
- artifact.add_file('path/to/file.txt') # Added as `file.txt'
407
+ # Add as `file.txt'
408
+ artifact.add_file('path/to/file.txt')
389
409
  ```
390
410
 
391
- Adding a file with an explicit name:
411
+ Add a file with an explicit name:
392
412
  ```
393
- artifact.add_file('path/to/file.txt', name='new/path/file.txt') # Added as 'new/path/file.txt'
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
- Exception: if problem
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 being added. Defaults
412
- to files being added under the root of the artifact.
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
- Adding a directory without an explicit name:
435
+ Add a directory without an explicit name:
416
436
  ```
417
- artifact.add_dir('my_dir/') # All files in `my_dir/` are added at the root of the artifact.
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
- Adding a directory without an explicit name:
441
+ Add a directory and name it explicitly:
421
442
  ```
422
- artifact.add_dir('my_dir/', name='destination') # All files in `my_dir/` are added under `destination/`.
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
- Exception: if problem.
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
- Adds a reference denoted by a URI to the artifact. Unlike adding files or directories,
442
- references are NOT uploaded to W&B. However, artifact methods such as `download()` can
443
- be used regardless of whether the artifact contains references or uploaded files.
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 `Content-Length` and
449
- the `ETag` response headers returned by the server.
450
- - s3: The checksum and size will be pulled from the object metadata. If bucket versioning
451
- is enabled, then the version ID is also tracked.
452
- - gs: The checksum and size will be pulled from the object metadata. If bucket versioning
453
- is enabled, then the version ID is also tracked.
454
- - file: The checksum and size will be pulled from the file system. This scheme is useful if
455
- you have an NFS share or other externally mounted volume containing files you wish to track
456
- but not necessarily upload.
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 blank.
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 from
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 reference
464
- checksum: (bool, optional) Whether or not to checksum the resource(s) located at the
465
- reference URI. Checksumming is strongly recommended as it enables automatic integrity
466
- validation, however it can be disabled to speed up artifact creation. (default: True)
467
- max_objects: (int, optional) The maximum number of objects to consider when adding a
468
- reference that points to directory or bucket store prefix. For S3 and GCS, this limit
469
- is 10,000 by default but is uncapped for other URI schemes. (default: None)
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
- Exception: If problem.
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
- Adding an HTTP link:
479
- ```
480
- # Adds `file.txt` to the root of the artifact as a reference
481
- artifact.add_reference('http://myserver.com/file.txt')
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
- Adding an S3 prefix without an explicit name:
485
- ```
486
- # All objects under `prefix/` will be added at the root of the artifact.
487
- artifact.add_reference('s3://mybucket/prefix')
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
- Adding a GCS prefix with an explicit name:
491
- ```
492
- # All objects under `prefix/` will be added under `path/` at the top of the artifact.
493
- artifact.add_reference('gs://mybucket/prefix', name='path')
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
- """Adds wandb.WBValue `obj` to the artifact.
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
- Retrieving an object:
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
- Exception: if problem
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
- Exception: if problem
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
- ) -> util.FilePathStr:
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
- Exception: if problem
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
- pass
782
+ def name(cls) -> str:
783
+ raise NotImplementedError
772
784
 
773
785
  @classmethod
774
- def from_config(cls, config):
775
- pass
786
+ def from_config(cls, config: Dict) -> "StoragePolicy":
787
+ raise NotImplementedError
776
788
 
777
- def config(self):
778
- pass
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, artifact, path, name=None, checksum=True, max_objects=None
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
- pass
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[util.URIStr, util.FilePathStr]:
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
- pass
847
+ raise NotImplementedError
832
848
 
833
849
  def store_path(
834
- self, artifact, path, name=None, checksum=True, max_objects=None
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
- pass
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
- filesystem.mkdir_exists_ok(self._cache_dir)
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[util.FilePathStr, bool, "Opener"]:
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
- filesystem.mkdir_exists_ok(os.path.dirname(path))
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: util.URIStr,
897
+ url: URIStr,
879
898
  etag: ETag,
880
899
  size: int,
881
- ) -> Tuple[util.FilePathStr, bool, "Opener"]:
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
- filesystem.mkdir_exists_ok(os.path.dirname(path))
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(self, client_id):
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: int = 0
907
- paths: Dict[os.PathLike, os.stat_result] = {}
908
- total_size: int = 0
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() -> util.FilePathStr:
1014
+ def get_staging_dir() -> FilePathStr:
993
1015
  path = os.path.join(env.get_data_dir(), "artifacts", "staging")
994
- filesystem.mkdir_exists_ok(path)
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: