wandb 0.22.0__py3-none-musllinux_1_2_aarch64.whl → 0.22.1__py3-none-musllinux_1_2_aarch64.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.
Files changed (81) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +3 -3
  3. wandb/_pydantic/__init__.py +12 -11
  4. wandb/_pydantic/base.py +49 -19
  5. wandb/apis/__init__.py +2 -0
  6. wandb/apis/attrs.py +2 -0
  7. wandb/apis/importers/internals/internal.py +16 -23
  8. wandb/apis/internal.py +2 -0
  9. wandb/apis/normalize.py +2 -0
  10. wandb/apis/public/__init__.py +3 -2
  11. wandb/apis/public/api.py +215 -164
  12. wandb/apis/public/artifacts.py +23 -20
  13. wandb/apis/public/const.py +2 -0
  14. wandb/apis/public/files.py +33 -24
  15. wandb/apis/public/history.py +2 -0
  16. wandb/apis/public/jobs.py +20 -18
  17. wandb/apis/public/projects.py +4 -2
  18. wandb/apis/public/query_generator.py +3 -0
  19. wandb/apis/public/registries/__init__.py +7 -0
  20. wandb/apis/public/registries/_freezable_list.py +9 -12
  21. wandb/apis/public/registries/registries_search.py +8 -6
  22. wandb/apis/public/registries/registry.py +22 -17
  23. wandb/apis/public/reports.py +2 -0
  24. wandb/apis/public/runs.py +261 -57
  25. wandb/apis/public/sweeps.py +10 -9
  26. wandb/apis/public/teams.py +2 -0
  27. wandb/apis/public/users.py +2 -0
  28. wandb/apis/public/utils.py +16 -15
  29. wandb/automations/_generated/__init__.py +54 -127
  30. wandb/automations/_generated/create_generic_webhook_integration.py +1 -7
  31. wandb/automations/_generated/fragments.py +26 -91
  32. wandb/bin/wandb-core +0 -0
  33. wandb/cli/beta_sync.py +9 -11
  34. wandb/errors/errors.py +3 -3
  35. wandb/proto/v3/wandb_sync_pb2.py +19 -6
  36. wandb/proto/v4/wandb_sync_pb2.py +10 -6
  37. wandb/proto/v5/wandb_sync_pb2.py +10 -6
  38. wandb/proto/v6/wandb_sync_pb2.py +10 -6
  39. wandb/sdk/artifacts/_factories.py +7 -2
  40. wandb/sdk/artifacts/_generated/__init__.py +112 -412
  41. wandb/sdk/artifacts/_generated/fragments.py +65 -0
  42. wandb/sdk/artifacts/_generated/operations.py +52 -22
  43. wandb/sdk/artifacts/_generated/run_input_artifacts.py +3 -23
  44. wandb/sdk/artifacts/_generated/run_output_artifacts.py +3 -23
  45. wandb/sdk/artifacts/_generated/type_info.py +19 -0
  46. wandb/sdk/artifacts/_gqlutils.py +47 -0
  47. wandb/sdk/artifacts/_models/__init__.py +4 -0
  48. wandb/sdk/artifacts/_models/base_model.py +20 -0
  49. wandb/sdk/artifacts/_validators.py +40 -12
  50. wandb/sdk/artifacts/artifact.py +69 -88
  51. wandb/sdk/artifacts/artifact_file_cache.py +6 -1
  52. wandb/sdk/artifacts/artifact_manifest_entry.py +61 -2
  53. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +10 -0
  54. wandb/sdk/data_types/bokeh.py +5 -1
  55. wandb/sdk/data_types/image.py +17 -6
  56. wandb/sdk/interface/interface.py +31 -4
  57. wandb/sdk/interface/interface_queue.py +10 -0
  58. wandb/sdk/interface/interface_shared.py +0 -7
  59. wandb/sdk/interface/interface_sock.py +9 -3
  60. wandb/sdk/internal/_generated/__init__.py +2 -12
  61. wandb/sdk/internal/sender.py +1 -1
  62. wandb/sdk/internal/settings_static.py +2 -82
  63. wandb/sdk/launch/runner/kubernetes_runner.py +25 -20
  64. wandb/sdk/launch/utils.py +82 -1
  65. wandb/sdk/lib/progress.py +7 -4
  66. wandb/sdk/lib/service/service_client.py +5 -9
  67. wandb/sdk/lib/service/service_connection.py +39 -23
  68. wandb/sdk/mailbox/mailbox_handle.py +2 -0
  69. wandb/sdk/projects/_generated/__init__.py +12 -33
  70. wandb/sdk/wandb_init.py +22 -2
  71. wandb/sdk/wandb_login.py +53 -27
  72. wandb/sdk/wandb_run.py +5 -3
  73. wandb/sdk/wandb_settings.py +50 -13
  74. wandb/sync/sync.py +7 -2
  75. wandb/util.py +1 -1
  76. {wandb-0.22.0.dist-info → wandb-0.22.1.dist-info}/METADATA +1 -1
  77. {wandb-0.22.0.dist-info → wandb-0.22.1.dist-info}/RECORD +810 -807
  78. wandb/sdk/artifacts/_graphql_fragments.py +0 -19
  79. {wandb-0.22.0.dist-info → wandb-0.22.1.dist-info}/WHEEL +0 -0
  80. {wandb-0.22.0.dist-info → wandb-0.22.1.dist-info}/entry_points.txt +0 -0
  81. {wandb-0.22.0.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 is_artifact_registry_project
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: Optional[Dict[str, Any]] = None,
281
- timeout: Optional[int] = None,
282
- api_key: Optional[str] = None,
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
- wandb_login._login(
311
- host=self.settings["base_url"],
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
- verify=True,
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: Optional[str] = None,
386
- project: Optional[str] = None,
387
- entity: Optional[str] = None,
388
- ) -> "public.Run":
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: "public.RunQueueResourceType",
410
- entity: Optional[str] = None,
411
- prioritization_mode: Optional["public.RunQueuePrioritizationMode"] = None,
412
- config: Optional[dict] = None,
413
- template_variables: Optional[dict] = None,
414
- ) -> "public.RunQueue":
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: Union[str, dict],
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: "public.RunQueueResourceType",
597
- entity: Optional[str] = None,
598
- template_variables: Optional[dict] = None,
599
- external_links: Optional[dict] = None,
600
- prioritization_mode: Optional["public.RunQueuePrioritizationMode"] = None,
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: Optional[bool] = False):
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
- return public.User.create(self, email, admin)
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 api_key(self) -> Optional[str]:
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) -> "public.User":
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 = public.User(self._client, 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
- path, colon, alias = path.partition(":")
920
- full_alias = colon + alias
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: Optional[str] = None, per_page: int = 200
934
- ) -> "public.Projects":
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: Optional[str] = None) -> "public.Project":
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: Optional[str] = None, per_page: int = 50
987
- ) -> "public.Reports":
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
- return public.Team.create(self, team, admin_username)
1061
+ from .teams import Team
1062
+
1063
+ return Team.create(self, team, admin_username)
1044
1064
 
