wandb 0.21.4__py3-none-macosx_12_0_arm64.whl → 0.22.1__py3-none-macosx_12_0_arm64.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 +3 -3
- wandb/_pydantic/__init__.py +12 -11
- wandb/_pydantic/base.py +49 -19
- wandb/apis/__init__.py +2 -0
- wandb/apis/attrs.py +2 -0
- wandb/apis/importers/internals/internal.py +16 -23
- wandb/apis/internal.py +2 -0
- wandb/apis/normalize.py +2 -0
- wandb/apis/public/__init__.py +44 -1
- wandb/apis/public/api.py +215 -164
- wandb/apis/public/artifacts.py +23 -20
- wandb/apis/public/const.py +2 -0
- wandb/apis/public/files.py +33 -24
- wandb/apis/public/history.py +2 -0
- wandb/apis/public/jobs.py +20 -18
- wandb/apis/public/projects.py +4 -2
- wandb/apis/public/query_generator.py +3 -0
- wandb/apis/public/registries/__init__.py +7 -0
- wandb/apis/public/registries/_freezable_list.py +9 -12
- wandb/apis/public/registries/registries_search.py +8 -6
- wandb/apis/public/registries/registry.py +22 -17
- wandb/apis/public/reports.py +2 -0
- wandb/apis/public/runs.py +282 -60
- wandb/apis/public/sweeps.py +10 -9
- wandb/apis/public/teams.py +2 -0
- wandb/apis/public/users.py +2 -0
- wandb/apis/public/utils.py +16 -15
- wandb/automations/_generated/__init__.py +54 -127
- wandb/automations/_generated/create_generic_webhook_integration.py +1 -7
- wandb/automations/_generated/fragments.py +26 -91
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/beta_sync.py +9 -11
- wandb/errors/errors.py +3 -3
- wandb/proto/v3/wandb_internal_pb2.py +234 -224
- wandb/proto/v3/wandb_sync_pb2.py +19 -6
- wandb/proto/v4/wandb_internal_pb2.py +226 -224
- wandb/proto/v4/wandb_sync_pb2.py +10 -6
- wandb/proto/v5/wandb_internal_pb2.py +226 -224
- wandb/proto/v5/wandb_sync_pb2.py +10 -6
- wandb/proto/v6/wandb_base_pb2.py +3 -3
- wandb/proto/v6/wandb_internal_pb2.py +229 -227
- wandb/proto/v6/wandb_server_pb2.py +3 -3
- wandb/proto/v6/wandb_settings_pb2.py +3 -3
- wandb/proto/v6/wandb_sync_pb2.py +13 -9
- wandb/proto/v6/wandb_telemetry_pb2.py +3 -3
- wandb/sdk/artifacts/_factories.py +7 -2
- wandb/sdk/artifacts/_generated/__init__.py +112 -412
- wandb/sdk/artifacts/_generated/fragments.py +65 -0
- wandb/sdk/artifacts/_generated/operations.py +52 -22
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +3 -23
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +3 -23
- wandb/sdk/artifacts/_generated/type_info.py +19 -0
- wandb/sdk/artifacts/_gqlutils.py +47 -0
- wandb/sdk/artifacts/_models/__init__.py +4 -0
- wandb/sdk/artifacts/_models/base_model.py +20 -0
- wandb/sdk/artifacts/_validators.py +40 -12
- wandb/sdk/artifacts/artifact.py +69 -88
- wandb/sdk/artifacts/artifact_file_cache.py +6 -1
- wandb/sdk/artifacts/artifact_manifest_entry.py +61 -2
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/http_handler.py +1 -3
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +1 -1
- wandb/sdk/artifacts/storage_policies/_factories.py +63 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +69 -124
- wandb/sdk/data_types/bokeh.py +5 -1
- wandb/sdk/data_types/image.py +17 -6
- wandb/sdk/interface/interface.py +41 -4
- wandb/sdk/interface/interface_queue.py +10 -0
- wandb/sdk/interface/interface_shared.py +9 -7
- wandb/sdk/interface/interface_sock.py +9 -3
- wandb/sdk/internal/_generated/__init__.py +2 -12
- wandb/sdk/internal/sender.py +1 -1
- wandb/sdk/internal/settings_static.py +2 -82
- wandb/sdk/launch/runner/kubernetes_runner.py +25 -20
- wandb/sdk/launch/utils.py +82 -1
- wandb/sdk/lib/progress.py +7 -4
- wandb/sdk/lib/service/service_client.py +5 -9
- wandb/sdk/lib/service/service_connection.py +39 -23
- wandb/sdk/mailbox/mailbox_handle.py +2 -0
- wandb/sdk/projects/_generated/__init__.py +12 -33
- wandb/sdk/wandb_init.py +31 -3
- wandb/sdk/wandb_login.py +53 -27
- wandb/sdk/wandb_run.py +5 -3
- wandb/sdk/wandb_settings.py +50 -13
- wandb/sync/sync.py +7 -2
- wandb/util.py +1 -1
- wandb/wandb_agent.py +35 -4
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/METADATA +1 -1
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/RECORD +95 -91
- wandb/sdk/artifacts/_graphql_fragments.py +0 -19
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/WHEEL +0 -0
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/entry_points.txt +0 -0
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/licenses/LICENSE +0 -0
wandb/apis/public/api.py
CHANGED
@@ -11,22 +11,14 @@ You might use the Public API to
|
|
11
11
|
For more on using the Public API, check out [our guide](https://docs.wandb.com/guides/track/public-api-guide).
|
12
12
|
"""
|
13
13
|
|
14
|
+
from __future__ import annotations
|
15
|
+
|
14
16
|
import json
|
15
17
|
import logging
|
16
18
|
import os
|
17
19
|
import urllib
|
18
20
|
from http import HTTPStatus
|
19
|
-
from typing import
|
20
|
-
TYPE_CHECKING,
|
21
|
-
Any,
|
22
|
-
Dict,
|
23
|
-
Iterator,
|
24
|
-
List,
|
25
|
-
Literal,
|
26
|
-
Optional,
|
27
|
-
Set,
|
28
|
-
Union,
|
29
|
-
)
|
21
|
+
from typing import TYPE_CHECKING, Any, Iterator, Literal
|
30
22
|
|
31
23
|
import requests
|
32
24
|
from pydantic import ValidationError
|
@@ -42,9 +34,8 @@ from wandb._strutils import nameof
|
|
42
34
|
from wandb.apis import public
|
43
35
|
from wandb.apis.normalize import normalize_exceptions
|
44
36
|
from wandb.apis.public.const import RETRY_TIMEDELTA
|
37
|
+
from wandb.apis.public.registries import Registries, Registry
|
45
38
|
from wandb.apis.public.registries._utils import fetch_org_entity_from_organization
|
46
|
-
from wandb.apis.public.registries.registries_search import Registries
|
47
|
-
from wandb.apis.public.registries.registry import Registry
|
48
39
|
from wandb.apis.public.utils import (
|
49
40
|
PathType,
|
50
41
|
fetch_org_from_settings_or_entity,
|
@@ -54,7 +45,11 @@ from wandb.apis.public.utils import (
|
|
54
45
|
from wandb.proto.wandb_deprecated import Deprecated
|
55
46
|
from wandb.proto.wandb_internal_pb2 import ServerFeature
|
56
47
|
from wandb.sdk import wandb_login
|
57
|
-
from wandb.sdk.artifacts._validators import
|
48
|
+
from wandb.sdk.artifacts._validators import (
|
49
|
+
ArtifactPath,
|
50
|
+
FullArtifactPath,
|
51
|
+
is_artifact_registry_project,
|
52
|
+
)
|
58
53
|
from wandb.sdk.internal.internal_api import Api as InternalApi
|
59
54
|
from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings
|
60
55
|
from wandb.sdk.launch.utils import LAUNCH_DEFAULT_PROJECT
|
@@ -73,6 +68,17 @@ if TYPE_CHECKING:
|
|
73
68
|
WebhookIntegration,
|
74
69
|
)
|
75
70
|
from wandb.automations._utils import WriteAutomationsKwargs
|
71
|
+
from wandb.sdk.artifacts.artifact import Artifact
|
72
|
+
|
73
|
+
from .artifacts import (
|
74
|
+
ArtifactCollection,
|
75
|
+
ArtifactCollections,
|
76
|
+
Artifacts,
|
77
|
+
ArtifactType,
|
78
|
+
ArtifactTypes,
|
79
|
+
)
|
80
|
+
from .teams import Team
|
81
|
+
from .users import User
|
76
82
|
|
77
83
|
logger = logging.getLogger(__name__)
|
78
84
|
|
@@ -277,9 +283,9 @@ class Api:
|
|
277
283
|
|
278
284
|
def __init__(
|
279
285
|
self,
|
280
|
-
overrides:
|
281
|
-
timeout:
|
282
|
-
api_key:
|
286
|
+
overrides: dict[str, Any] | None = None,
|
287
|
+
timeout: int | None = None,
|
288
|
+
api_key: str | None = None,
|
283
289
|
) -> None:
|
284
290
|
"""Initialize the API.
|
285
291
|
|
@@ -291,6 +297,8 @@ class Api:
|
|
291
297
|
specified, the default timeout will be used.
|
292
298
|
api_key: API key to use for authentication. If not provided,
|
293
299
|
the API key from the current environment or configuration will be used.
|
300
|
+
Prompts for an API key if none is provided
|
301
|
+
or configured in the environment.
|
294
302
|
"""
|
295
303
|
self.settings = InternalApi().settings()
|
296
304
|
|
@@ -305,18 +313,14 @@ class Api:
|
|
305
313
|
)
|
306
314
|
self.settings["entity"] = _overrides["username"]
|
307
315
|
|
308
|
-
self._api_key = api_key
|
309
316
|
if _thread_local_api_settings.cookies is None:
|
310
|
-
|
311
|
-
|
317
|
+
self.api_key = self._load_api_key(
|
318
|
+
base_url=self.settings["base_url"],
|
319
|
+
init_api_key=api_key,
|
320
|
+
)
|
321
|
+
wandb_login._verify_login(
|
312
322
|
key=self.api_key,
|
313
|
-
|
314
|
-
_silent=(
|
315
|
-
self.settings.get("silent", False)
|
316
|
-
or self.settings.get("quiet", False)
|
317
|
-
),
|
318
|
-
update_api_key=False,
|
319
|
-
_disable_warning=True,
|
323
|
+
base_url=self.settings["base_url"],
|
320
324
|
)
|
321
325
|
|
322
326
|
self._viewer = None
|
@@ -353,6 +357,45 @@ class Api:
|
|
353
357
|
self._sentry = wandb.analytics.sentry.Sentry()
|
354
358
|
self._configure_sentry()
|
355
359
|
|
360
|
+
def _load_api_key(
|
361
|
+
self,
|
362
|
+
base_url: str,
|
363
|
+
init_api_key: str | None = None,
|
364
|
+
) -> str:
|
365
|
+
"""Attempts to load a configured API key or prompt if one is not found.
|
366
|
+
|
367
|
+
The API key is loaded in the following order:
|
368
|
+
1. Thread local api key
|
369
|
+
2. User explicitly provided api key
|
370
|
+
3. Environment variable
|
371
|
+
4. Netrc file
|
372
|
+
5. Prompt for api key using wandb.login
|
373
|
+
"""
|
374
|
+
# just use thread local api key if it's set
|
375
|
+
if _thread_local_api_settings.api_key:
|
376
|
+
return _thread_local_api_settings.api_key
|
377
|
+
if init_api_key is not None:
|
378
|
+
return init_api_key
|
379
|
+
if os.getenv("WANDB_API_KEY"):
|
380
|
+
return os.environ["WANDB_API_KEY"]
|
381
|
+
|
382
|
+
auth = requests.utils.get_netrc_auth(base_url)
|
383
|
+
if auth:
|
384
|
+
return auth[-1]
|
385
|
+
|
386
|
+
_, prompted_key = wandb_login._login(
|
387
|
+
host=base_url,
|
388
|
+
key=None,
|
389
|
+
# We will explicitly verify the key later
|
390
|
+
verify=False,
|
391
|
+
_silent=(
|
392
|
+
self.settings.get("silent", False) or self.settings.get("quiet", False)
|
393
|
+
),
|
394
|
+
update_api_key=False,
|
395
|
+
_disable_warning=True,
|
396
|
+
)
|
397
|
+
return prompted_key
|
398
|
+
|
356
399
|
def _configure_sentry(self) -> None:
|
357
400
|
try:
|
358
401
|
viewer = self.viewer
|
@@ -382,10 +425,10 @@ class Api:
|
|
382
425
|
def create_run(
|
383
426
|
self,
|
384
427
|
*,
|
385
|
-
run_id:
|
386
|
-
project:
|
387
|
-
entity:
|
388
|
-
) ->
|
428
|
+
run_id: str | None = None,
|
429
|
+
project: str | None = None,
|
430
|
+
entity: str | None = None,
|
431
|
+
) -> public.Run:
|
389
432
|
"""Create a new run.
|
390
433
|
|
391
434
|
Args:
|
@@ -406,12 +449,12 @@ class Api:
|
|
406
449
|
def create_run_queue(
|
407
450
|
self,
|
408
451
|
name: str,
|
409
|
-
type:
|
410
|
-
entity:
|
411
|
-
prioritization_mode:
|
412
|
-
config:
|
413
|
-
template_variables:
|
414
|
-
) ->
|
452
|
+
type: public.RunQueueResourceType,
|
453
|
+
entity: str | None = None,
|
454
|
+
prioritization_mode: public.RunQueuePrioritizationMode | None = None,
|
455
|
+
config: dict | None = None,
|
456
|
+
template_variables: dict | None = None,
|
457
|
+
) -> public.RunQueue:
|
415
458
|
"""Create a new run queue in W&B Launch.
|
416
459
|
|
417
460
|
Args:
|
@@ -517,7 +560,7 @@ class Api:
|
|
517
560
|
display_name: str,
|
518
561
|
spec_type: Literal["vega2"],
|
519
562
|
access: Literal["private", "public"],
|
520
|
-
spec:
|
563
|
+
spec: str | dict,
|
521
564
|
) -> str:
|
522
565
|
"""Create a custom chart preset and return its id.
|
523
566
|
|
@@ -593,11 +636,11 @@ class Api:
|
|
593
636
|
self,
|
594
637
|
name: str,
|
595
638
|
resource_config: dict,
|
596
|
-
resource_type:
|
597
|
-
entity:
|
598
|
-
template_variables:
|
599
|
-
external_links:
|
600
|
-
prioritization_mode:
|
639
|
+
resource_type: public.RunQueueResourceType,
|
640
|
+
entity: str | None = None,
|
641
|
+
template_variables: dict | None = None,
|
642
|
+
external_links: dict | None = None,
|
643
|
+
prioritization_mode: public.RunQueuePrioritizationMode | None = None,
|
601
644
|
):
|
602
645
|
"""Upsert a run queue in W&B Launch.
|
603
646
|
|
@@ -698,7 +741,7 @@ class Api:
|
|
698
741
|
entity=entity,
|
699
742
|
)
|
700
743
|
|
701
|
-
def create_user(self, email: str, admin:
|
744
|
+
def create_user(self, email: str, admin: bool | None = False) -> User:
|
702
745
|
"""Create a new user.
|
703
746
|
|
704
747
|
Args:
|
@@ -708,7 +751,9 @@ class Api:
|
|
708
751
|
Returns:
|
709
752
|
A `User` object.
|
710
753
|
"""
|
711
|
-
|
754
|
+
from .users import User
|
755
|
+
|
756
|
+
return User.create(self, email, admin)
|
712
757
|
|
713
758
|
def sync_tensorboard(self, root_dir, run_id=None, project=None, entity=None):
|
714
759
|
"""Sync a local directory containing tfevent files to wandb."""
|
@@ -745,25 +790,7 @@ class Api:
|
|
745
790
|
return "W&B Public Client {}".format(wandb.__version__)
|
746
791
|
|
747
792
|
@property
|
748
|
-
def
|
749
|
-
"""Returns W&B API key."""
|
750
|
-
# just use thread local api key if it's set
|
751
|
-
if _thread_local_api_settings.api_key:
|
752
|
-
return _thread_local_api_settings.api_key
|
753
|
-
if self._api_key is not None:
|
754
|
-
return self._api_key
|
755
|
-
auth = requests.utils.get_netrc_auth(self.settings["base_url"])
|
756
|
-
key = None
|
757
|
-
if auth:
|
758
|
-
key = auth[-1]
|
759
|
-
# Environment should take precedence
|
760
|
-
if os.getenv("WANDB_API_KEY"):
|
761
|
-
key = os.environ["WANDB_API_KEY"]
|
762
|
-
self._api_key = key # memoize key
|
763
|
-
return key
|
764
|
-
|
765
|
-
@property
|
766
|
-
def default_entity(self) -> Optional[str]:
|
793
|
+
def default_entity(self) -> str | None:
|
767
794
|
"""Returns the default W&B entity."""
|
768
795
|
if self._default_entity is None:
|
769
796
|
res = self._client.execute(self.DEFAULT_ENTITY_QUERY)
|
@@ -771,13 +798,15 @@ class Api:
|
|
771
798
|
return self._default_entity
|
772
799
|
|
773
800
|
@property
|
774
|
-
def viewer(self) ->
|
801
|
+
def viewer(self) -> User:
|
775
802
|
"""Returns the viewer object.
|
776
803
|
|
777
804
|
Raises:
|
778
805
|
ValueError: If viewer data is not able to be fetched from W&B.
|
779
806
|
requests.RequestException: If an error occurs while making the graphql request.
|
780
807
|
"""
|
808
|
+
from .users import User
|
809
|
+
|
781
810
|
if self._viewer is None:
|
782
811
|
viewer = self._client.execute(self.VIEWER_QUERY).get("viewer")
|
783
812
|
|
@@ -787,7 +816,7 @@ class Api:
|
|
787
816
|
" please verify your API key is valid."
|
788
817
|
)
|
789
818
|
|
790
|
-
self._viewer =
|
819
|
+
self._viewer = User(self._client, viewer)
|
791
820
|
self._default_entity = self._viewer.entity
|
792
821
|
return self._viewer
|
793
822
|
|
@@ -916,22 +945,13 @@ class Api:
|
|
916
945
|
if path is None:
|
917
946
|
return entity, project
|
918
947
|
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
parts = path.split("/")
|
923
|
-
if len(parts) > 3:
|
924
|
-
raise ValueError("Invalid artifact path: {}".format(path))
|
925
|
-
elif len(parts) == 1:
|
926
|
-
return entity, project, path + full_alias
|
927
|
-
elif len(parts) == 2:
|
928
|
-
return entity, parts[0], parts[1] + full_alias
|
929
|
-
parts[-1] += full_alias
|
930
|
-
return parts
|
948
|
+
parsed = ArtifactPath.from_str(path)
|
949
|
+
parsed = parsed.with_defaults(prefix=entity, project=project)
|
950
|
+
return parsed.prefix, parsed.project, parsed.name
|
931
951
|
|
932
952
|
def projects(
|
933
|
-
self, entity:
|
934
|
-
) ->
|
953
|
+
self, entity: str | None = None, per_page: int = 200
|
954
|
+
) -> public.Projects:
|
935
955
|
"""Get projects for a given entity.
|
936
956
|
|
937
957
|
Args:
|
@@ -956,7 +976,7 @@ class Api:
|
|
956
976
|
)
|
957
977
|
return self._projects[entity]
|
958
978
|
|
959
|
-
def project(self, name: str, entity:
|
979
|
+
def project(self, name: str, entity: str | None = None) -> public.Project:
|
960
980
|
"""Return the `Project` with the given name (and entity, if given).
|
961
981
|
|
962
982
|
Args:
|
@@ -983,8 +1003,8 @@ class Api:
|
|
983
1003
|
return public.Project(self.client, entity, name, {})
|
984
1004
|
|
985
1005
|
def reports(
|
986
|
-
self, path: str = "", name:
|
987
|
-
) ->
|
1006
|
+
self, path: str = "", name: str | None = None, per_page: int = 50
|
1007
|
+
) -> public.Reports:
|
988
1008
|
"""Get reports for a given project path.
|
989
1009
|
|
990
1010
|
Note: `wandb.Api.reports()` API is in beta and will likely change in
|
@@ -1027,9 +1047,7 @@ class Api:
|
|
1027
1047
|
)
|
1028
1048
|
return self._reports[key]
|
1029
1049
|
|
1030
|
-
def create_team(
|
1031
|
-
self, team: str, admin_username: Optional[str] = None
|
1032
|
-
) -> "public.Team":
|
1050
|
+
def create_team(self, team: str, admin_username: str | None = None) -> Team:
|
1033
1051
|
"""Create a new team.
|
1034
1052
|
|
1035
1053
|
Args:
|
@@ -1040,9 +1058,11 @@ class Api:
|
|
1040
1058
|
Returns:
|
1041
1059
|
A `Team` object.
|
1042
1060
|
"""
|
1043
|
-
|
1061
|
+
from .teams import Team
|
1062
|
+
|
1063
|
+
return Team.create(self, team, admin_username)
|
1044
1064
|
|
1045
|
-
def team(self, team: str) ->
|
1065
|
+
def team(self, team: str) -> Team:
|
1046
1066
|
"""Return the matching `Team` with the given name.
|
1047
1067
|
|
1048
1068
|
Args:
|
@@ -1051,9 +1071,11 @@ class Api:
|
|
1051
1071
|
Returns:
|
1052
1072
|
A `Team` object.
|
1053
1073
|
"""
|
1054
|
-
|
1074
|
+
from .teams import Team
|
1055
1075
|
|
1056
|
-
|
1076
|
+
return Team(self.client, team)
|
1077
|
+
|
1078
|
+
def user(self, username_or_email: str) -> User | None:
|
1057
1079
|
"""Return a user from a username or email address.
|
1058
1080
|
|
1059
1081
|
This function only works for local administrators. Use `api.viewer`
|
@@ -1065,6 +1087,8 @@ class Api:
|
|
1065
1087
|
Returns:
|
1066
1088
|
A `User` object or None if a user is not found.
|
1067
1089
|
"""
|
1090
|
+
from .users import User
|
1091
|
+
|
1068
1092
|
res = self._client.execute(self.USERS_QUERY, {"query": username_or_email})
|
1069
1093
|
if len(res["users"]["edges"]) == 0:
|
1070
1094
|
return None
|
@@ -1074,9 +1098,9 @@ class Api:
|
|
1074
1098
|
username_or_email
|
1075
1099
|
)
|
1076
1100
|
)
|
1077
|
-
return
|
1101
|
+
return User(self._client, res["users"]["edges"][0]["node"])
|
1078
1102
|
|
1079
|
-
def users(self, username_or_email: str) ->
|
1103
|
+
def users(self, username_or_email: str) -> list[User]:
|
1080
1104
|
"""Return all users from a partial username or email address query.
|
1081
1105
|
|
1082
1106
|
This function only works for local administrators. Use `api.viewer`
|
@@ -1088,18 +1112,19 @@ class Api:
|
|
1088
1112
|
Returns:
|
1089
1113
|
An array of `User` objects.
|
1090
1114
|
"""
|
1115
|
+
from .users import User
|
1116
|
+
|
1091
1117
|
res = self._client.execute(self.USERS_QUERY, {"query": username_or_email})
|
1092
|
-
return [
|
1093
|
-
public.User(self._client, edge["node"]) for edge in res["users"]["edges"]
|
1094
|
-
]
|
1118
|
+
return [User(self._client, edge["node"]) for edge in res["users"]["edges"]]
|
1095
1119
|
|
1096
1120
|
def runs(
|
1097
1121
|
self,
|
1098
|
-
path:
|
1099
|
-
filters:
|
1122
|
+
path: str | None = None,
|
1123
|
+
filters: dict[str, Any] | None = None,
|
1100
1124
|
order: str = "+created_at",
|
1101
1125
|
per_page: int = 50,
|
1102
1126
|
include_sweeps: bool = True,
|
1127
|
+
lazy: bool = True,
|
1103
1128
|
):
|
1104
1129
|
"""Returns a `Runs` object, which lazily iterates over `Run` objects.
|
1105
1130
|
|
@@ -1149,6 +1174,10 @@ class Api:
|
|
1149
1174
|
The default order is run.created_at from oldest to newest.
|
1150
1175
|
per_page: (int) Sets the page size for query pagination.
|
1151
1176
|
include_sweeps: (bool) Whether to include the sweep runs in the results.
|
1177
|
+
lazy: (bool) Whether to use lazy loading for faster performance.
|
1178
|
+
When True (default), only essential run metadata is loaded initially.
|
1179
|
+
Heavy fields like config, summaryMetrics, and systemMetrics are loaded
|
1180
|
+
on-demand when accessed. Set to False for full data upfront.
|
1152
1181
|
|
1153
1182
|
Returns:
|
1154
1183
|
A `Runs` object, which is an iterable collection of `Run` objects.
|
@@ -1197,16 +1226,26 @@ class Api:
|
|
1197
1226
|
entity, project = self._parse_project_path(path)
|
1198
1227
|
filters = filters or {}
|
1199
1228
|
key = (path or "") + str(filters) + str(order)
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1229
|
+
|
1230
|
+
# Check if we have cached results
|
1231
|
+
if self._runs.get(key):
|
1232
|
+
cached_runs = self._runs[key]
|
1233
|
+
# If requesting full data but cached data is lazy, upgrade it
|
1234
|
+
if not lazy and cached_runs._lazy:
|
1235
|
+
cached_runs.upgrade_to_full()
|
1236
|
+
return cached_runs
|
1237
|
+
|
1238
|
+
# Create new Runs object
|
1239
|
+
self._runs[key] = public.Runs(
|
1240
|
+
self.client,
|
1241
|
+
entity,
|
1242
|
+
project,
|
1243
|
+
filters=filters,
|
1244
|
+
order=order,
|
1245
|
+
per_page=per_page,
|
1246
|
+
include_sweeps=include_sweeps,
|
1247
|
+
lazy=lazy,
|
1248
|
+
)
|
1210
1249
|
return self._runs[key]
|
1211
1250
|
|
1212
1251
|
@normalize_exceptions
|
@@ -1223,7 +1262,10 @@ class Api:
|
|
1223
1262
|
"""
|
1224
1263
|
entity, project, run_id = self._parse_path(path)
|
1225
1264
|
if not self._runs.get(path):
|
1226
|
-
|
1265
|
+
# Individual runs should load full data by default
|
1266
|
+
self._runs[path] = public.Run(
|
1267
|
+
self.client, entity, project, run_id, lazy=False
|
1268
|
+
)
|
1227
1269
|
return self._runs[path]
|
1228
1270
|
|
1229
1271
|
def queued_run(
|
@@ -1283,7 +1325,7 @@ class Api:
|
|
1283
1325
|
return self._sweeps[path]
|
1284
1326
|
|
1285
1327
|
@normalize_exceptions
|
1286
|
-
def artifact_types(self, project:
|
1328
|
+
def artifact_types(self, project: str | None = None) -> ArtifactTypes:
|
1287
1329
|
"""Returns a collection of matching artifact types.
|
1288
1330
|
|
1289
1331
|
Args:
|
@@ -1292,6 +1334,8 @@ class Api:
|
|
1292
1334
|
Returns:
|
1293
1335
|
An iterable `ArtifactTypes` object.
|
1294
1336
|
"""
|
1337
|
+
from .artifacts import ArtifactTypes
|
1338
|
+
|
1295
1339
|
project_path = project
|
1296
1340
|
entity, project = self._parse_project_path(project_path)
|
1297
1341
|
# If its a Registry project, the entity is considered to be an org instead
|
@@ -1301,12 +1345,10 @@ class Api:
|
|
1301
1345
|
entity = InternalApi()._resolve_org_entity_name(
|
1302
1346
|
entity=settings_entity, organization=org
|
1303
1347
|
)
|
1304
|
-
return
|
1348
|
+
return ArtifactTypes(self.client, entity, project)
|
1305
1349
|
|
1306
1350
|
@normalize_exceptions
|
1307
|
-
def artifact_type(
|
1308
|
-
self, type_name: str, project: Optional[str] = None
|
1309
|
-
) -> "public.ArtifactType":
|
1351
|
+
def artifact_type(self, type_name: str, project: str | None = None) -> ArtifactType:
|
1310
1352
|
"""Returns the matching `ArtifactType`.
|
1311
1353
|
|
1312
1354
|
Args:
|
@@ -1316,6 +1358,8 @@ class Api:
|
|
1316
1358
|
Returns:
|
1317
1359
|
An `ArtifactType` object.
|
1318
1360
|
"""
|
1361
|
+
from .artifacts import ArtifactType
|
1362
|
+
|
1319
1363
|
project_path = project
|
1320
1364
|
entity, project = self._parse_project_path(project_path)
|
1321
1365
|
# If its an Registry artifact, the entity is an org instead
|
@@ -1325,12 +1369,12 @@ class Api:
|
|
1325
1369
|
entity = InternalApi()._resolve_org_entity_name(
|
1326
1370
|
entity=settings_entity, organization=org
|
1327
1371
|
)
|
1328
|
-
return
|
1372
|
+
return ArtifactType(self.client, entity, project, type_name)
|
1329
1373
|
|
1330
1374
|
@normalize_exceptions
|
1331
1375
|
def artifact_collections(
|
1332
1376
|
self, project_name: str, type_name: str, per_page: int = 50
|
1333
|
-
) ->
|
1377
|
+
) -> ArtifactCollections:
|
1334
1378
|
"""Returns a collection of matching artifact collections.
|
1335
1379
|
|
1336
1380
|
Args:
|
@@ -1342,6 +1386,8 @@ class Api:
|
|
1342
1386
|
Returns:
|
1343
1387
|
An iterable `ArtifactCollections` object.
|
1344
1388
|
"""
|
1389
|
+
from .artifacts import ArtifactCollections
|
1390
|
+
|
1345
1391
|
entity, project = self._parse_project_path(project_name)
|
1346
1392
|
# If iterating through Registry project, the entity is considered to be an org instead
|
1347
1393
|
if is_artifact_registry_project(project):
|
@@ -1350,14 +1396,12 @@ class Api:
|
|
1350
1396
|
entity = InternalApi()._resolve_org_entity_name(
|
1351
1397
|
entity=settings_entity, organization=org
|
1352
1398
|
)
|
1353
|
-
return
|
1399
|
+
return ArtifactCollections(
|
1354
1400
|
self.client, entity, project, type_name, per_page=per_page
|
1355
1401
|
)
|
1356
1402
|
|
1357
1403
|
@normalize_exceptions
|
1358
|
-
def artifact_collection(
|
1359
|
-
self, type_name: str, name: str
|
1360
|
-
) -> "public.ArtifactCollection":
|
1404
|
+
def artifact_collection(self, type_name: str, name: str) -> ArtifactCollection:
|
1361
1405
|
"""Returns a single artifact collection by type.
|
1362
1406
|
|
1363
1407
|
You can use the returned `ArtifactCollection` object to retrieve
|
@@ -1392,6 +1436,8 @@ class Api:
|
|
1392
1436
|
artifact_example.download()
|
1393
1437
|
```
|
1394
1438
|
"""
|
1439
|
+
from .artifacts import ArtifactCollection
|
1440
|
+
|
1395
1441
|
entity, project, collection_name = self._parse_artifact_path(name)
|
1396
1442
|
# If its an Registry artifact, the entity is considered to be an org instead
|
1397
1443
|
if is_artifact_registry_project(project):
|
@@ -1406,7 +1452,7 @@ class Api:
|
|
1406
1452
|
"Could not determine entity. Please include the entity as part of the collection name path."
|
1407
1453
|
)
|
1408
1454
|
|
1409
|
-
return
|
1455
|
+
return ArtifactCollection(
|
1410
1456
|
self.client, entity, project, collection_name, type_name
|
1411
1457
|
)
|
1412
1458
|
|
@@ -1428,8 +1474,8 @@ class Api:
|
|
1428
1474
|
type_name: str,
|
1429
1475
|
name: str,
|
1430
1476
|
per_page: int = 50,
|
1431
|
-
tags:
|
1432
|
-
) ->
|
1477
|
+
tags: list[str] | None = None,
|
1478
|
+
) -> Artifacts:
|
1433
1479
|
"""Return an `Artifacts` collection.
|
1434
1480
|
|
1435
1481
|
Args:
|
@@ -1457,6 +1503,8 @@ class Api:
|
|
1457
1503
|
wandb.Api().artifacts(type_name="type", name="entity/project/artifact_name")
|
1458
1504
|
```
|
1459
1505
|
"""
|
1506
|
+
from .artifacts import Artifacts
|
1507
|
+
|
1460
1508
|
entity, project, collection_name = self._parse_artifact_path(name)
|
1461
1509
|
# If its an Registry project, the entity is considered to be an org instead
|
1462
1510
|
if is_artifact_registry_project(project):
|
@@ -1465,7 +1513,7 @@ class Api:
|
|
1465
1513
|
entity = InternalApi()._resolve_org_entity_name(
|
1466
1514
|
entity=settings_entity, organization=org
|
1467
1515
|
)
|
1468
|
-
return
|
1516
|
+
return Artifacts(
|
1469
1517
|
self.client,
|
1470
1518
|
entity,
|
1471
1519
|
project,
|
@@ -1477,8 +1525,10 @@ class Api:
|
|
1477
1525
|
|
1478
1526
|
@normalize_exceptions
|
1479
1527
|
def _artifact(
|
1480
|
-
self, name: str, type:
|
1481
|
-
):
|
1528
|
+
self, name: str, type: str | None = None, enable_tracking: bool = False
|
1529
|
+
) -> Artifact:
|
1530
|
+
from wandb.sdk.artifacts.artifact import Artifact
|
1531
|
+
|
1482
1532
|
if name is None:
|
1483
1533
|
raise ValueError("You must specify name= to fetch an artifact.")
|
1484
1534
|
entity, project, artifact_name = self._parse_artifact_path(name)
|
@@ -1503,10 +1553,9 @@ class Api:
|
|
1503
1553
|
"Could not determine entity. Please include the entity as part of the artifact name path."
|
1504
1554
|
)
|
1505
1555
|
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
name=artifact_name,
|
1556
|
+
path = FullArtifactPath(prefix=entity, project=project, name=artifact_name)
|
1557
|
+
artifact = Artifact._from_name(
|
1558
|
+
path=path,
|
1510
1559
|
client=self.client,
|
1511
1560
|
enable_tracking=enable_tracking,
|
1512
1561
|
)
|
@@ -1517,7 +1566,7 @@ class Api:
|
|
1517
1566
|
return artifact
|
1518
1567
|
|
1519
1568
|
@normalize_exceptions
|
1520
|
-
def artifact(self, name: str, type:
|
1569
|
+
def artifact(self, name: str, type: str | None = None):
|
1521
1570
|
"""Returns a single artifact.
|
1522
1571
|
|
1523
1572
|
Args:
|
@@ -1566,7 +1615,7 @@ class Api:
|
|
1566
1615
|
return self._artifact(name=name, type=type, enable_tracking=True)
|
1567
1616
|
|
1568
1617
|
@normalize_exceptions
|
1569
|
-
def job(self, name:
|
1618
|
+
def job(self, name: str | None, path: str | None = None) -> public.Job:
|
1570
1619
|
"""Return a `Job` object.
|
1571
1620
|
|
1572
1621
|
Args:
|
@@ -1585,7 +1634,7 @@ class Api:
|
|
1585
1634
|
return public.Job(self, name, path)
|
1586
1635
|
|
1587
1636
|
@normalize_exceptions
|
1588
|
-
def list_jobs(self, entity: str, project: str) ->
|
1637
|
+
def list_jobs(self, entity: str, project: str) -> list[dict[str, Any]]:
|
1589
1638
|
"""Return a list of jobs, if any, for the given entity and project.
|
1590
1639
|
|
1591
1640
|
Args:
|
@@ -1663,7 +1712,7 @@ class Api:
|
|
1663
1712
|
return False
|
1664
1713
|
|
1665
1714
|
@normalize_exceptions
|
1666
|
-
def artifact_exists(self, name: str, type:
|
1715
|
+
def artifact_exists(self, name: str, type: str | None = None) -> bool:
|
1667
1716
|
"""Whether an artifact version exists within the specified project and entity.
|
1668
1717
|
|
1669
1718
|
Args:
|
@@ -1694,9 +1743,10 @@ class Api:
|
|
1694
1743
|
"""
|
1695
1744
|
try:
|
1696
1745
|
self._artifact(name, type)
|
1697
|
-
except wandb.errors.CommError:
|
1746
|
+
except wandb.errors.CommError as e:
|
1747
|
+
if isinstance(e.exc, requests.Timeout):
|
1748
|
+
raise
|
1698
1749
|
return False
|
1699
|
-
|
1700
1750
|
return True
|
1701
1751
|
|
1702
1752
|
@normalize_exceptions
|
@@ -1727,16 +1777,17 @@ class Api:
|
|
1727
1777
|
"""
|
1728
1778
|
try:
|
1729
1779
|
self.artifact_collection(type, name)
|
1730
|
-
except wandb.errors.CommError:
|
1780
|
+
except wandb.errors.CommError as e:
|
1781
|
+
if isinstance(e.exc, requests.Timeout):
|
1782
|
+
raise
|
1731
1783
|
return False
|
1732
|
-
|
1733
1784
|
return True
|
1734
1785
|
|
1735
1786
|
@tracked
|
1736
1787
|
def registries(
|
1737
1788
|
self,
|
1738
|
-
organization:
|
1739
|
-
filter:
|
1789
|
+
organization: str | None = None,
|
1790
|
+
filter: dict[str, Any] | None = None,
|
1740
1791
|
) -> Registries:
|
1741
1792
|
"""Returns a lazy iterator of `Registry` objects.
|
1742
1793
|
|
@@ -1801,7 +1852,7 @@ class Api:
|
|
1801
1852
|
return Registries(self.client, organization, filter)
|
1802
1853
|
|
1803
1854
|
@tracked
|
1804
|
-
def registry(self, name: str, organization:
|
1855
|
+
def registry(self, name: str, organization: str | None = None) -> Registry:
|
1805
1856
|
"""Return a registry given a registry name.
|
1806
1857
|
|
1807
1858
|
Args:
|
@@ -1845,9 +1896,9 @@ class Api:
|
|
1845
1896
|
self,
|
1846
1897
|
name: str,
|
1847
1898
|
visibility: Literal["organization", "restricted"],
|
1848
|
-
organization:
|
1849
|
-
description:
|
1850
|
-
artifact_types:
|
1899
|
+
organization: str | None = None,
|
1900
|
+
description: str | None = None,
|
1901
|
+
artifact_types: list[str] | None = None,
|
1851
1902
|
) -> Registry:
|
1852
1903
|
"""Create a new registry.
|
1853
1904
|
|
@@ -1918,10 +1969,10 @@ class Api:
|
|
1918
1969
|
@tracked
|
1919
1970
|
def integrations(
|
1920
1971
|
self,
|
1921
|
-
entity:
|
1972
|
+
entity: str | None = None,
|
1922
1973
|
*,
|
1923
1974
|
per_page: int = 50,
|
1924
|
-
) -> Iterator[
|
1975
|
+
) -> Iterator[Integration]:
|
1925
1976
|
"""Return an iterator of all integrations for an entity.
|
1926
1977
|
|
1927
1978
|
Args:
|
@@ -1941,8 +1992,8 @@ class Api:
|
|
1941
1992
|
|
1942
1993
|
@tracked
|
1943
1994
|
def webhook_integrations(
|
1944
|
-
self, entity:
|
1945
|
-
) -> Iterator[
|
1995
|
+
self, entity: str | None = None, *, per_page: int = 50
|
1996
|
+
) -> Iterator[WebhookIntegration]:
|
1946
1997
|
"""Returns an iterator of webhook integrations for an entity.
|
1947
1998
|
|
1948
1999
|
Args:
|
@@ -1985,8 +2036,8 @@ class Api:
|
|
1985
2036
|
|
1986
2037
|
@tracked
|
1987
2038
|
def slack_integrations(
|
1988
|
-
self, *, entity:
|
1989
|
-
) -> Iterator[
|
2039
|
+
self, *, entity: str | None = None, per_page: int = 50
|
2040
|
+
) -> Iterator[SlackIntegration]:
|
1990
2041
|
"""Returns an iterator of Slack integrations for an entity.
|
1991
2042
|
|
1992
2043
|
Args:
|
@@ -2030,8 +2081,8 @@ class Api:
|
|
2030
2081
|
def _supports_automation(
|
2031
2082
|
self,
|
2032
2083
|
*,
|
2033
|
-
event:
|
2034
|
-
action:
|
2084
|
+
event: EventType | None = None,
|
2085
|
+
action: ActionType | None = None,
|
2035
2086
|
) -> bool:
|
2036
2087
|
"""Returns whether the server recognizes the automation event and/or action."""
|
2037
2088
|
from wandb.automations._utils import (
|
@@ -2052,7 +2103,7 @@ class Api:
|
|
2052
2103
|
)
|
2053
2104
|
return supports_event and supports_action
|
2054
2105
|
|
2055
|
-
def _omitted_automation_fragments(self) ->
|
2106
|
+
def _omitted_automation_fragments(self) -> set[str]:
|
2056
2107
|
"""Returns the names of unsupported automation-related fragments.
|
2057
2108
|
|
2058
2109
|
Older servers won't recognize newer GraphQL types, so a valid request may
|
@@ -2105,8 +2156,8 @@ class Api:
|
|
2105
2156
|
self,
|
2106
2157
|
name: str,
|
2107
2158
|
*,
|
2108
|
-
entity:
|
2109
|
-
) ->
|
2159
|
+
entity: str | None = None,
|
2160
|
+
) -> Automation:
|
2110
2161
|
"""Returns the only Automation matching the parameters.
|
2111
2162
|
|
2112
2163
|
Args:
|
@@ -2141,11 +2192,11 @@ class Api:
|
|
2141
2192
|
@tracked
|
2142
2193
|
def automations(
|
2143
2194
|
self,
|
2144
|
-
entity:
|
2195
|
+
entity: str | None = None,
|
2145
2196
|
*,
|
2146
|
-
name:
|
2197
|
+
name: str | None = None,
|
2147
2198
|
per_page: int = 50,
|
2148
|
-
) -> Iterator[
|
2199
|
+
) -> Iterator[Automation]:
|
2149
2200
|
"""Returns an iterator over all Automations that match the given parameters.
|
2150
2201
|
|
2151
2202
|
If no parameters are provided, the returned iterator will contain all
|
@@ -2199,11 +2250,11 @@ class Api:
|
|
2199
2250
|
@tracked
|
2200
2251
|
def create_automation(
|
2201
2252
|
self,
|
2202
|
-
obj:
|
2253
|
+
obj: NewAutomation,
|
2203
2254
|
*,
|
2204
2255
|
fetch_existing: bool = False,
|
2205
|
-
**kwargs: Unpack[
|
2206
|
-
) ->
|
2256
|
+
**kwargs: Unpack[WriteAutomationsKwargs],
|
2257
|
+
) -> Automation:
|
2207
2258
|
"""Create a new Automation.
|
2208
2259
|
|
2209
2260
|
Args:
|
@@ -2307,11 +2358,11 @@ class Api:
|
|
2307
2358
|
@tracked
|
2308
2359
|
def update_automation(
|
2309
2360
|
self,
|
2310
|
-
obj:
|
2361
|
+
obj: Automation,
|
2311
2362
|
*,
|
2312
2363
|
create_missing: bool = False,
|
2313
|
-
**kwargs: Unpack[
|
2314
|
-
) ->
|
2364
|
+
**kwargs: Unpack[WriteAutomationsKwargs],
|
2365
|
+
) -> Automation:
|
2315
2366
|
"""Update an existing automation.
|
2316
2367
|
|
2317
2368
|
Args:
|
@@ -2428,7 +2479,7 @@ class Api:
|
|
2428
2479
|
|
2429
2480
|
@normalize_exceptions
|
2430
2481
|
@tracked
|
2431
|
-
def delete_automation(self, obj:
|
2482
|
+
def delete_automation(self, obj: Automation | str) -> Literal[True]:
|
2432
2483
|
"""Delete an automation.
|
2433
2484
|
|
2434
2485
|
Args:
|