wandb 0.19.11__py3-none-win32.whl → 0.20.0__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 -2
- wandb/__init__.pyi +3 -6
- wandb/_iterutils.py +26 -7
- wandb/_pydantic/__init__.py +2 -1
- wandb/_pydantic/utils.py +7 -0
- wandb/agents/pyagent.py +9 -15
- wandb/analytics/sentry.py +1 -2
- wandb/apis/attrs.py +3 -4
- wandb/apis/importers/internals/util.py +1 -1
- wandb/apis/importers/validation.py +2 -2
- wandb/apis/importers/wandb.py +30 -25
- wandb/apis/normalize.py +2 -2
- wandb/apis/public/__init__.py +1 -0
- wandb/apis/public/api.py +37 -33
- wandb/apis/public/artifacts.py +103 -72
- wandb/apis/public/jobs.py +3 -2
- wandb/apis/public/registries/registries_search.py +4 -2
- wandb/apis/public/registries/registry.py +1 -1
- wandb/apis/public/registries/utils.py +9 -9
- wandb/apis/public/runs.py +18 -6
- wandb/automations/_filters/expressions.py +1 -1
- wandb/automations/_filters/operators.py +1 -1
- wandb/automations/_filters/run_metrics.py +1 -1
- wandb/beta/workflows.py +6 -5
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +54 -73
- wandb/docker/__init__.py +21 -74
- wandb/docker/names.py +40 -0
- wandb/env.py +0 -1
- wandb/errors/util.py +1 -1
- wandb/filesync/step_checksum.py +1 -1
- wandb/filesync/step_upload.py +1 -1
- wandb/integration/diffusers/resolvers/multimodal.py +1 -2
- wandb/integration/gym/__init__.py +5 -6
- wandb/integration/keras/callbacks/model_checkpoint.py +2 -2
- wandb/integration/keras/keras.py +13 -19
- wandb/integration/kfp/kfp_patch.py +2 -3
- wandb/integration/langchain/wandb_tracer.py +1 -1
- wandb/integration/metaflow/metaflow.py +13 -13
- wandb/integration/openai/fine_tuning.py +3 -2
- wandb/integration/sagemaker/auth.py +2 -1
- wandb/integration/sklearn/utils.py +2 -1
- wandb/integration/tensorboard/__init__.py +1 -1
- wandb/integration/tensorboard/log.py +2 -5
- wandb/integration/tensorflow/__init__.py +2 -2
- wandb/jupyter.py +20 -17
- wandb/plot/confusion_matrix.py +1 -1
- wandb/plot/utils.py +8 -7
- wandb/proto/v3/wandb_internal_pb2.py +355 -335
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v4/wandb_internal_pb2.py +339 -335
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v5/wandb_internal_pb2.py +339 -335
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v6/wandb_internal_pb2.py +339 -335
- wandb/proto/v6/wandb_settings_pb2.py +2 -2
- wandb/proto/v6/wandb_telemetry_pb2.py +12 -12
- wandb/proto/wandb_deprecated.py +6 -8
- wandb/sdk/artifacts/_internal_artifact.py +43 -0
- wandb/sdk/artifacts/_validators.py +55 -35
- wandb/sdk/artifacts/artifact.py +117 -115
- wandb/sdk/artifacts/artifact_download_logger.py +2 -0
- wandb/sdk/artifacts/artifact_saver.py +1 -3
- wandb/sdk/artifacts/artifact_state.py +2 -0
- wandb/sdk/artifacts/artifact_ttl.py +2 -0
- wandb/sdk/artifacts/exceptions.py +14 -0
- wandb/sdk/artifacts/staging.py +2 -0
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +2 -6
- wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/tracking_handler.py +2 -6
- wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +1 -5
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +1 -1
- wandb/sdk/artifacts/storage_layout.py +2 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +3 -3
- wandb/sdk/backend/backend.py +11 -182
- wandb/sdk/data_types/_dtypes.py +2 -6
- wandb/sdk/data_types/audio.py +20 -3
- wandb/sdk/data_types/base_types/media.py +12 -7
- wandb/sdk/data_types/base_types/wb_value.py +8 -18
- wandb/sdk/data_types/bokeh.py +19 -2
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +17 -1
- wandb/sdk/data_types/helper_types/image_mask.py +7 -1
- wandb/sdk/data_types/html.py +4 -4
- wandb/sdk/data_types/image.py +164 -103
- wandb/sdk/data_types/molecule.py +6 -6
- wandb/sdk/data_types/object_3d.py +10 -5
- wandb/sdk/data_types/saved_model.py +11 -6
- wandb/sdk/data_types/table.py +313 -83
- wandb/sdk/data_types/table_decorators.py +108 -0
- wandb/sdk/data_types/utils.py +43 -7
- wandb/sdk/data_types/video.py +21 -3
- wandb/sdk/interface/interface.py +10 -0
- wandb/sdk/internal/datastore.py +2 -6
- wandb/sdk/internal/file_pusher.py +1 -5
- wandb/sdk/internal/file_stream.py +8 -17
- wandb/sdk/internal/handler.py +2 -2
- wandb/sdk/internal/incremental_table_util.py +53 -0
- wandb/sdk/internal/internal.py +3 -5
- wandb/sdk/internal/internal_api.py +66 -89
- wandb/sdk/internal/job_builder.py +2 -7
- wandb/sdk/internal/profiler.py +2 -2
- wandb/sdk/internal/progress.py +1 -3
- wandb/sdk/internal/run.py +1 -6
- wandb/sdk/internal/sender.py +24 -36
- wandb/sdk/internal/system/assets/aggregators.py +1 -7
- wandb/sdk/internal/system/assets/disk.py +3 -3
- wandb/sdk/internal/system/assets/gpu.py +4 -4
- wandb/sdk/internal/system/assets/gpu_amd.py +4 -4
- wandb/sdk/internal/system/assets/interfaces.py +6 -6
- wandb/sdk/internal/system/assets/tpu.py +1 -1
- wandb/sdk/internal/system/assets/trainium.py +6 -6
- wandb/sdk/internal/system/system_info.py +5 -7
- wandb/sdk/internal/system/system_monitor.py +4 -4
- wandb/sdk/internal/tb_watcher.py +5 -7
- wandb/sdk/launch/_launch.py +1 -1
- wandb/sdk/launch/_project_spec.py +19 -20
- wandb/sdk/launch/agent/agent.py +3 -3
- wandb/sdk/launch/agent/config.py +1 -1
- wandb/sdk/launch/agent/job_status_tracker.py +2 -2
- wandb/sdk/launch/builder/build.py +2 -3
- wandb/sdk/launch/builder/kaniko_builder.py +5 -4
- wandb/sdk/launch/environment/gcp_environment.py +1 -2
- wandb/sdk/launch/registry/azure_container_registry.py +2 -2
- wandb/sdk/launch/registry/elastic_container_registry.py +2 -2
- wandb/sdk/launch/registry/google_artifact_registry.py +3 -3
- wandb/sdk/launch/runner/abstract.py +5 -5
- wandb/sdk/launch/runner/kubernetes_monitor.py +2 -2
- wandb/sdk/launch/runner/kubernetes_runner.py +1 -1
- wandb/sdk/launch/runner/sagemaker_runner.py +2 -4
- wandb/sdk/launch/runner/vertex_runner.py +2 -7
- wandb/sdk/launch/sweeps/__init__.py +1 -1
- wandb/sdk/launch/sweeps/scheduler.py +2 -2
- wandb/sdk/launch/sweeps/utils.py +3 -3
- wandb/sdk/launch/utils.py +3 -4
- wandb/sdk/lib/apikey.py +5 -8
- wandb/sdk/lib/config_util.py +3 -3
- wandb/sdk/lib/fsm.py +3 -18
- wandb/sdk/lib/gitlib.py +6 -5
- wandb/sdk/lib/ipython.py +2 -2
- wandb/sdk/lib/json_util.py +9 -14
- wandb/sdk/lib/printer.py +3 -8
- wandb/sdk/lib/redirect.py +1 -1
- wandb/sdk/lib/retry.py +3 -7
- wandb/sdk/lib/run_moment.py +2 -2
- wandb/sdk/lib/service_connection.py +19 -21
- wandb/sdk/lib/service_token.py +1 -2
- wandb/sdk/mailbox/mailbox_handle.py +3 -7
- wandb/sdk/mailbox/response_handle.py +2 -6
- wandb/sdk/service/streams.py +3 -7
- wandb/sdk/verify/verify.py +5 -6
- wandb/sdk/wandb_config.py +1 -1
- wandb/sdk/wandb_init.py +38 -106
- wandb/sdk/wandb_login.py +7 -6
- wandb/sdk/wandb_run.py +52 -240
- wandb/sdk/wandb_settings.py +71 -60
- wandb/sdk/wandb_setup.py +43 -17
- wandb/sdk/wandb_watch.py +5 -7
- wandb/sync/__init__.py +1 -1
- wandb/sync/sync.py +13 -13
- wandb/util.py +17 -35
- wandb/wandb_agent.py +8 -11
- {wandb-0.19.11.dist-info → wandb-0.20.0.dist-info}/METADATA +5 -5
- {wandb-0.19.11.dist-info → wandb-0.20.0.dist-info}/RECORD +170 -168
- wandb/docker/auth.py +0 -435
- wandb/docker/www_authenticate.py +0 -94
- {wandb-0.19.11.dist-info → wandb-0.20.0.dist-info}/WHEEL +0 -0
- {wandb-0.19.11.dist-info → wandb-0.20.0.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.11.dist-info → wandb-0.20.0.dist-info}/licenses/LICENSE +0 -0
wandb/__init__.py
CHANGED
@@ -10,7 +10,7 @@ For reference documentation, see https://docs.wandb.com/ref/python.
|
|
10
10
|
"""
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
-
__version__ = "0.
|
13
|
+
__version__ = "0.20.0"
|
14
14
|
|
15
15
|
|
16
16
|
from wandb.errors import Error
|
@@ -169,7 +169,6 @@ gym = _lazyloader.LazyLoader("wandb.gym", globals(), "wandb.integration.gym")
|
|
169
169
|
lightgbm = _lazyloader.LazyLoader(
|
170
170
|
"wandb.lightgbm", globals(), "wandb.integration.lightgbm"
|
171
171
|
)
|
172
|
-
docker = _lazyloader.LazyLoader("wandb.docker", globals(), "wandb.docker")
|
173
172
|
jupyter = _lazyloader.LazyLoader("wandb.jupyter", globals(), "wandb.jupyter")
|
174
173
|
sacred = _lazyloader.LazyLoader("wandb.sacred", globals(), "wandb.integration.sacred")
|
175
174
|
|
wandb/__init__.pyi
CHANGED
@@ -106,7 +106,7 @@ if TYPE_CHECKING:
|
|
106
106
|
import wandb
|
107
107
|
from wandb.plot import CustomChart
|
108
108
|
|
109
|
-
__version__: str = "0.
|
109
|
+
__version__: str = "0.20.0"
|
110
110
|
|
111
111
|
run: Run | None
|
112
112
|
config: wandb_config.Config
|
@@ -512,7 +512,6 @@ def log(
|
|
512
512
|
data: dict[str, Any],
|
513
513
|
step: int | None = None,
|
514
514
|
commit: bool | None = None,
|
515
|
-
sync: bool | None = None,
|
516
515
|
) -> None:
|
517
516
|
"""Upload run data.
|
518
517
|
|
@@ -617,7 +616,6 @@ def log(
|
|
617
616
|
accumulate data for the step. See the notes in the description.
|
618
617
|
If `step` is `None`, then the default is `commit=True`;
|
619
618
|
otherwise, the default is `commit=False`.
|
620
|
-
sync: This argument is deprecated and does nothing.
|
621
619
|
|
622
620
|
Examples:
|
623
621
|
For more and more detailed examples, see
|
@@ -748,7 +746,7 @@ def log(
|
|
748
746
|
...
|
749
747
|
|
750
748
|
def save(
|
751
|
-
glob_str: str | os.PathLike
|
749
|
+
glob_str: str | os.PathLike,
|
752
750
|
base_path: str | os.PathLike | None = None,
|
753
751
|
policy: PolicyName = "live",
|
754
752
|
) -> bool | list[str]:
|
@@ -978,8 +976,7 @@ def use_artifact(
|
|
978
976
|
You can also pass an Artifact object created by calling `wandb.Artifact`
|
979
977
|
type: (str, optional) The type of artifact to use.
|
980
978
|
aliases: (list, optional) Aliases to apply to this artifact
|
981
|
-
use_as:
|
982
|
-
Will be shown in UI.
|
979
|
+
use_as: This argument is deprecated and does nothing.
|
983
980
|
|
984
981
|
Returns:
|
985
982
|
An `Artifact` object.
|
wandb/_iterutils.py
CHANGED
@@ -1,14 +1,31 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import Iterable, TypeVar
|
3
|
+
from typing import TYPE_CHECKING, Any, Iterable, TypeVar, Union, overload
|
4
4
|
|
5
|
-
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
T = TypeVar("T")
|
7
|
+
ClassInfo = Union[type[T], tuple[type[T], ...]]
|
8
|
+
|
9
|
+
|
10
|
+
@overload
|
11
|
+
def always_list(obj: Iterable[T], base_type: ClassInfo = ...) -> list[T]: ...
|
12
|
+
@overload
|
13
|
+
def always_list(obj: T, base_type: ClassInfo = ...) -> list[T]: ...
|
14
|
+
def always_list(obj: Any, base_type: Any = (str, bytes)) -> list[T]:
|
15
|
+
"""Return a guaranteed list of objects from a single instance OR iterable of such objects.
|
16
|
+
|
17
|
+
By default, assume the returned list should have string-like elements (i.e. `str`/`bytes`).
|
18
|
+
|
19
|
+
Adapted from `more_itertools.always_iterable`, but simplified for internal use. See:
|
20
|
+
https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable
|
21
|
+
"""
|
22
|
+
return [obj] if isinstance(obj, base_type) else list(obj)
|
6
23
|
|
7
24
|
|
8
25
|
def one(
|
9
26
|
iterable: Iterable[T],
|
10
|
-
too_short: Exception | None = None,
|
11
|
-
too_long: Exception | None = None,
|
27
|
+
too_short: type[Exception] | Exception | None = None,
|
28
|
+
too_long: type[Exception] | Exception | None = None,
|
12
29
|
) -> T:
|
13
30
|
"""Return the only item in the iterable.
|
14
31
|
|
@@ -35,12 +52,14 @@ def one(
|
|
35
52
|
it = iter(iterable)
|
36
53
|
try:
|
37
54
|
obj = next(it)
|
38
|
-
except StopIteration
|
39
|
-
raise too_short or ValueError("Expected 1 item in iterable, got 0") from
|
55
|
+
except StopIteration:
|
56
|
+
raise (too_short or ValueError("Expected 1 item in iterable, got 0")) from None
|
40
57
|
|
41
58
|
# ...the second item doesn't
|
42
59
|
try:
|
43
60
|
_ = next(it)
|
44
61
|
except StopIteration:
|
45
62
|
return obj
|
46
|
-
raise
|
63
|
+
raise (
|
64
|
+
too_long or ValueError("Expected 1 item in iterable, got multiple")
|
65
|
+
) from None
|
wandb/_pydantic/__init__.py
CHANGED
@@ -8,7 +8,7 @@ from .base import (
|
|
8
8
|
Typename,
|
9
9
|
ensure_json,
|
10
10
|
)
|
11
|
-
from .utils import IS_PYDANTIC_V2, from_json, pydantic_isinstance, to_json
|
11
|
+
from .utils import IS_PYDANTIC_V2, from_json, gql_typename, pydantic_isinstance, to_json
|
12
12
|
from .v1_compat import AliasChoices, computed_field, field_validator, model_validator
|
13
13
|
|
14
14
|
__all__ = [
|
@@ -26,4 +26,5 @@ __all__ = [
|
|
26
26
|
"to_json",
|
27
27
|
"from_json",
|
28
28
|
"ensure_json",
|
29
|
+
"gql_typename",
|
29
30
|
]
|
wandb/_pydantic/utils.py
CHANGED
@@ -20,6 +20,13 @@ IS_PYDANTIC_V2: bool = int(pydantic_major) >= 2
|
|
20
20
|
BaseModelType: TypeAlias = Type[BaseModel]
|
21
21
|
|
22
22
|
|
23
|
+
def gql_typename(cls: type[BaseModel]) -> str:
|
24
|
+
"""Get the GraphQL typename for a Pydantic model."""
|
25
|
+
if (field := cls.model_fields.get("typename__")) and (typename := field.default):
|
26
|
+
return typename
|
27
|
+
raise TypeError(f"Cannot extract GraphQL typename from: {cls.__qualname__!r}.")
|
28
|
+
|
29
|
+
|
23
30
|
if IS_PYDANTIC_V2:
|
24
31
|
import pydantic_core # pydantic_core is only installed by pydantic v2
|
25
32
|
|
wandb/agents/pyagent.py
CHANGED
@@ -176,7 +176,7 @@ class Agent:
|
|
176
176
|
return
|
177
177
|
time.sleep(5)
|
178
178
|
|
179
|
-
def _run_jobs_from_queue(self):
|
179
|
+
def _run_jobs_from_queue(self):
|
180
180
|
global _INSTANCES
|
181
181
|
_INSTANCES += 1
|
182
182
|
try:
|
@@ -239,9 +239,7 @@ class Agent:
|
|
239
239
|
elif (
|
240
240
|
time.time() - self._start_time < self.FLAPPING_MAX_SECONDS
|
241
241
|
) and (len(self._exceptions) >= self.FLAPPING_MAX_FAILURES):
|
242
|
-
msg = "Detected {} failed runs in the first {} seconds, killing sweep."
|
243
|
-
self.FLAPPING_MAX_FAILURES, self.FLAPPING_MAX_SECONDS
|
244
|
-
)
|
242
|
+
msg = f"Detected {self.FLAPPING_MAX_FAILURES} failed runs in the first {self.FLAPPING_MAX_SECONDS} seconds, killing sweep."
|
245
243
|
logger.error(msg)
|
246
244
|
wandb.termerror(msg)
|
247
245
|
wandb.termlog(
|
@@ -253,9 +251,7 @@ class Agent:
|
|
253
251
|
self._max_initial_failures < len(self._exceptions)
|
254
252
|
and len(self._exceptions) >= count
|
255
253
|
):
|
256
|
-
msg = "Detected {} failed runs in a row at start, killing sweep."
|
257
|
-
self._max_initial_failures
|
258
|
-
)
|
254
|
+
msg = f"Detected {self._max_initial_failures} failed runs in a row at start, killing sweep."
|
259
255
|
logger.error(msg)
|
260
256
|
wandb.termerror(msg)
|
261
257
|
wandb.termlog(
|
@@ -272,13 +268,13 @@ class Agent:
|
|
272
268
|
wandb.termlog("Ctrl + C detected. Stopping sweep.")
|
273
269
|
self._exit()
|
274
270
|
return
|
275
|
-
except Exception
|
271
|
+
except Exception:
|
276
272
|
if self._exit_flag:
|
277
273
|
logger.debug("Exiting main loop due to exit flag.")
|
278
274
|
wandb.termlog("Sweep Agent: Killed.")
|
279
275
|
return
|
280
276
|
else:
|
281
|
-
raise
|
277
|
+
raise
|
282
278
|
finally:
|
283
279
|
_INSTANCES -= 1
|
284
280
|
|
@@ -305,8 +301,8 @@ class Agent:
|
|
305
301
|
|
306
302
|
self._function()
|
307
303
|
wandb.finish()
|
308
|
-
except KeyboardInterrupt
|
309
|
-
raise
|
304
|
+
except KeyboardInterrupt:
|
305
|
+
raise
|
310
306
|
except Exception as e:
|
311
307
|
wandb.finish(exit_code=1)
|
312
308
|
if self._run_status[run_id] == RunStatus.RUNNING:
|
@@ -320,9 +316,7 @@ class Agent:
|
|
320
316
|
|
321
317
|
def run(self):
|
322
318
|
logger.info(
|
323
|
-
"Starting sweep agent: entity={}, project={}, count={}"
|
324
|
-
self._entity, self._project, self._count
|
325
|
-
)
|
319
|
+
f"Starting sweep agent: entity={self._entity}, project={self._project}, count={self._count}"
|
326
320
|
)
|
327
321
|
self._setup()
|
328
322
|
# self._main_thread = threading.Thread(target=self._run_jobs_from_queue)
|
@@ -345,7 +339,7 @@ def pyagent(sweep_id, function, entity=None, project=None, count=None):
|
|
345
339
|
count (int, optional): the number of trials to run.
|
346
340
|
"""
|
347
341
|
if not callable(function):
|
348
|
-
raise
|
342
|
+
raise TypeError("function parameter must be callable!")
|
349
343
|
agent = Agent(
|
350
344
|
sweep_id,
|
351
345
|
function=function,
|
wandb/analytics/sentry.py
CHANGED
@@ -209,7 +209,6 @@ class Sentry:
|
|
209
209
|
"sweep_url",
|
210
210
|
"sweep_id",
|
211
211
|
"deployment",
|
212
|
-
"x_disable_service",
|
213
212
|
"x_require_legacy_service",
|
214
213
|
"launch",
|
215
214
|
"_platform",
|
@@ -256,7 +255,7 @@ class Sentry:
|
|
256
255
|
|
257
256
|
email = tags.get("email")
|
258
257
|
if email:
|
259
|
-
self.scope.user = {"email": email}
|
258
|
+
self.scope.user = {"email": email}
|
260
259
|
|
261
260
|
# todo: add back the option to pass general tags see: c645f625d1c1a3db4a6b0e2aa8e924fee101904c (wandb/util.py)
|
262
261
|
|
wandb/apis/attrs.py
CHANGED
@@ -28,14 +28,13 @@ class Attrs:
|
|
28
28
|
|
29
29
|
try:
|
30
30
|
from IPython import display
|
31
|
-
|
32
|
-
display.display(display.HTML(html))
|
33
|
-
return True
|
34
|
-
|
35
31
|
except ImportError:
|
36
32
|
wandb.termwarn(".display() only works in jupyter environments")
|
37
33
|
return False
|
38
34
|
|
35
|
+
display.display(display.HTML(html))
|
36
|
+
return True
|
37
|
+
|
39
38
|
def to_html(self, *args, **kwargs):
|
40
39
|
return None
|
41
40
|
|
@@ -92,8 +92,8 @@ def _check_entry_is_downloable(entry):
|
|
92
92
|
|
93
93
|
try:
|
94
94
|
resp = requests.head(url, allow_redirects=True)
|
95
|
-
except Exception
|
96
|
-
logger.
|
95
|
+
except Exception:
|
96
|
+
logger.exception(f"Problem validating {entry=}")
|
97
97
|
return False
|
98
98
|
|
99
99
|
if resp.status_code != 200:
|
wandb/apis/importers/wandb.py
CHANGED
@@ -151,7 +151,7 @@ class WandbRun:
|
|
151
151
|
if self._parquet_history_paths:
|
152
152
|
rows = self._get_rows_from_parquet_history_paths()
|
153
153
|
else:
|
154
|
-
logger.
|
154
|
+
logger.warning(
|
155
155
|
"No parquet files detected; using scan history (this may not be reliable)"
|
156
156
|
)
|
157
157
|
rows = self.run.scan_history()
|
@@ -453,13 +453,15 @@ class WandbImporter:
|
|
453
453
|
try:
|
454
454
|
dst_collection = self.dst_api.artifact_collection(art_type, art_name)
|
455
455
|
except (wandb.CommError, ValueError):
|
456
|
-
logger.
|
456
|
+
logger.warning(f"Collection doesn't exist {art_type=}, {art_name=}")
|
457
457
|
return
|
458
458
|
|
459
459
|
try:
|
460
460
|
dst_collection.delete()
|
461
461
|
except (wandb.CommError, ValueError) as e:
|
462
|
-
logger.
|
462
|
+
logger.warning(
|
463
|
+
f"Collection can't be deleted, {art_type=}, {art_name=}, {e=}"
|
464
|
+
)
|
463
465
|
return
|
464
466
|
|
465
467
|
def _import_artifact_sequence(
|
@@ -475,7 +477,7 @@ class WandbImporter:
|
|
475
477
|
if not seq.artifacts:
|
476
478
|
# The artifact sequence has no versions. This usually means all artifacts versions were deleted intentionally,
|
477
479
|
# but it can also happen if the sequence represents run history and that run was deleted.
|
478
|
-
logger.
|
480
|
+
logger.warning(f"Artifact {seq=} has no artifacts, skipping.")
|
479
481
|
return
|
480
482
|
|
481
483
|
if namespace is None:
|
@@ -517,7 +519,7 @@ class WandbImporter:
|
|
517
519
|
|
518
520
|
# Could be logged by None (rare) or ValueError
|
519
521
|
if wandb_run is None:
|
520
|
-
logger.
|
522
|
+
logger.warning(
|
521
523
|
f"Run for {art.name=} does not exist (deleted?), using {run_or_dummy=}"
|
522
524
|
)
|
523
525
|
wandb_run = run_or_dummy
|
@@ -546,10 +548,12 @@ class WandbImporter:
|
|
546
548
|
retry_arts_func = internal.exp_retry(self._dst_api.artifacts)
|
547
549
|
dst_arts = list(retry_arts_func(seq.type_, seq.name))
|
548
550
|
except wandb.CommError:
|
549
|
-
logger.
|
551
|
+
logger.warning(
|
552
|
+
f"{seq=} does not exist in dst. Has it already been deleted?"
|
553
|
+
)
|
550
554
|
return
|
551
|
-
except TypeError
|
552
|
-
logger.
|
555
|
+
except TypeError:
|
556
|
+
logger.exception("Problem getting dst versions (try again later).")
|
553
557
|
return
|
554
558
|
|
555
559
|
for art in dst_arts:
|
@@ -562,9 +566,9 @@ class WandbImporter:
|
|
562
566
|
art.delete(delete_aliases=True)
|
563
567
|
except wandb.CommError as e:
|
564
568
|
if "cannot delete system managed artifact" in str(e):
|
565
|
-
logger.
|
569
|
+
logger.warning("Cannot delete system managed artifact")
|
566
570
|
else:
|
567
|
-
raise
|
571
|
+
raise
|
568
572
|
|
569
573
|
def _get_dst_art(
|
570
574
|
self, src_art: Run, entity: Optional[str] = None, project: Optional[str] = None
|
@@ -701,7 +705,7 @@ class WandbImporter:
|
|
701
705
|
)
|
702
706
|
except ValueError as e:
|
703
707
|
if "Could not find project" in str(e):
|
704
|
-
logger.
|
708
|
+
logger.exception("Could not find project, does it exist?")
|
705
709
|
continue
|
706
710
|
|
707
711
|
for run in runs:
|
@@ -732,7 +736,7 @@ class WandbImporter:
|
|
732
736
|
api.create_project(project, entity)
|
733
737
|
except requests.exceptions.HTTPError as e:
|
734
738
|
if e.response.status_code != 409:
|
735
|
-
logger.
|
739
|
+
logger.warning(f"Issue upserting {entity=}/{project=}, {e=}")
|
736
740
|
|
737
741
|
logger.debug(f"Upserting report {entity=}, {project=}, {name=}, {title=}")
|
738
742
|
api.client.execute(
|
@@ -1169,8 +1173,8 @@ class WandbImporter:
|
|
1169
1173
|
for art in seq:
|
1170
1174
|
try:
|
1171
1175
|
logged_by = _get_run_or_dummy_from_art(art, self.src_api)
|
1172
|
-
except requests.HTTPError
|
1173
|
-
logger.
|
1176
|
+
except requests.HTTPError:
|
1177
|
+
logger.exception(f"Failed to get run, skipping: {art=}")
|
1174
1178
|
continue
|
1175
1179
|
|
1176
1180
|
if art.type == "wandb-history" and isinstance(logged_by, _DummyRun):
|
@@ -1215,9 +1219,10 @@ class WandbImporter:
|
|
1215
1219
|
art = seq.artifacts[0]
|
1216
1220
|
try:
|
1217
1221
|
logged_by = _get_run_or_dummy_from_art(art, self.src_api)
|
1218
|
-
except requests.HTTPError
|
1219
|
-
logger.
|
1220
|
-
f"Validate Artifact http error: {art.entity=},
|
1222
|
+
except requests.HTTPError:
|
1223
|
+
logger.exception(
|
1224
|
+
f"Validate Artifact http error: {art.entity=},"
|
1225
|
+
f" {art.project=}, {art.name=}"
|
1221
1226
|
)
|
1222
1227
|
continue
|
1223
1228
|
|
@@ -1347,8 +1352,8 @@ class WandbImporter:
|
|
1347
1352
|
types = []
|
1348
1353
|
try:
|
1349
1354
|
types = [t for t in api.artifact_types(ns.path)]
|
1350
|
-
except Exception
|
1351
|
-
logger.
|
1355
|
+
except Exception:
|
1356
|
+
logger.exception("Failed to get artifact types.")
|
1352
1357
|
|
1353
1358
|
for t in types:
|
1354
1359
|
collections = []
|
@@ -1359,8 +1364,8 @@ class WandbImporter:
|
|
1359
1364
|
|
1360
1365
|
try:
|
1361
1366
|
collections = t.collections()
|
1362
|
-
except Exception
|
1363
|
-
logger.
|
1367
|
+
except Exception:
|
1368
|
+
logger.exception("Failed to get artifact collections.")
|
1364
1369
|
|
1365
1370
|
for c in collections:
|
1366
1371
|
if c.is_sequence():
|
@@ -1471,7 +1476,7 @@ def _read_ndjson(fname: str) -> Optional[pl.DataFrame]:
|
|
1471
1476
|
return None
|
1472
1477
|
if "error parsing ndjson" in str(e):
|
1473
1478
|
return None
|
1474
|
-
raise
|
1479
|
+
raise
|
1475
1480
|
|
1476
1481
|
return df
|
1477
1482
|
|
@@ -1482,7 +1487,7 @@ def _get_run_or_dummy_from_art(art: Artifact, api=None):
|
|
1482
1487
|
try:
|
1483
1488
|
run = art.logged_by()
|
1484
1489
|
except ValueError as e:
|
1485
|
-
logger.
|
1490
|
+
logger.warning(
|
1486
1491
|
f"Can't log artifact because run doesn't exist, {art=}, {run=}, {e=}"
|
1487
1492
|
)
|
1488
1493
|
|
@@ -1538,8 +1543,8 @@ def _download_art(art: Artifact, root: str) -> Optional[str]:
|
|
1538
1543
|
try:
|
1539
1544
|
with patch("click.echo"):
|
1540
1545
|
return art.download(root=root, skip_cache=True)
|
1541
|
-
except Exception
|
1542
|
-
logger.
|
1546
|
+
except Exception:
|
1547
|
+
logger.exception(f"Error downloading artifact {art=}")
|
1543
1548
|
|
1544
1549
|
|
1545
1550
|
def _clone_art(art: Artifact, root: Optional[str] = None):
|
wandb/apis/normalize.py
CHANGED
@@ -61,8 +61,8 @@ def normalize_exceptions(func: _F) -> _F:
|
|
61
61
|
raise CommError(message, err.last_exception).with_traceback(
|
62
62
|
sys.exc_info()[2]
|
63
63
|
)
|
64
|
-
except Error
|
65
|
-
raise
|
64
|
+
except Error:
|
65
|
+
raise
|
66
66
|
except Exception as err:
|
67
67
|
# gql raises server errors with dict's as strings...
|
68
68
|
if len(err.args) > 0:
|
wandb/apis/public/__init__.py
CHANGED
@@ -22,6 +22,7 @@ from wandb.apis.public.jobs import (
|
|
22
22
|
)
|
23
23
|
from wandb.apis.public.projects import PROJECT_FRAGMENT, Project, Projects
|
24
24
|
from wandb.apis.public.query_generator import QueryGenerator
|
25
|
+
from wandb.apis.public.registries.registry import Registry
|
25
26
|
from wandb.apis.public.reports import (
|
26
27
|
BetaReport,
|
27
28
|
PanelMetricsHelper,
|
wandb/apis/public/api.py
CHANGED
@@ -103,7 +103,9 @@ class RetryingClient:
|
|
103
103
|
check_retry_fn=util.no_retry_auth,
|
104
104
|
retryable_exceptions=(RetryError, requests.RequestException),
|
105
105
|
)
|
106
|
-
def execute(
|
106
|
+
def execute(
|
107
|
+
self, *args, **kwargs
|
108
|
+
): # User not encouraged to use this class directly
|
107
109
|
try:
|
108
110
|
return self._client.execute(*args, **kwargs)
|
109
111
|
except requests.exceptions.ReadTimeout:
|
@@ -122,10 +124,12 @@ class RetryingClient:
|
|
122
124
|
self._server_info = self.execute(self.INFO_QUERY).get("serverInfo")
|
123
125
|
return self._server_info
|
124
126
|
|
125
|
-
def version_supported(
|
126
|
-
|
127
|
+
def version_supported(
|
128
|
+
self, min_version: str
|
129
|
+
) -> bool: # User not encouraged to use this class directly
|
130
|
+
from packaging.version import parse
|
127
131
|
|
128
|
-
return
|
132
|
+
return parse(min_version) <= parse(
|
129
133
|
self.server_info["cliVersionInfo"]["max_cli_version"]
|
130
134
|
)
|
131
135
|
|
@@ -272,20 +276,21 @@ class Api:
|
|
272
276
|
api_key: Optional[str] = None,
|
273
277
|
) -> None:
|
274
278
|
self.settings = InternalApi().settings()
|
279
|
+
|
275
280
|
_overrides = overrides or {}
|
276
|
-
self._api_key = api_key
|
277
|
-
if self.api_key is None and _thread_local_api_settings.cookies is None:
|
278
|
-
wandb.login(host=_overrides.get("base_url"))
|
279
281
|
self.settings.update(_overrides)
|
282
|
+
self.settings["base_url"] = self.settings["base_url"].rstrip("/")
|
283
|
+
if "organization" in _overrides:
|
284
|
+
self.settings["organization"] = _overrides["organization"]
|
280
285
|
if "username" in _overrides and "entity" not in _overrides:
|
281
286
|
wandb.termwarn(
|
282
287
|
'Passing "username" to Api is deprecated. please use "entity" instead.'
|
283
288
|
)
|
284
289
|
self.settings["entity"] = _overrides["username"]
|
285
|
-
self.settings["base_url"] = self.settings["base_url"].rstrip("/")
|
286
290
|
|
287
|
-
|
288
|
-
|
291
|
+
self._api_key = api_key
|
292
|
+
if self.api_key is None and _thread_local_api_settings.cookies is None:
|
293
|
+
wandb.login(host=_overrides.get("base_url"))
|
289
294
|
|
290
295
|
self._viewer = None
|
291
296
|
self._projects = {}
|
@@ -318,7 +323,6 @@ class Api:
|
|
318
323
|
)
|
319
324
|
)
|
320
325
|
self._client = RetryingClient(self._base_client)
|
321
|
-
self._server_features_cache: Optional[dict[str, bool]] = None
|
322
326
|
|
323
327
|
def create_project(self, name: str, entity: str) -> None:
|
324
328
|
"""Create a new project.
|
@@ -1464,10 +1468,11 @@ class Api:
|
|
1464
1468
|
"""
|
1465
1469
|
try:
|
1466
1470
|
self._artifact(name, type)
|
1467
|
-
return True
|
1468
1471
|
except wandb.errors.CommError:
|
1469
1472
|
return False
|
1470
1473
|
|
1474
|
+
return True
|
1475
|
+
|
1471
1476
|
@normalize_exceptions
|
1472
1477
|
def artifact_collection_exists(self, name: str, type: str) -> bool:
|
1473
1478
|
"""Return whether an artifact collection exists within a specified project and entity.
|
@@ -1483,10 +1488,11 @@ class Api:
|
|
1483
1488
|
"""
|
1484
1489
|
try:
|
1485
1490
|
self.artifact_collection(type, name)
|
1486
|
-
return True
|
1487
1491
|
except wandb.errors.CommError:
|
1488
1492
|
return False
|
1489
1493
|
|
1494
|
+
return True
|
1495
|
+
|
1490
1496
|
def registries(
|
1491
1497
|
self,
|
1492
1498
|
organization: Optional[str] = None,
|
@@ -1539,9 +1545,7 @@ class Api:
|
|
1539
1545
|
Returns:
|
1540
1546
|
A registry iterator.
|
1541
1547
|
"""
|
1542
|
-
if not InternalApi().
|
1543
|
-
ServerFeature.ARTIFACT_REGISTRY_SEARCH
|
1544
|
-
):
|
1548
|
+
if not InternalApi()._server_supports(ServerFeature.ARTIFACT_REGISTRY_SEARCH):
|
1545
1549
|
raise RuntimeError(
|
1546
1550
|
"Registry search API is not enabled on this wandb server version. "
|
1547
1551
|
"Please upgrade your server version or contact support at support@wandb.com."
|
@@ -1577,9 +1581,7 @@ class Api:
|
|
1577
1581
|
registry.save()
|
1578
1582
|
```
|
1579
1583
|
"""
|
1580
|
-
if not InternalApi().
|
1581
|
-
ServerFeature.ARTIFACT_REGISTRY_SEARCH
|
1582
|
-
):
|
1584
|
+
if not InternalApi()._server_supports(ServerFeature.ARTIFACT_REGISTRY_SEARCH):
|
1583
1585
|
raise RuntimeError(
|
1584
1586
|
"api.registry() is not enabled on this wandb server version. "
|
1585
1587
|
"Please upgrade your server version or contact support at support@wandb.com."
|
@@ -1635,7 +1637,7 @@ class Api:
|
|
1635
1637
|
)
|
1636
1638
|
```
|
1637
1639
|
"""
|
1638
|
-
if not InternalApi().
|
1640
|
+
if not InternalApi()._server_supports(
|
1639
1641
|
ServerFeature.INCLUDE_ARTIFACT_TYPES_IN_REGISTRY_CREATION
|
1640
1642
|
):
|
1641
1643
|
raise RuntimeError(
|
@@ -1783,19 +1785,18 @@ class Api:
|
|
1783
1785
|
ALWAYS_SUPPORTED_EVENTS,
|
1784
1786
|
)
|
1785
1787
|
|
1786
|
-
|
1787
|
-
|
1788
|
-
(
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
or server_features.get(f"AUTOMATION_ACTION_{action.value}")
|
1797
|
-
)
|
1788
|
+
api = InternalApi()
|
1789
|
+
supports_event = (
|
1790
|
+
(event is None)
|
1791
|
+
or (event in ALWAYS_SUPPORTED_EVENTS)
|
1792
|
+
or api._server_supports(f"AUTOMATION_EVENT_{event.value}")
|
1793
|
+
)
|
1794
|
+
supports_action = (
|
1795
|
+
(action is None)
|
1796
|
+
or (action in ALWAYS_SUPPORTED_ACTIONS)
|
1797
|
+
or api._server_supports(f"AUTOMATION_ACTION_{action.value}")
|
1798
1798
|
)
|
1799
|
+
return supports_event and supports_action
|
1799
1800
|
|
1800
1801
|
def _omitted_automation_fragments(self) -> Set[str]:
|
1801
1802
|
"""Returns the names of unsupported automation-related fragments.
|
@@ -1938,6 +1939,7 @@ class Api:
|
|
1938
1939
|
iterator = filter(lambda x: x.name == name, iterator)
|
1939
1940
|
yield from iterator
|
1940
1941
|
|
1942
|
+
@normalize_exceptions
|
1941
1943
|
def create_automation(
|
1942
1944
|
self,
|
1943
1945
|
obj: "NewAutomation",
|
@@ -2044,6 +2046,7 @@ class Api:
|
|
2044
2046
|
|
2045
2047
|
return Automation.model_validate(result.trigger)
|
2046
2048
|
|
2049
|
+
@normalize_exceptions
|
2047
2050
|
def update_automation(
|
2048
2051
|
self,
|
2049
2052
|
obj: "Automation",
|
@@ -2151,7 +2154,7 @@ class Api:
|
|
2151
2154
|
|
2152
2155
|
# Not a (known) recoverable HTTP error
|
2153
2156
|
wandb.termerror(f"Got response status {status!r}: {e.response.text!r}")
|
2154
|
-
raise
|
2157
|
+
raise
|
2155
2158
|
|
2156
2159
|
try:
|
2157
2160
|
result = UpdateAutomation.model_validate(data).result
|
@@ -2165,6 +2168,7 @@ class Api:
|
|
2165
2168
|
|
2166
2169
|
return Automation.model_validate(result.trigger)
|
2167
2170
|
|
2171
|
+
@normalize_exceptions
|
2168
2172
|
def delete_automation(self, obj: Union["Automation", str]) -> Literal[True]:
|
2169
2173
|
"""Delete an automation.
|
2170
2174
|
|