wandb 0.19.9__py3-none-win32.whl → 0.19.11__py3-none-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wandb/__init__.py +1 -1
- wandb/__init__.pyi +6 -3
- wandb/_pydantic/__init__.py +14 -8
- wandb/_pydantic/base.py +51 -36
- wandb/_pydantic/utils.py +73 -0
- wandb/_pydantic/v1_compat.py +79 -57
- wandb/apis/public/__init__.py +2 -2
- wandb/apis/public/api.py +684 -4
- wandb/apis/public/artifacts.py +377 -677
- wandb/apis/public/automations.py +69 -0
- wandb/apis/public/integrations.py +180 -0
- wandb/apis/public/projects.py +29 -0
- wandb/apis/public/registries/__init__.py +0 -0
- wandb/apis/public/registries/_freezable_list.py +179 -0
- wandb/apis/public/{registries.py → registries/registries_search.py} +22 -129
- wandb/apis/public/registries/registry.py +357 -0
- wandb/apis/public/registries/utils.py +140 -0
- wandb/apis/public/runs.py +58 -56
- wandb/apis/public/utils.py +107 -1
- wandb/automations/__init__.py +73 -0
- wandb/automations/_filters/__init__.py +40 -0
- wandb/automations/_filters/expressions.py +181 -0
- wandb/automations/_filters/operators.py +258 -0
- wandb/automations/_filters/run_metrics.py +332 -0
- wandb/automations/_generated/__init__.py +177 -0
- wandb/automations/_generated/create_automation.py +17 -0
- wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
- wandb/automations/_generated/delete_automation.py +17 -0
- wandb/automations/_generated/enums.py +33 -0
- wandb/automations/_generated/fragments.py +358 -0
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
- wandb/automations/_generated/get_automations.py +24 -0
- wandb/automations/_generated/get_automations_by_entity.py +26 -0
- wandb/automations/_generated/input_types.py +104 -0
- wandb/automations/_generated/integrations_by_entity.py +22 -0
- wandb/automations/_generated/operations.py +647 -0
- wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
- wandb/automations/_generated/update_automation.py +17 -0
- wandb/automations/_utils.py +237 -0
- wandb/automations/_validators.py +165 -0
- wandb/automations/actions.py +220 -0
- wandb/automations/automations.py +87 -0
- wandb/automations/events.py +287 -0
- wandb/automations/integrations.py +45 -0
- wandb/automations/scopes.py +78 -0
- wandb/beta/workflows.py +9 -10
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +3 -3
- wandb/env.py +11 -0
- wandb/integration/keras/keras.py +2 -1
- wandb/integration/langchain/wandb_tracer.py +2 -1
- wandb/jupyter.py +137 -118
- wandb/old/settings.py +4 -1
- wandb/old/summary.py +0 -2
- wandb/proto/v3/wandb_internal_pb2.py +297 -292
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_internal_pb2.py +292 -292
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v5/wandb_internal_pb2.py +292 -292
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v6/wandb_base_pb2.py +41 -0
- wandb/proto/v6/wandb_internal_pb2.py +393 -0
- wandb/proto/v6/wandb_server_pb2.py +78 -0
- wandb/proto/v6/wandb_settings_pb2.py +58 -0
- wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
- wandb/proto/wandb_base_pb2.py +2 -0
- wandb/proto/wandb_deprecated.py +8 -0
- wandb/proto/wandb_internal_pb2.py +3 -1
- wandb/proto/wandb_server_pb2.py +2 -0
- wandb/proto/wandb_settings_pb2.py +2 -0
- wandb/proto/wandb_telemetry_pb2.py +2 -0
- wandb/sdk/artifacts/_generated/__init__.py +289 -0
- wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
- wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
- wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
- wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_generated/enums.py +17 -0
- wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
- wandb/sdk/artifacts/_generated/fragments.py +221 -0
- wandb/sdk/artifacts/_generated/input_types.py +28 -0
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
- wandb/sdk/artifacts/_generated/operations.py +611 -0
- wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
- wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
- wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
- wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_graphql_fragments.py +57 -79
- wandb/sdk/artifacts/_validators.py +120 -1
- wandb/sdk/artifacts/artifact.py +419 -215
- wandb/sdk/artifacts/artifact_file_cache.py +4 -6
- wandb/sdk/artifacts/artifact_manifest_entry.py +13 -3
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
- wandb/sdk/artifacts/storage_policy.py +3 -0
- wandb/sdk/data_types/base_types/media.py +2 -3
- wandb/sdk/data_types/base_types/wb_value.py +34 -11
- wandb/sdk/data_types/html.py +36 -9
- wandb/sdk/data_types/image.py +12 -12
- wandb/sdk/data_types/table.py +5 -0
- wandb/sdk/data_types/trace_tree.py +2 -0
- wandb/sdk/data_types/utils.py +1 -1
- wandb/sdk/data_types/video.py +59 -57
- wandb/sdk/interface/interface.py +4 -3
- wandb/sdk/internal/internal_api.py +21 -31
- wandb/sdk/internal/profiler.py +6 -5
- wandb/sdk/internal/run.py +13 -6
- wandb/sdk/internal/sender.py +5 -2
- wandb/sdk/launch/sweeps/utils.py +8 -0
- wandb/sdk/lib/apikey.py +25 -4
- wandb/sdk/lib/asyncio_compat.py +1 -1
- wandb/sdk/lib/deprecate.py +13 -22
- wandb/sdk/lib/disabled.py +2 -1
- wandb/sdk/lib/printer.py +37 -8
- wandb/sdk/lib/printer_asyncio.py +46 -0
- wandb/sdk/lib/redirect.py +10 -5
- wandb/sdk/projects/_generated/__init__.py +47 -0
- wandb/sdk/projects/_generated/delete_project.py +22 -0
- wandb/sdk/projects/_generated/enums.py +4 -0
- wandb/sdk/projects/_generated/fetch_registry.py +22 -0
- wandb/sdk/projects/_generated/fragments.py +41 -0
- wandb/sdk/projects/_generated/input_types.py +13 -0
- wandb/sdk/projects/_generated/operations.py +88 -0
- wandb/sdk/projects/_generated/rename_project.py +27 -0
- wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
- wandb/sdk/service/server_sock.py +19 -14
- wandb/sdk/service/service.py +18 -8
- wandb/sdk/service/streams.py +5 -0
- wandb/sdk/verify/verify.py +6 -3
- wandb/sdk/wandb_init.py +217 -70
- wandb/sdk/wandb_login.py +13 -4
- wandb/sdk/wandb_run.py +419 -295
- wandb/sdk/wandb_settings.py +27 -10
- wandb/sdk/wandb_setup.py +61 -0
- wandb/util.py +33 -29
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/METADATA +5 -5
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/RECORD +153 -83
- wandb/_globals.py +0 -19
- wandb/sdk/internal/_generated/base.py +0 -226
- wandb/sdk/internal/_generated/typing_compat.py +0 -14
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/licenses/LICENSE +0 -0
@@ -11,7 +11,7 @@ import subprocess
|
|
11
11
|
import sys
|
12
12
|
from pathlib import Path
|
13
13
|
from tempfile import NamedTemporaryFile
|
14
|
-
from typing import IO,
|
14
|
+
from typing import IO, ContextManager, Iterator, Protocol
|
15
15
|
|
16
16
|
import wandb
|
17
17
|
from wandb import env, util
|
@@ -19,12 +19,10 @@ from wandb.sdk.lib.filesystem import files_in
|
|
19
19
|
from wandb.sdk.lib.hashutil import B64MD5, ETag, b64_to_hex_id
|
20
20
|
from wandb.sdk.lib.paths import FilePathStr, StrPath, URIStr
|
21
21
|
|
22
|
-
if TYPE_CHECKING:
|
23
|
-
from typing import Protocol
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
class Opener(Protocol):
|
24
|
+
def __call__(self, mode: str = ...) -> ContextManager[IO]:
|
25
|
+
pass
|
28
26
|
|
29
27
|
|
30
28
|
def _get_sys_umask_threadsafe() -> int:
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import concurrent.futures
|
5
6
|
import json
|
6
7
|
import logging
|
7
8
|
import os
|
@@ -9,8 +10,9 @@ from pathlib import Path
|
|
9
10
|
from typing import TYPE_CHECKING
|
10
11
|
from urllib.parse import urlparse
|
11
12
|
|
13
|
+
from wandb.proto.wandb_deprecated import Deprecated
|
12
14
|
from wandb.sdk.lib import filesystem
|
13
|
-
from wandb.sdk.lib.deprecate import
|
15
|
+
from wandb.sdk.lib.deprecate import deprecate
|
14
16
|
from wandb.sdk.lib.hashutil import (
|
15
17
|
B64MD5,
|
16
18
|
ETag,
|
@@ -129,7 +131,11 @@ class ArtifactManifestEntry:
|
|
129
131
|
return self._parent_artifact
|
130
132
|
|
131
133
|
def download(
|
132
|
-
self,
|
134
|
+
self,
|
135
|
+
root: str | None = None,
|
136
|
+
skip_cache: bool | None = None,
|
137
|
+
executor: concurrent.futures.Executor | None = None,
|
138
|
+
multipart: bool | None = None,
|
133
139
|
) -> FilePathStr:
|
134
140
|
"""Download this artifact entry to the specified root path.
|
135
141
|
|
@@ -169,7 +175,11 @@ class ArtifactManifestEntry:
|
|
169
175
|
)
|
170
176
|
else:
|
171
177
|
cache_path = self._parent_artifact.manifest.storage_policy.load_file(
|
172
|
-
self._parent_artifact,
|
178
|
+
self._parent_artifact,
|
179
|
+
self,
|
180
|
+
dest_path=override_cache_path,
|
181
|
+
executor=executor,
|
182
|
+
multipart=multipart,
|
173
183
|
)
|
174
184
|
|
175
185
|
if skip_cache:
|
@@ -171,6 +171,7 @@ class AzureHandler(StorageHandler):
|
|
171
171
|
def _get_credential(
|
172
172
|
self, account_url: str
|
173
173
|
) -> azure.identity.DefaultAzureCredential | str:
|
174
|
+
# NOTE: Always returns default credential for reinit="create_new" runs.
|
174
175
|
if (
|
175
176
|
wandb.run
|
176
177
|
and wandb.run.settings.azure_account_url_to_access_key is not None
|
@@ -2,20 +2,28 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import concurrent.futures
|
6
|
+
import functools
|
5
7
|
import hashlib
|
8
|
+
import logging
|
6
9
|
import math
|
7
10
|
import os
|
11
|
+
import queue
|
8
12
|
import shutil
|
9
|
-
|
13
|
+
import threading
|
14
|
+
from collections import deque
|
15
|
+
from typing import IO, TYPE_CHECKING, Any, NamedTuple, Sequence
|
10
16
|
from urllib.parse import quote
|
11
17
|
|
12
18
|
import requests
|
13
19
|
import urllib3
|
14
20
|
|
21
|
+
from wandb import env
|
15
22
|
from wandb.errors.term import termwarn
|
16
23
|
from wandb.proto.wandb_internal_pb2 import ServerFeature
|
17
24
|
from wandb.sdk.artifacts.artifact_file_cache import (
|
18
25
|
ArtifactFileCache,
|
26
|
+
Opener,
|
19
27
|
get_artifact_file_cache,
|
20
28
|
)
|
21
29
|
from wandb.sdk.artifacts.staging import get_staging_dir
|
@@ -60,6 +68,27 @@ S3_MIN_MULTI_UPLOAD_SIZE = 2 * 1024**3
|
|
60
68
|
S3_MAX_MULTI_UPLOAD_SIZE = 5 * 1024**4
|
61
69
|
|
62
70
|
|
71
|
+
# Minimum size to switch to multipart download, same as upload, 2GB.
|
72
|
+
_MULTIPART_DOWNLOAD_SIZE = S3_MIN_MULTI_UPLOAD_SIZE
|
73
|
+
# Multipart download part size is same as multpart upload size, which is hard coded to 100MB.
|
74
|
+
# https://github.com/wandb/wandb/blob/7b2a13cb8efcd553317167b823c8e52d8c3f7c4e/core/pkg/artifacts/saver.go#L496
|
75
|
+
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range
|
76
|
+
_DOWNLOAD_PART_SIZE_BYTES = 100 * 1024 * 1024
|
77
|
+
# Chunk size for reading http response and writing to disk. 1MB.
|
78
|
+
_HTTP_RES_CHUNK_SIZE_BYTES = 1 * 1024 * 1024
|
79
|
+
# Signal end of _ChunkQueue, consumer (file writer) should stop after getting this item.
|
80
|
+
# NOTE: it should only be used for multithread executor, it does notwork for multiprocess executor.
|
81
|
+
# multipart download is using the executor from artifact.download() which is a multithread executor.
|
82
|
+
_CHUNK_QUEUE_SENTINEL = object()
|
83
|
+
|
84
|
+
logger = logging.getLogger(__name__)
|
85
|
+
|
86
|
+
|
87
|
+
class _ChunkContent(NamedTuple):
|
88
|
+
offset: int
|
89
|
+
data: bytes
|
90
|
+
|
91
|
+
|
63
92
|
class WandbStoragePolicy(StoragePolicy):
|
64
93
|
@classmethod
|
65
94
|
def name(cls) -> str:
|
@@ -120,7 +149,20 @@ class WandbStoragePolicy(StoragePolicy):
|
|
120
149
|
artifact: Artifact,
|
121
150
|
manifest_entry: ArtifactManifestEntry,
|
122
151
|
dest_path: str | None = None,
|
152
|
+
executor: concurrent.futures.Executor | None = None,
|
153
|
+
multipart: bool | None = None,
|
123
154
|
) -> FilePathStr:
|
155
|
+
"""Use cache or download the file using signed url.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
executor: Passed from caller, artifact has a thread pool for multi file download.
|
159
|
+
Reuse the thread pool for multi part download. The thread pool is closed when
|
160
|
+
artifact download is done.
|
161
|
+
multipart: If set to `None` (default), the artifact will be downloaded
|
162
|
+
in parallel using multipart download if individual file size is greater than
|
163
|
+
2GB. If set to `True` or `False`, the artifact will be downloaded in
|
164
|
+
parallel or serially regardless of the file size.
|
165
|
+
"""
|
124
166
|
if dest_path is not None:
|
125
167
|
self._cache._override_cache_path = dest_path
|
126
168
|
|
@@ -132,6 +174,20 @@ class WandbStoragePolicy(StoragePolicy):
|
|
132
174
|
return path
|
133
175
|
|
134
176
|
if manifest_entry._download_url is not None:
|
177
|
+
# Use multipart parallel download for large file
|
178
|
+
if (
|
179
|
+
executor is not None
|
180
|
+
and manifest_entry.size is not None
|
181
|
+
and self._should_multipart_download(manifest_entry.size, multipart)
|
182
|
+
):
|
183
|
+
self._multipart_file_download(
|
184
|
+
executor,
|
185
|
+
manifest_entry._download_url,
|
186
|
+
manifest_entry.size,
|
187
|
+
cache_open,
|
188
|
+
)
|
189
|
+
return path
|
190
|
+
# Serial download
|
135
191
|
response = self._session.get(manifest_entry._download_url, stream=True)
|
136
192
|
try:
|
137
193
|
response.raise_for_status()
|
@@ -165,6 +221,131 @@ class WandbStoragePolicy(StoragePolicy):
|
|
165
221
|
file.write(data)
|
166
222
|
return path
|
167
223
|
|
224
|
+
def _should_multipart_download(
|
225
|
+
self,
|
226
|
+
file_size: int,
|
227
|
+
multipart: bool | None,
|
228
|
+
) -> bool:
|
229
|
+
if multipart is not None:
|
230
|
+
return multipart
|
231
|
+
return file_size >= _MULTIPART_DOWNLOAD_SIZE
|
232
|
+
|
233
|
+
def _write_chunks_to_file(
|
234
|
+
self,
|
235
|
+
f: IO,
|
236
|
+
q: queue.Queue,
|
237
|
+
download_has_error: threading.Event,
|
238
|
+
):
|
239
|
+
while not download_has_error.is_set():
|
240
|
+
item = q.get()
|
241
|
+
if item is _CHUNK_QUEUE_SENTINEL:
|
242
|
+
# Normal shutdown, all the chunks are written
|
243
|
+
return
|
244
|
+
elif isinstance(item, _ChunkContent):
|
245
|
+
try:
|
246
|
+
# NOTE: Seek works without pre allocating the file on disk.
|
247
|
+
# It automatically creates a sparse file, e.g. ls -hl would show
|
248
|
+
# a bigger size compared to du -sh * because downloading different
|
249
|
+
# chunks is not a sequential write.
|
250
|
+
# See https://man7.org/linux/man-pages/man2/lseek.2.html
|
251
|
+
f.seek(item.offset)
|
252
|
+
f.write(item.data)
|
253
|
+
except Exception as e:
|
254
|
+
if env.is_debug():
|
255
|
+
logger.debug(f"Error writing chunk to file: {e}")
|
256
|
+
download_has_error.set()
|
257
|
+
raise e
|
258
|
+
else:
|
259
|
+
raise ValueError(f"Unknown queue item type: {type(item)}")
|
260
|
+
|
261
|
+
def _download_part(
|
262
|
+
self,
|
263
|
+
download_url: str,
|
264
|
+
headers: dict,
|
265
|
+
start: int,
|
266
|
+
q: queue.Queue,
|
267
|
+
download_has_error: threading.Event,
|
268
|
+
):
|
269
|
+
# Other threads has error, no need to start
|
270
|
+
if download_has_error.is_set():
|
271
|
+
return
|
272
|
+
response = self._session.get(
|
273
|
+
url=download_url,
|
274
|
+
headers=headers,
|
275
|
+
stream=True,
|
276
|
+
)
|
277
|
+
response.raise_for_status()
|
278
|
+
|
279
|
+
file_offset = start
|
280
|
+
for content in response.iter_content(chunk_size=_HTTP_RES_CHUNK_SIZE_BYTES):
|
281
|
+
if download_has_error.is_set():
|
282
|
+
return
|
283
|
+
q.put(_ChunkContent(offset=file_offset, data=content))
|
284
|
+
file_offset += len(content)
|
285
|
+
|
286
|
+
def _multipart_file_download(
|
287
|
+
self,
|
288
|
+
executor: concurrent.futures.Executor,
|
289
|
+
download_url: str,
|
290
|
+
file_size_bytes: int,
|
291
|
+
cache_open: Opener,
|
292
|
+
):
|
293
|
+
"""Download file as multiple parts in parallel.
|
294
|
+
|
295
|
+
Only one thread for writing to file. Each part run one http request in one thread.
|
296
|
+
HTTP response chunk of a file part is sent to the writer thread via a queue.
|
297
|
+
"""
|
298
|
+
q: queue.Queue[_ChunkContent | object] = queue.Queue(maxsize=500)
|
299
|
+
download_has_error = threading.Event()
|
300
|
+
|
301
|
+
# Put cache_open at top so we remove the tmp file when there is network error.
|
302
|
+
with cache_open("wb") as f:
|
303
|
+
# Start writer thread first.
|
304
|
+
write_handler = functools.partial(
|
305
|
+
self._write_chunks_to_file, f, q, download_has_error
|
306
|
+
)
|
307
|
+
write_future = executor.submit(write_handler)
|
308
|
+
|
309
|
+
# Start download threads for each part.
|
310
|
+
download_futures: deque[concurrent.futures.Future] = deque()
|
311
|
+
part_size = _DOWNLOAD_PART_SIZE_BYTES
|
312
|
+
num_parts = int(math.ceil(file_size_bytes / float(part_size)))
|
313
|
+
for i in range(num_parts):
|
314
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Range
|
315
|
+
# Start and end are both inclusive, empty end means use the actual end of the file.
|
316
|
+
start = i * part_size
|
317
|
+
bytes_range = f"bytes={start}-"
|
318
|
+
if i != (num_parts - 1):
|
319
|
+
# bytes=0-499
|
320
|
+
bytes_range += f"{start + part_size - 1}"
|
321
|
+
headers = {"Range": bytes_range}
|
322
|
+
download_handler = functools.partial(
|
323
|
+
self._download_part,
|
324
|
+
download_url,
|
325
|
+
headers,
|
326
|
+
start,
|
327
|
+
q,
|
328
|
+
download_has_error,
|
329
|
+
)
|
330
|
+
download_futures.append(executor.submit(download_handler))
|
331
|
+
|
332
|
+
# Wait for download
|
333
|
+
done, not_done = concurrent.futures.wait(
|
334
|
+
download_futures, return_when=concurrent.futures.FIRST_EXCEPTION
|
335
|
+
)
|
336
|
+
try:
|
337
|
+
for fut in done:
|
338
|
+
fut.result()
|
339
|
+
except Exception as e:
|
340
|
+
if env.is_debug():
|
341
|
+
logger.debug(f"Error downloading file: {e}")
|
342
|
+
download_has_error.set()
|
343
|
+
raise e
|
344
|
+
finally:
|
345
|
+
# Always signal the writer to stop
|
346
|
+
q.put(_CHUNK_QUEUE_SENTINEL)
|
347
|
+
write_future.result()
|
348
|
+
|
168
349
|
def store_reference(
|
169
350
|
self,
|
170
351
|
artifact: Artifact,
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import concurrent.futures
|
5
6
|
from typing import TYPE_CHECKING, Sequence
|
6
7
|
|
7
8
|
from wandb.sdk.internal.internal_api import Api as InternalApi
|
@@ -40,6 +41,8 @@ class StoragePolicy:
|
|
40
41
|
artifact: Artifact,
|
41
42
|
manifest_entry: ArtifactManifestEntry,
|
42
43
|
dest_path: str | None = None,
|
44
|
+
executor: concurrent.futures.Executor | None = None,
|
45
|
+
multipart: bool | None = None,
|
43
46
|
) -> FilePathStr:
|
44
47
|
raise NotImplementedError
|
45
48
|
|
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Type, Union, ca
|
|
7
7
|
|
8
8
|
import wandb
|
9
9
|
from wandb import util
|
10
|
-
from wandb._globals import _datatypes_callback
|
11
10
|
from wandb.sdk.lib import filesystem
|
12
11
|
from wandb.sdk.lib.paths import LogicalPath
|
13
12
|
|
@@ -192,7 +191,7 @@ class Media(WBValue):
|
|
192
191
|
shutil.move(self._path, new_path)
|
193
192
|
self._path = new_path
|
194
193
|
self._is_tmp = False
|
195
|
-
|
194
|
+
run._publish_file(media_path)
|
196
195
|
else:
|
197
196
|
try:
|
198
197
|
shutil.copy(self._path, new_path)
|
@@ -200,7 +199,7 @@ class Media(WBValue):
|
|
200
199
|
if not ignore_copy_err:
|
201
200
|
raise e
|
202
201
|
self._path = new_path
|
203
|
-
|
202
|
+
run._publish_file(media_path)
|
204
203
|
|
205
204
|
def to_json(self, run: Union["LocalRun", "Artifact"]) -> dict:
|
206
205
|
"""Serialize the object into a JSON blob.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Type, Union
|
2
2
|
|
3
|
-
import wandb
|
4
3
|
from wandb import util
|
4
|
+
from wandb.sdk import wandb_setup
|
5
5
|
|
6
6
|
if TYPE_CHECKING: # pragma: no cover
|
7
7
|
from wandb.sdk.artifacts.artifact import Artifact
|
@@ -11,6 +11,31 @@ if TYPE_CHECKING: # pragma: no cover
|
|
11
11
|
TypeMappingType = Dict[str, Type["WBValue"]]
|
12
12
|
|
13
13
|
|
14
|
+
def _is_maybe_offline() -> bool:
|
15
|
+
"""Guess whether wandb is configured to be offline.
|
16
|
+
|
17
|
+
This is an anti-pattern because there is no library-level "offline" mode:
|
18
|
+
only runs can be offline. Online and offline runs can exist in the same
|
19
|
+
process. This function is a heuristic that works only if there is at most
|
20
|
+
one run in the process, and could otherwise produce unexpected results.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
Whether the user likely configured wandb to be offline.
|
24
|
+
"""
|
25
|
+
singleton = wandb_setup._setup(start_service=False)
|
26
|
+
|
27
|
+
# First check: if there's a run, check if it is offline.
|
28
|
+
#
|
29
|
+
# This covers uses like `wandb.init(mode="offline")` which don't modify
|
30
|
+
# the singleton's settings.
|
31
|
+
if run := singleton.most_recent_active_run:
|
32
|
+
return run.offline
|
33
|
+
|
34
|
+
# Second check: default to global defaults derived from environment
|
35
|
+
# variables or passed explicitly to `wandb.setup()`.
|
36
|
+
return singleton.settings._offline
|
37
|
+
|
38
|
+
|
14
39
|
def _server_accepts_client_ids() -> bool:
|
15
40
|
from wandb.util import parse_version
|
16
41
|
|
@@ -25,15 +50,13 @@ def _server_accepts_client_ids() -> bool:
|
|
25
50
|
# AS OF NOW, 2024/11/06, we assume that all customer's server deployments accept
|
26
51
|
# client IDs.
|
27
52
|
|
28
|
-
if
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
return False
|
34
|
-
# Assume client IDs are accepted
|
53
|
+
if _is_maybe_offline():
|
54
|
+
singleton = wandb_setup._setup(start_service=False)
|
55
|
+
|
56
|
+
if run := singleton.most_recent_active_run:
|
57
|
+
return run._settings.allow_offline_artifacts
|
35
58
|
else:
|
36
|
-
return
|
59
|
+
return singleton.settings.allow_offline_artifacts
|
37
60
|
|
38
61
|
# If the script is online, request the max_cli_version and ensure the server
|
39
62
|
# is of a high enough version.
|
@@ -240,7 +263,7 @@ class WBValue:
|
|
240
263
|
self._artifact_target
|
241
264
|
and self._artifact_target.name
|
242
265
|
and self._artifact_target.artifact._is_draft_save_started()
|
243
|
-
and not
|
266
|
+
and not _is_maybe_offline()
|
244
267
|
and not _server_accepts_client_ids()
|
245
268
|
):
|
246
269
|
self._artifact_target.artifact.wait()
|
@@ -271,7 +294,7 @@ class WBValue:
|
|
271
294
|
self._artifact_target
|
272
295
|
and self._artifact_target.name
|
273
296
|
and self._artifact_target.artifact._is_draft_save_started()
|
274
|
-
and not
|
297
|
+
and not _is_maybe_offline()
|
275
298
|
and not _server_accepts_client_ids()
|
276
299
|
):
|
277
300
|
self._artifact_target.artifact.wait()
|
wandb/sdk/data_types/html.py
CHANGED
@@ -16,19 +16,46 @@ if TYPE_CHECKING: # pragma: no cover
|
|
16
16
|
|
17
17
|
|
18
18
|
class Html(BatchableMedia):
|
19
|
-
"""
|
20
|
-
|
21
|
-
Args:
|
22
|
-
data: (string or io object) HTML to display in wandb
|
23
|
-
inject: (boolean) Add a stylesheet to the HTML object. If set
|
24
|
-
to False the HTML will pass through unchanged.
|
25
|
-
"""
|
19
|
+
"""A class for logging HTML content to W&B."""
|
26
20
|
|
27
21
|
_log_type = "html-file"
|
28
22
|
|
29
|
-
def __init__(
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
data: Union[str, "TextIO"],
|
26
|
+
inject: bool = True,
|
27
|
+
data_is_not_path: bool = False,
|
28
|
+
) -> None:
|
29
|
+
"""Creates a W&B HTML object.
|
30
|
+
|
31
|
+
It can be initialized by providing a path to a file:
|
32
|
+
```
|
33
|
+
with wandb.init() as run:
|
34
|
+
run.log({"html": wandb.Html("./index.html")})
|
35
|
+
```
|
36
|
+
|
37
|
+
Alternatively, it can be initialized by providing literal HTML,
|
38
|
+
in either a string or IO object:
|
39
|
+
```
|
40
|
+
with wandb.init() as run:
|
41
|
+
run.log({"html": wandb.Html("<h1>Hello, world!</h1>")})
|
42
|
+
```
|
43
|
+
|
44
|
+
Args:
|
45
|
+
data:
|
46
|
+
A string that is a path to a file with the extension ".html",
|
47
|
+
or a string or IO object containing literal HTML.
|
48
|
+
inject: Add a stylesheet to the HTML object. If set
|
49
|
+
to False the HTML will pass through unchanged.
|
50
|
+
data_is_not_path: If set to False, the data will be
|
51
|
+
treated as a path to a file.
|
52
|
+
"""
|
30
53
|
super().__init__()
|
31
|
-
data_is_path =
|
54
|
+
data_is_path = (
|
55
|
+
isinstance(data, str)
|
56
|
+
and os.path.isfile(data)
|
57
|
+
and os.path.splitext(data)[1] == ".html"
|
58
|
+
) and not data_is_not_path
|
32
59
|
data_path = ""
|
33
60
|
if data_is_path:
|
34
61
|
assert isinstance(data, str)
|
wandb/sdk/data_types/image.py
CHANGED
@@ -34,8 +34,8 @@ if TYPE_CHECKING: # pragma: no cover
|
|
34
34
|
TorchTensorType = Union["torch.Tensor", "torch.Variable"]
|
35
35
|
|
36
36
|
|
37
|
-
def _server_accepts_image_filenames() -> bool:
|
38
|
-
if
|
37
|
+
def _server_accepts_image_filenames(run: "LocalRun") -> bool:
|
38
|
+
if run.offline:
|
39
39
|
return True
|
40
40
|
|
41
41
|
# Newer versions of wandb accept large image filenames arrays
|
@@ -51,15 +51,15 @@ def _server_accepts_image_filenames() -> bool:
|
|
51
51
|
return accepts_image_filenames
|
52
52
|
|
53
53
|
|
54
|
-
def _server_accepts_artifact_path() -> bool:
|
55
|
-
|
54
|
+
def _server_accepts_artifact_path(run: "LocalRun") -> bool:
|
55
|
+
if run.offline:
|
56
|
+
return False
|
57
|
+
|
58
|
+
max_cli_version = util._get_max_cli_version()
|
59
|
+
if max_cli_version is None:
|
60
|
+
return False
|
56
61
|
|
57
|
-
|
58
|
-
max_cli_version = util._get_max_cli_version() if not util._is_offline() else None
|
59
|
-
accepts_artifact_path: bool = max_cli_version is not None and parse_version(
|
60
|
-
target_version
|
61
|
-
) <= parse_version(max_cli_version)
|
62
|
-
return accepts_artifact_path
|
62
|
+
return util.parse_version("0.12.14") <= util.parse_version(max_cli_version)
|
63
63
|
|
64
64
|
|
65
65
|
class Image(BatchableMedia):
|
@@ -401,7 +401,7 @@ class Image(BatchableMedia):
|
|
401
401
|
)
|
402
402
|
|
403
403
|
if (
|
404
|
-
not _server_accepts_artifact_path()
|
404
|
+
not _server_accepts_artifact_path(run)
|
405
405
|
or self._get_artifact_entry_ref_url() is None
|
406
406
|
):
|
407
407
|
super().bind_to_run(run, key, step, id_, ignore_copy_err=ignore_copy_err)
|
@@ -575,7 +575,7 @@ class Image(BatchableMedia):
|
|
575
575
|
"format": format,
|
576
576
|
"count": num_images_to_log,
|
577
577
|
}
|
578
|
-
if _server_accepts_image_filenames():
|
578
|
+
if _server_accepts_image_filenames(run):
|
579
579
|
meta["filenames"] = [
|
580
580
|
obj.get("path", obj.get("artifact_path")) for obj in jsons
|
581
581
|
]
|
wandb/sdk/data_types/table.py
CHANGED
@@ -480,6 +480,11 @@ class Table(Media):
|
|
480
480
|
max_rows = Table.MAX_ROWS
|
481
481
|
n_rows = len(self.data)
|
482
482
|
if n_rows > max_rows and warn:
|
483
|
+
# NOTE: Never raises for reinit="create_new" runs.
|
484
|
+
# Since this is called by bind_to_run(), this can be fixed by
|
485
|
+
# propagating the run. It cannot be fixed for to_json() calls
|
486
|
+
# that are given an artifact, other than by deferring to singleton
|
487
|
+
# settings.
|
483
488
|
if wandb.run and (
|
484
489
|
wandb.run.settings.table_raise_on_max_row_limit_exceeded
|
485
490
|
or wandb.run.settings.strict
|
@@ -431,6 +431,8 @@ class Trace:
|
|
431
431
|
name: The name of the trace to be logged
|
432
432
|
"""
|
433
433
|
trace_tree = WBTraceTree(self._span, self._model_dict)
|
434
|
+
# NOTE: Does not work for reinit="create_new" runs.
|
435
|
+
# This method should be deprecated and users should call run.log().
|
434
436
|
assert (
|
435
437
|
wandb.run is not None
|
436
438
|
), "You must call wandb.init() before logging a trace"
|