1045
- def team(self, team: str) -> "public.Team":
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
- return public.Team(self.client, team)
1074
+ from .teams import Team
1055
1075
 
1056
- def user(self, username_or_email: str) -> Optional["public.User"]:
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 public.User(self._client, res["users"]["edges"][0]["node"])
1101
+ return User(self._client, res["users"]["edges"][0]["node"])
1078
1102
 
1079
- def users(self, username_or_email: str) -> List["public.User"]:
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: Optional[str] = None,
1099
- filters: Optional[Dict[str, Any]] = None,
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
- if not self._runs.get(key):
1201
- self._runs[key] = public.Runs(
1202
- self.client,
1203
- entity,
1204
- project,
1205
- filters=filters,
1206
- order=order,
1207
- per_page=per_page,
1208
- include_sweeps=include_sweeps,
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
- self._runs[path] = public.Run(self.client, entity, project, run_id)
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: Optional[str] = None) -> "public.ArtifactTypes":
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 public.ArtifactTypes(self.client, entity, project)
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 public.ArtifactType(self.client, entity, project, type_name)
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
- ) -> "public.ArtifactCollections":
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 public.ArtifactCollections(
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 public.ArtifactCollection(
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: Optional[List[str]] = None,
1432
- ) -> "public.Artifacts":
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 public.Artifacts(
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: Optional[str] = None, enable_tracking: bool = False
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
- artifact = wandb.Artifact._from_name(
1507
- entity=entity,
1508
- project=project,
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: Optional[str] = None):
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: Optional[str], path: Optional[str] = None) -> "public.Job":
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) -> List[Dict[str, Any]]:
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: Optional[str] = None) -> bool:
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: Optional[str] = None,
1739
- filter: Optional[Dict[str, Any]] = None,
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: Optional[str] = None) -> Registry:
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: Optional[str] = None,
1849
- description: Optional[str] = None,
1850
- artifact_types: Optional[List[str]] = None,
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: Optional[str] = None,
1972
+ entity: str | None = None,
1922
1973
  *,
1923
1974
  per_page: int = 50,
1924
- ) -> Iterator["Integration"]:
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: Optional[str] = None, *, per_page: int = 50
1945
- ) -> Iterator["WebhookIntegration"]:
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: Optional[str] = None, per_page: int = 50
1989
- ) -> Iterator["SlackIntegration"]:
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: Optional["EventType"] = None,
2034
- action: Optional["ActionType"] = None,
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) -> Set[str]:
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: Optional[str] = None,
2109
- ) -> "Automation":
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: Optional[str] = None,
2195
+ entity: str | None = None,
2145
2196
  *,
2146
- name: Optional[str] = None,
2197
+ name: str | None = None,
2147
2198
  per_page: int = 50,
2148
- ) -> Iterator["Automation"]:
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: "NewAutomation",
2253
+ obj: NewAutomation,
2203
2254
  *,
2204
2255
  fetch_existing: bool = False,
2205
- **kwargs: Unpack["WriteAutomationsKwargs"],
2206
- ) -> "Automation":
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: "Automation",
2361
+ obj: Automation,
2311
2362
  *,
2312
2363
  create_missing: bool = False,
2313
- **kwargs: Unpack["WriteAutomationsKwargs"],
2314
- ) -> "Automation":
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: Union["Automation", str]) -> Literal[True]:
2482
+ def delete_automation(self, obj: Automation | str) -> Literal[True]:
2432
2483
  """Delete an automation.
2433
2484
 
2434
2485
  Args: