wandb 0.20.2rc20250616__py3-none-musllinux_1_2_aarch64.whl → 0.21.0__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 (72) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +24 -23
  3. wandb/apis/internal.py +3 -0
  4. wandb/apis/paginator.py +17 -4
  5. wandb/apis/public/api.py +83 -2
  6. wandb/apis/public/artifacts.py +10 -8
  7. wandb/apis/public/files.py +5 -5
  8. wandb/apis/public/projects.py +44 -3
  9. wandb/apis/public/reports.py +64 -8
  10. wandb/apis/public/runs.py +16 -23
  11. wandb/automations/__init__.py +10 -10
  12. wandb/automations/_filters/run_metrics.py +0 -2
  13. wandb/automations/_utils.py +0 -2
  14. wandb/automations/actions.py +0 -2
  15. wandb/automations/automations.py +0 -2
  16. wandb/automations/events.py +0 -2
  17. wandb/bin/gpu_stats +0 -0
  18. wandb/bin/wandb-core +0 -0
  19. wandb/integration/catboost/catboost.py +6 -2
  20. wandb/integration/kfp/kfp_patch.py +3 -1
  21. wandb/integration/sb3/sb3.py +3 -3
  22. wandb/integration/ultralytics/callback.py +6 -2
  23. wandb/plot/__init__.py +2 -0
  24. wandb/plot/bar.py +30 -29
  25. wandb/plot/confusion_matrix.py +75 -71
  26. wandb/plot/histogram.py +26 -25
  27. wandb/plot/line.py +33 -32
  28. wandb/plot/line_series.py +100 -103
  29. wandb/plot/pr_curve.py +33 -32
  30. wandb/plot/roc_curve.py +38 -38
  31. wandb/plot/scatter.py +27 -27
  32. wandb/proto/v3/wandb_internal_pb2.py +366 -385
  33. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  34. wandb/proto/v4/wandb_internal_pb2.py +352 -356
  35. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  36. wandb/proto/v5/wandb_internal_pb2.py +352 -356
  37. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  38. wandb/proto/v6/wandb_internal_pb2.py +352 -356
  39. wandb/proto/v6/wandb_settings_pb2.py +2 -2
  40. wandb/sdk/artifacts/_generated/__init__.py +12 -1
  41. wandb/sdk/artifacts/_generated/input_types.py +20 -2
  42. wandb/sdk/artifacts/_generated/link_artifact.py +21 -0
  43. wandb/sdk/artifacts/_generated/operations.py +9 -0
  44. wandb/sdk/artifacts/_validators.py +40 -2
  45. wandb/sdk/artifacts/artifact.py +163 -21
  46. wandb/sdk/data_types/base_types/media.py +9 -7
  47. wandb/sdk/data_types/base_types/wb_value.py +6 -6
  48. wandb/sdk/data_types/saved_model.py +3 -3
  49. wandb/sdk/data_types/table.py +41 -41
  50. wandb/sdk/data_types/trace_tree.py +12 -12
  51. wandb/sdk/interface/interface.py +8 -19
  52. wandb/sdk/interface/interface_shared.py +7 -16
  53. wandb/sdk/internal/datastore.py +18 -18
  54. wandb/sdk/internal/handler.py +3 -5
  55. wandb/sdk/internal/internal_api.py +54 -0
  56. wandb/sdk/internal/sender.py +23 -3
  57. wandb/sdk/internal/sender_config.py +9 -0
  58. wandb/sdk/launch/_project_spec.py +3 -3
  59. wandb/sdk/launch/agent/agent.py +3 -3
  60. wandb/sdk/launch/agent/job_status_tracker.py +3 -1
  61. wandb/sdk/launch/utils.py +3 -3
  62. wandb/sdk/lib/console_capture.py +66 -19
  63. wandb/sdk/wandb_init.py +1 -2
  64. wandb/sdk/wandb_require.py +0 -1
  65. wandb/sdk/wandb_run.py +23 -113
  66. wandb/sdk/wandb_settings.py +234 -72
  67. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.0.dist-info}/METADATA +1 -1
  68. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.0.dist-info}/RECORD +71 -71
  69. wandb/sdk/wandb_metadata.py +0 -623
  70. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.0.dist-info}/WHEEL +0 -0
  71. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.0.dist-info}/entry_points.txt +0 -0
  72. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.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.20.2rc20250616"
13
+ __version__ = "0.21.0"
14
14
 
15
15
 
16
16
  from wandb.errors import Error
wandb/__init__.pyi CHANGED
@@ -12,20 +12,20 @@ For reference documentation, see https://docs.wandb.com/ref/python.
12
12
  from __future__ import annotations
13
13
 
14
14
  __all__ = (
15
- "__version__",
15
+ "__version__", # doc:exclude
16
16
  "init",
17
17
  "finish",
18
18
  "setup",
19
19
  "login",
20
- "save",
20
+ "save", # doc:exclude
21
21
  "sweep",
22
22
  "controller",
23
23
  "agent",
24
- "config",
25
- "log",
26
- "summary",
24
+ "config", # doc:exclude
25
+ "log", # doc:exclude
26
+ "summary", # doc:exclude
27
27
  "Api",
28
- "Graph",
28
+ "Graph", # doc:exclude
29
29
  "Image",
30
30
  "Plotly",
31
31
  "Video",
@@ -36,26 +36,27 @@ __all__ = (
36
36
  "Object3D",
37
37
  "Molecule",
38
38
  "Histogram",
39
- "ArtifactTTL",
40
- "log_artifact",
41
- "use_artifact",
42
- "log_model",
43
- "use_model",
44
- "link_model",
45
- "define_metric",
46
- "Error",
47
- "termsetup",
48
- "termlog",
49
- "termerror",
50
- "termwarn",
39
+ "ArtifactTTL", # doc:exclude
40
+ "log_artifact", # doc:exclude
41
+ "use_artifact", # doc:exclude
42
+ "log_model", # doc:exclude
43
+ "use_model", # doc:exclude
44
+ "link_model", # doc:exclude
45
+ "define_metric", # doc:exclude
46
+ "Error", # doc:exclude
47
+ "termsetup", # doc:exclude
48
+ "termlog", # doc:exclude
49
+ "termerror", # doc:exclude
50
+ "termwarn", # doc:exclude
51
51
  "Artifact",
52
52
  "Settings",
53
53
  "teardown",
54
- "watch",
55
- "unwatch",
56
- "plot",
54
+ "watch", # doc:exclude
55
+ "unwatch", # doc:exclude
56
+ "plot", # doc:exclude
57
57
  "plot_table",
58
58
  "restore",
59
+ "Run",
59
60
  )
60
61
 
61
62
  import os
@@ -106,7 +107,7 @@ if TYPE_CHECKING:
106
107
  import wandb
107
108
  from wandb.plot import CustomChart
108
109
 
109
- __version__: str = "0.20.2rc20250616"
110
+ __version__: str = "0.21.0"
110
111
 
111
112
  run: Run | None
112
113
  config: wandb_config.Config
@@ -325,7 +326,7 @@ def init(
325
326
  the UI.
326
327
  If resuming a run, the tags provided here will replace any existing
327
328
  tags. To add tags to a resumed run without overwriting the current
328
- tags, use `run.tags += ["new_tag"]` after calling `run = wandb.init()`.
329
+ tags, use `run.tags += ("new_tag",)` after calling `run = wandb.init()`.
329
330
  config: Sets `wandb.config`, a dictionary-like object for storing input
330
331
  parameters to your run, such as model hyperparameters or data
331
332
  preprocessing settings.
wandb/apis/internal.py CHANGED
@@ -211,6 +211,9 @@ class Api:
211
211
  def upsert_run_queue(self, *args, **kwargs):
212
212
  return self.api.upsert_run_queue(*args, **kwargs)
213
213
 
214
+ def create_custom_chart(self, *args, **kwargs):
215
+ return self.api.create_custom_chart(*args, **kwargs)
216
+
214
217
  def update_launch_agent_status(self, *args, **kwargs):
215
218
  return self.api.update_launch_agent_status(*args, **kwargs)
216
219
 
wandb/apis/paginator.py CHANGED
@@ -13,6 +13,8 @@ from typing import (
13
13
  overload,
14
14
  )
15
15
 
16
+ import wandb
17
+
16
18
  if TYPE_CHECKING:
17
19
  from wandb_graphql.language.ast import Document
18
20
 
@@ -112,14 +114,25 @@ class Paginator(Iterator[T]):
112
114
  class SizedPaginator(Paginator[T], Sized):
113
115
  """A Paginator for objects with a known total count."""
114
116
 
117
+ @property
118
+ def length(self) -> int | None:
119
+ wandb.termwarn(
120
+ (
121
+ "`.length` is deprecated and will be removed in a future version. "
122
+ "Use `len(...)` instead."
123
+ ),
124
+ repeat=False,
125
+ )
126
+ return len(self)
127
+
115
128
  def __len__(self) -> int:
116
- if self.length is None:
129
+ if self._length is None:
117
130
  self._load_page()
118
- if self.length is None:
131
+ if self._length is None:
119
132
  raise ValueError("Object doesn't provide length")
120
- return self.length
133
+ return self._length
121
134
 
122
135
  @property
123
136
  @abstractmethod
124
- def length(self) -> int | None:
137
+ def _length(self) -> int | None:
125
138
  raise NotImplementedError
wandb/apis/public/api.py CHANGED
@@ -467,6 +467,85 @@ class Api:
467
467
  _default_resource_config=config,
468
468
  )
469
469
 
470
+ def create_custom_chart(
471
+ self,
472
+ entity: str,
473
+ name: str,
474
+ display_name: str,
475
+ spec_type: Literal["vega2"],
476
+ access: Literal["private", "public"],
477
+ spec: Union[str, dict],
478
+ ) -> str:
479
+ """Create a custom chart preset and return its id.
480
+
481
+ Args:
482
+ entity: The entity (user or team) that owns the chart
483
+ name: Unique identifier for the chart preset
484
+ display_name: Human-readable name shown in the UI
485
+ spec_type: Type of specification. Must be "vega2" for Vega-Lite v2 specifications.
486
+ access: Access level for the chart:
487
+ - "private": Chart is only accessible to the entity that created it
488
+ - "public": Chart is publicly accessible
489
+ spec: The Vega/Vega-Lite specification as a dictionary or JSON string
490
+
491
+ Returns:
492
+ The ID of the created chart preset in the format "entity/name"
493
+
494
+ Raises:
495
+ wandb.Error: If chart creation fails
496
+ UnsupportedError: If the server doesn't support custom charts
497
+
498
+ Example:
499
+ ```python
500
+ import wandb
501
+
502
+ api = wandb.Api()
503
+
504
+ # Define a simple bar chart specification
505
+ vega_spec = {
506
+ "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
507
+ "mark": "bar",
508
+ "data": {"name": "wandb"},
509
+ "encoding": {
510
+ "x": {"field": "${field:x}", "type": "ordinal"},
511
+ "y": {"field": "${field:y}", "type": "quantitative"},
512
+ },
513
+ }
514
+
515
+ # Create the custom chart
516
+ chart_id = api.create_custom_chart(
517
+ entity="my-team",
518
+ name="my-bar-chart",
519
+ display_name="My Custom Bar Chart",
520
+ spec_type="vega2",
521
+ access="private",
522
+ spec=vega_spec,
523
+ )
524
+
525
+ # Use with wandb.plot_table()
526
+ chart = wandb.plot_table(
527
+ vega_spec_name=chart_id,
528
+ data_table=my_table,
529
+ fields={"x": "category", "y": "value"},
530
+ )
531
+ ```
532
+ """
533
+ # Convert user-facing lowercase access to backend uppercase
534
+ backend_access = access.upper()
535
+
536
+ api = InternalApi(retry_timedelta=RETRY_TIMEDELTA)
537
+ result = api.create_custom_chart(
538
+ entity=entity,
539
+ name=name,
540
+ display_name=display_name,
541
+ spec_type=spec_type,
542
+ access=backend_access,
543
+ spec=spec,
544
+ )
545
+ if result is None or result.get("chart") is None:
546
+ raise wandb.Error("failed to create custom chart")
547
+ return result["chart"]["id"]
548
+
470
549
  def upsert_run_queue(
471
550
  self,
472
551
  name: str,
@@ -713,7 +792,7 @@ class Api:
713
792
  return public.BetaReport(
714
793
  self.client,
715
794
  {
716
- "display_name": urllib.parse.unquote(name.replace("-", " ")),
795
+ "displayName": urllib.parse.unquote(name.replace("-", " ")),
717
796
  "id": id,
718
797
  "spec": "{}",
719
798
  },
@@ -1514,7 +1593,9 @@ class Api:
1514
1593
 
1515
1594
  Find all collections in the registries with the name "my_collection" and the tag "my_tag"
1516
1595
  ```python
1517
- api.registries().collections(filter={"name": "my_collection", "tag": "my_tag"})
1596
+ api.registries().collections(
1597
+ filter={"name": "my_collection", "tag": "my_tag"}
1598
+ )
1518
1599
  ```
1519
1600
 
1520
1601
  Find all artifact versions in the registries with a collection name that contains "my_collection" and a version that has the alias "best"
@@ -100,7 +100,7 @@ class ArtifactTypes(Paginator["ArtifactType"]):
100
100
  self.last_response = ArtifactTypesFragment.model_validate(conn)
101
101
 
102
102
  @property
103
- def length(self) -> None:
103
+ def _length(self) -> None:
104
104
  # TODO
105
105
  return None
106
106
 
@@ -240,9 +240,9 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
240
240
  self.last_response = ArtifactCollectionsFragment.model_validate(conn)
241
241
 
242
242
  @property
243
- def length(self):
243
+ def _length(self) -> int:
244
244
  if self.last_response is None:
245
- return None
245
+ self._load_page()
246
246
  return self.last_response.total_count
247
247
 
248
248
  @property
@@ -608,9 +608,9 @@ class Artifacts(SizedPaginator["Artifact"]):
608
608
  self.last_response = ArtifactsFragment.model_validate(conn)
609
609
 
610
610
  @property
611
- def length(self) -> int | None:
611
+ def _length(self) -> int:
612
612
  if self.last_response is None:
613
- return None
613
+ self._load_page()
614
614
  return self.last_response.total_count
615
615
 
616
616
  @property
@@ -698,9 +698,9 @@ class RunArtifacts(SizedPaginator["Artifact"]):
698
698
  self.last_response = self._response_cls.model_validate(inner_data)
699
699
 
700
700
  @property
701
- def length(self) -> int | None:
701
+ def _length(self) -> int:
702
702
  if self.last_response is None:
703
- return None
703
+ self._load_page()
704
704
  return self.last_response.total_count
705
705
 
706
706
  @property
@@ -799,7 +799,9 @@ class ArtifactFiles(SizedPaginator["public.File"]):
799
799
  return [self.artifact.entity, self.artifact.project, self.artifact.name]
800
800
 
801
801
  @property
802
- def length(self) -> int:
802
+ def _length(self) -> int:
803
+ if self.last_response is None:
804
+ self._load_page()
803
805
  return self.artifact.file_count
804
806
 
805
807
  @property
@@ -72,11 +72,11 @@ class Files(SizedPaginator["File"]):
72
72
  super().__init__(client, variables, per_page)
73
73
 
74
74
  @property
75
- def length(self):
76
- if self.last_response:
77
- return self.last_response["project"]["run"]["fileCount"]
78
- else:
79
- return None
75
+ def _length(self):
76
+ if not self.last_response:
77
+ self._load_page()
78
+
79
+ return self.last_response["project"]["run"]["fileCount"]
80
80
 
81
81
  @property
82
82
  def more(self):
@@ -9,6 +9,7 @@ from wandb.apis import public
9
9
  from wandb.apis.attrs import Attrs
10
10
  from wandb.apis.normalize import normalize_exceptions
11
11
  from wandb.apis.paginator import Paginator
12
+ from wandb.apis.public.api import RetryingClient
12
13
  from wandb.sdk.lib import ipython
13
14
 
14
15
  PROJECT_FRAGMENT = """fragment ProjectFragment on Project {
@@ -43,7 +44,19 @@ class Projects(Paginator["Project"]):
43
44
  """.format(PROJECT_FRAGMENT)
44
45
  )
45
46
 
46
- def __init__(self, client, entity, per_page=50):
47
+ def __init__(
48
+ self,
49
+ client: RetryingClient,
50
+ entity: str,
51
+ per_page: int = 50,
52
+ ) -> "Projects":
53
+ """An iterable collection of `Project` objects.
54
+
55
+ Args:
56
+ client: The API client used to query W&B.
57
+ entity: The entity which owns the projects.
58
+ per_page: The number of projects to fetch per request to the API.
59
+ """
47
60
  self.client = client
48
61
  self.entity = entity
49
62
  variables = {
@@ -83,7 +96,31 @@ class Projects(Paginator["Project"]):
83
96
  class Project(Attrs):
84
97
  """A project is a namespace for runs."""
85
98
 
86
- def __init__(self, client, entity, project, attrs):
99
+ QUERY = gql(
100
+ """
101
+ query Project($project: String!, $entity: String!) {
102
+ project(name: $project, entityName: $entity) {
103
+ id
104
+ }
105
+ }
106
+ """
107
+ )
108
+
109
+ def __init__(
110
+ self,
111
+ client: RetryingClient,
112
+ entity: str,
113
+ project: str,
114
+ attrs: dict,
115
+ ) -> "Project":
116
+ """A single project associated with an entity.
117
+
118
+ Args:
119
+ client: The API client used to query W&B.
120
+ entity: The entity which owns the project.
121
+ project: The name of the project to query.
122
+ attrs: The attributes of the project.
123
+ """
87
124
  super().__init__(dict(attrs))
88
125
  self.client = client
89
126
  self.name = project
@@ -143,7 +180,7 @@ class Project(Attrs):
143
180
  )
144
181
  variable_values = {"project": self.name, "entity": self.entity}
145
182
  ret = self.client.execute(query, variable_values)
146
- if ret["project"]["totalSweeps"] < 1:
183
+ if not ret.get("project") or ret["project"]["totalSweeps"] < 1:
147
184
  return []
148
185
 
149
186
  return [
@@ -178,6 +215,10 @@ class Project(Attrs):
178
215
  variable_values = {"projectName": self.name, "entityName": self.entity}
179
216
  try:
180
217
  data = self.client.execute(self._PROJECT_ID, variable_values)
218
+
219
+ if not data.get("project") or not data["project"].get("id"):
220
+ raise ValueError(f"Project {self.name} not found")
221
+
181
222
  self._attrs["id"] = data["project"]["id"]
182
223
  return self._attrs["id"]
183
224
  except (HTTPError, LookupError, TypeError) as e:
@@ -2,6 +2,7 @@
2
2
 
3
3
  import ast
4
4
  import json
5
+ import re
5
6
  import urllib
6
7
 
7
8
  from wandb_gql import gql
@@ -32,9 +33,11 @@ class Reports(SizedPaginator["BetaReport"]):
32
33
  user {
33
34
  username
34
35
  photoUrl
36
+ email
35
37
  }
36
38
  spec
37
39
  updatedAt
40
+ createdAt
38
41
  }
39
42
  cursor
40
43
  }
@@ -60,7 +63,7 @@ class Reports(SizedPaginator["BetaReport"]):
60
63
  super().__init__(client, variables, per_page)
61
64
 
62
65
  @property
63
- def length(self):
66
+ def _length(self):
64
67
  # TODO: Add the count the backend
65
68
  if self.last_response:
66
69
  return len(self.objects)
@@ -111,11 +114,15 @@ class BetaReport(Attrs):
111
114
  WARNING: this API will likely change in a future release
112
115
 
113
116
  Attributes:
117
+ id (string): unique identifier of the report
114
118
  name (string): report name
115
- description (string): report description;
116
- user (User): the user that created the report
117
- spec (dict): the spec off the report;
119
+ display_name (string): display name of the report
120
+ description (string): report description
121
+ user (User): the user that created the report (contains username and email)
122
+ spec (dict): the spec of the report
123
+ url (string): the url of the report
118
124
  updated_at (string): timestamp of last update
125
+ created_at (string): timestamp when the report was created
119
126
  """
120
127
 
121
128
  def __init__(self, client, attrs, entity=None, project=None):
@@ -124,7 +131,16 @@ class BetaReport(Attrs):
124
131
  self.entity = entity
125
132
  self.query_generator = public.QueryGenerator()
126
133
  super().__init__(dict(attrs))
127
- self._attrs["spec"] = json.loads(self._attrs["spec"])
134
+
135
+ if "spec" in self._attrs:
136
+ if isinstance(self._attrs["spec"], str):
137
+ self._attrs["spec"] = json.loads(self._attrs["spec"])
138
+ else:
139
+ self._attrs["spec"] = {}
140
+
141
+ @property
142
+ def spec(self):
143
+ return self._attrs["spec"]
128
144
 
129
145
  @property
130
146
  def sections(self):
@@ -153,12 +169,44 @@ class BetaReport(Attrs):
153
169
  per_page=per_page,
154
170
  )
155
171
 
172
+ @property
173
+ def id(self):
174
+ return self._attrs.get("id")
175
+
176
+ @property
177
+ def name(self):
178
+ return self._attrs.get("name")
179
+
180
+ @property
181
+ def display_name(self):
182
+ return self._attrs.get("displayName")
183
+
184
+ @property
185
+ def description(self):
186
+ return self._attrs.get("description")
187
+
188
+ @property
189
+ def user(self):
190
+ return self._attrs.get("user")
191
+
156
192
  @property
157
193
  def updated_at(self):
158
- return self._attrs["updatedAt"]
194
+ return self._attrs.get("updatedAt")
195
+
196
+ @property
197
+ def created_at(self):
198
+ return self._attrs.get("createdAt")
159
199
 
160
200
  @property
161
201
  def url(self):
202
+ if (
203
+ not self.client
204
+ or not self.entity
205
+ or not self.project
206
+ or not self.display_name
207
+ or not self.id
208
+ ):
209
+ return None
162
210
  return self.client.app_url + "/".join(
163
211
  [
164
212
  self.entity,
@@ -166,7 +214,12 @@ class BetaReport(Attrs):
166
214
  "reports",
167
215
  "--".join(
168
216
  [
169
- urllib.parse.quote(self.display_name.replace(" ", "-")),
217
+ # made this more closely match the url creation in the frontend (https://github.com/wandb/core/blob/76943979c8e967f7a62dae8bef0a001a2672584c/frontends/app/src/util/report/urls.ts#L19)
218
+ urllib.parse.quote(
219
+ re.sub(
220
+ r"-+", "-", re.sub(r"\W", "-", self.display_name)
221
+ ).strip("-")
222
+ ),
170
223
  self.id.replace("=", ""),
171
224
  ]
172
225
  ),
@@ -175,7 +228,10 @@ class BetaReport(Attrs):
175
228
 
176
229
  def to_html(self, height=1024, hidden=False):
177
230
  """Generate HTML containing an iframe displaying this report."""
178
- url = self.url + "?jupyter=true"
231
+ url = self.url
232
+ if url is None:
233
+ return "<div>Report URL not available</div>"
234
+ url = url + "?jupyter=true"
179
235
  style = f"border:none;width:100%;height:{height}px;"
180
236
  prefix = ""
181
237
  if hidden:
wandb/apis/public/runs.py CHANGED
@@ -111,7 +111,7 @@ class Runs(SizedPaginator["Run"]):
111
111
  runs(filters: $filters, after: $cursor, first: $perPage, order: $order) {{
112
112
  edges {{
113
113
  node {{
114
- {"" if _server_provides_internal_id_for_project(client) else "internalId"}
114
+ {"projectId" if _server_provides_internal_id_for_project(client) else ""}
115
115
  ...RunFragment
116
116
  }}
117
117
  cursor
@@ -143,11 +143,10 @@ class Runs(SizedPaginator["Run"]):
143
143
  super().__init__(client, variables, per_page)
144
144
 
145
145
  @property
146
- def length(self):
147
- if self.last_response:
148
- return self.last_response["project"]["runCount"]
149
- else:
150
- return None
146
+ def _length(self):
147
+ if not self.last_response:
148
+ self._load_page()
149
+ return self.last_response["project"]["runCount"]
151
150
 
152
151
  @property
153
152
  def more(self):
@@ -454,25 +453,19 @@ class Run(Attrs):
454
453
  )
455
454
 
456
455
  def load(self, force=False):
457
- query = gql(
458
- """
459
- query Run($project: String!, $entity: String!, $name: String!) {{
460
- project(name: $project, entityName: $entity) {{
461
- run(name: $name) {{
462
- {}
463
- ...RunFragment
456
+ if force or not self._attrs:
457
+ query = gql(f"""#graphql
458
+ query Run($project: String!, $entity: String!, $name: String!) {{
459
+ project(name: $project, entityName: $entity) {{
460
+ run(name: $name) {{
461
+ {"projectId" if _server_provides_internal_id_for_project(self.client) else ""}
462
+ ...RunFragment
463
+ }}
464
464
  }}
465
465
  }}
466
- }}
467
- {}
468
- """.format(
469
- "projectId"
470
- if _server_provides_internal_id_for_project(self.client)
471
- else "",
472
- RUN_FRAGMENT,
473
- )
474
- )
475
- if force or not self._attrs:
466
+ {RUN_FRAGMENT}
467
+ """)
468
+
476
469
  response = self._exec(query)
477
470
  if (
478
471
  response is None
@@ -45,21 +45,21 @@ else:
45
45
 
46
46
  __all__ = [
47
47
  # Scopes
48
- "ScopeType",
49
- "ArtifactCollectionScope",
50
- "ProjectScope",
48
+ "ScopeType", # doc:exclude
49
+ "ArtifactCollectionScope", # doc:exclude
50
+ "ProjectScope", # doc:exclude
51
51
  # Events
52
- "EventType",
52
+ "EventType", # doc:exclude
53
53
  "OnAddArtifactAlias",
54
54
  "OnCreateArtifact",
55
55
  "OnLinkArtifact",
56
56
  "OnRunMetric",
57
- "ArtifactEvent",
58
- "RunEvent",
57
+ "ArtifactEvent", # doc:exclude
58
+ "RunEvent", # doc:exclude
59
59
  "MetricThresholdFilter",
60
60
  "MetricChangeFilter",
61
61
  # Actions
62
- "ActionType",
62
+ "ActionType", # doc:exclude
63
63
  "SendNotification",
64
64
  "SendWebhook",
65
65
  "DoNothing",
@@ -67,7 +67,7 @@ __all__ = [
67
67
  "Automation",
68
68
  "NewAutomation",
69
69
  # Integrations
70
- "Integration",
71
- "SlackIntegration",
72
- "WebhookIntegration",
70
+ "Integration", # doc:exclude
71
+ "SlackIntegration", # doc:exclude
72
+ "WebhookIntegration", # doc:exclude
73
73
  ]
@@ -1,5 +1,3 @@
1
- # ruff: noqa: UP007
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from abc import ABC, abstractmethod
@@ -1,5 +1,3 @@
1
- # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Any, Collection, Final, Optional, Protocol, TypedDict
@@ -1,7 +1,5 @@
1
1
  """Actions that are triggered by W&B Automations."""
2
2
 
3
- # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
4
-
5
3
  from __future__ import annotations
6
4
 
7
5
  from typing import Any, Literal, Optional, Union
@@ -1,5 +1,3 @@
1
- # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from datetime import datetime