wandb 0.20.2rc20250616__py3-none-win_amd64.whl → 0.21.1__py3-none-win_amd64.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 (140) hide show
  1. wandb/__init__.py +16 -14
  2. wandb/__init__.pyi +450 -472
  3. wandb/agents/pyagent.py +41 -12
  4. wandb/analytics/sentry.py +7 -2
  5. wandb/apis/importers/mlflow.py +1 -1
  6. wandb/apis/internal.py +3 -0
  7. wandb/apis/paginator.py +17 -4
  8. wandb/apis/public/__init__.py +1 -1
  9. wandb/apis/public/api.py +606 -359
  10. wandb/apis/public/artifacts.py +214 -16
  11. wandb/apis/public/automations.py +19 -3
  12. wandb/apis/public/files.py +177 -38
  13. wandb/apis/public/history.py +67 -15
  14. wandb/apis/public/integrations.py +25 -2
  15. wandb/apis/public/jobs.py +90 -2
  16. wandb/apis/public/projects.py +161 -69
  17. wandb/apis/public/query_generator.py +11 -1
  18. wandb/apis/public/registries/registries_search.py +7 -15
  19. wandb/apis/public/reports.py +147 -13
  20. wandb/apis/public/runs.py +315 -128
  21. wandb/apis/public/sweeps.py +222 -22
  22. wandb/apis/public/teams.py +41 -4
  23. wandb/apis/public/users.py +45 -4
  24. wandb/automations/__init__.py +10 -10
  25. wandb/automations/_filters/run_metrics.py +0 -2
  26. wandb/automations/_utils.py +0 -2
  27. wandb/automations/actions.py +0 -2
  28. wandb/automations/automations.py +0 -2
  29. wandb/automations/events.py +0 -2
  30. wandb/beta/workflows.py +66 -30
  31. wandb/bin/gpu_stats.exe +0 -0
  32. wandb/bin/wandb-core +0 -0
  33. wandb/cli/cli.py +80 -1
  34. wandb/env.py +8 -0
  35. wandb/errors/errors.py +4 -1
  36. wandb/integration/catboost/catboost.py +6 -2
  37. wandb/integration/kfp/kfp_patch.py +3 -1
  38. wandb/integration/lightning/fabric/logger.py +3 -4
  39. wandb/integration/metaflow/__init__.py +6 -0
  40. wandb/integration/metaflow/data_pandas.py +74 -0
  41. wandb/integration/metaflow/errors.py +13 -0
  42. wandb/integration/metaflow/metaflow.py +205 -190
  43. wandb/integration/openai/fine_tuning.py +1 -2
  44. wandb/integration/sb3/sb3.py +3 -3
  45. wandb/integration/ultralytics/callback.py +6 -2
  46. wandb/jupyter.py +5 -5
  47. wandb/plot/__init__.py +2 -0
  48. wandb/plot/bar.py +30 -29
  49. wandb/plot/confusion_matrix.py +75 -71
  50. wandb/plot/custom_chart.py +30 -7
  51. wandb/plot/histogram.py +26 -25
  52. wandb/plot/line.py +33 -32
  53. wandb/plot/line_series.py +100 -103
  54. wandb/plot/pr_curve.py +33 -32
  55. wandb/plot/roc_curve.py +38 -38
  56. wandb/plot/scatter.py +27 -27
  57. wandb/proto/v3/wandb_internal_pb2.py +366 -385
  58. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  59. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  60. wandb/proto/v4/wandb_internal_pb2.py +352 -356
  61. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  62. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  63. wandb/proto/v5/wandb_internal_pb2.py +352 -356
  64. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  65. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  66. wandb/proto/v6/wandb_internal_pb2.py +352 -356
  67. wandb/proto/v6/wandb_settings_pb2.py +2 -2
  68. wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
  69. wandb/proto/wandb_deprecated.py +6 -0
  70. wandb/sdk/artifacts/_generated/__init__.py +12 -1
  71. wandb/sdk/artifacts/_generated/input_types.py +20 -2
  72. wandb/sdk/artifacts/_generated/link_artifact.py +21 -0
  73. wandb/sdk/artifacts/_generated/operations.py +9 -0
  74. wandb/sdk/artifacts/_internal_artifact.py +19 -8
  75. wandb/sdk/artifacts/_validators.py +48 -2
  76. wandb/sdk/artifacts/artifact.py +269 -96
  77. wandb/sdk/data_types/audio.py +38 -10
  78. wandb/sdk/data_types/base_types/media.py +15 -63
  79. wandb/sdk/data_types/base_types/wb_value.py +6 -6
  80. wandb/sdk/data_types/graph.py +48 -14
  81. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
  82. wandb/sdk/data_types/helper_types/image_mask.py +1 -3
  83. wandb/sdk/data_types/histogram.py +34 -21
  84. wandb/sdk/data_types/html.py +35 -12
  85. wandb/sdk/data_types/image.py +104 -68
  86. wandb/sdk/data_types/molecule.py +32 -19
  87. wandb/sdk/data_types/object_3d.py +36 -17
  88. wandb/sdk/data_types/plotly.py +18 -5
  89. wandb/sdk/data_types/saved_model.py +7 -9
  90. wandb/sdk/data_types/table.py +99 -70
  91. wandb/sdk/data_types/trace_tree.py +12 -12
  92. wandb/sdk/data_types/video.py +53 -26
  93. wandb/sdk/integration_utils/auto_logging.py +2 -2
  94. wandb/sdk/interface/interface.py +8 -19
  95. wandb/sdk/interface/interface_shared.py +7 -16
  96. wandb/sdk/internal/datastore.py +18 -18
  97. wandb/sdk/internal/handler.py +3 -5
  98. wandb/sdk/internal/internal_api.py +60 -0
  99. wandb/sdk/internal/job_builder.py +6 -0
  100. wandb/sdk/internal/sender.py +23 -3
  101. wandb/sdk/internal/sender_config.py +9 -0
  102. wandb/sdk/launch/_project_spec.py +3 -3
  103. wandb/sdk/launch/agent/agent.py +11 -4
  104. wandb/sdk/launch/agent/job_status_tracker.py +3 -1
  105. wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
  106. wandb/sdk/launch/create_job.py +3 -1
  107. wandb/sdk/launch/inputs/internal.py +3 -4
  108. wandb/sdk/launch/inputs/schema.py +1 -0
  109. wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
  110. wandb/sdk/launch/runner/kubernetes_runner.py +328 -1
  111. wandb/sdk/launch/sweeps/scheduler.py +2 -3
  112. wandb/sdk/launch/utils.py +3 -3
  113. wandb/sdk/lib/asyncio_compat.py +3 -0
  114. wandb/sdk/lib/console_capture.py +66 -19
  115. wandb/sdk/lib/deprecate.py +1 -7
  116. wandb/sdk/lib/disabled.py +1 -1
  117. wandb/sdk/lib/hashutil.py +14 -1
  118. wandb/sdk/lib/module.py +7 -13
  119. wandb/sdk/lib/progress.py +0 -19
  120. wandb/sdk/lib/sock_client.py +0 -4
  121. wandb/sdk/wandb_init.py +67 -93
  122. wandb/sdk/wandb_login.py +18 -14
  123. wandb/sdk/wandb_metric.py +2 -0
  124. wandb/sdk/wandb_require.py +0 -1
  125. wandb/sdk/wandb_run.py +429 -527
  126. wandb/sdk/wandb_settings.py +364 -74
  127. wandb/sdk/wandb_setup.py +28 -28
  128. wandb/sdk/wandb_sweep.py +14 -13
  129. wandb/sdk/wandb_watch.py +4 -6
  130. wandb/sync/sync.py +10 -0
  131. wandb/util.py +57 -0
  132. wandb/wandb_run.py +1 -2
  133. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/METADATA +1 -1
  134. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/RECORD +137 -137
  135. wandb/sdk/wandb_metadata.py +0 -623
  136. wandb/vendor/pynvml/__init__.py +0 -0
  137. wandb/vendor/pynvml/pynvml.py +0 -4779
  138. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/WHEEL +0 -0
  139. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/entry_points.txt +0 -0
  140. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/licenses/LICENSE +0 -0
wandb/apis/public/runs.py CHANGED
@@ -1,20 +1,45 @@
1
- """Public API: runs."""
1
+ """W&B Public API for Runs.
2
+
3
+ This module provides classes for interacting with W&B runs and their associated
4
+ data.
5
+
6
+ Example:
7
+ ```python
8
+ from wandb.apis.public import Api
9
+
10
+ # Get runs matching filters
11
+ runs = Api().runs(
12
+ path="entity/project", filters={"state": "finished", "config.batch_size": 32}
13
+ )
14
+
15
+ # Access run data
16
+ for run in runs:
17
+ print(f"Run: {run.name}")
18
+ print(f"Config: {run.config}")
19
+ print(f"Metrics: {run.summary}")
20
+
21
+ # Get history with pandas
22
+ history_df = run.history(keys=["loss", "accuracy"], pandas=True)
23
+
24
+ # Work with artifacts
25
+ for artifact in run.logged_artifacts():
26
+ print(f"Artifact: {artifact.name}")
27
+ ```
28
+
29
+ Note:
30
+ This module is part of the W&B Public API and provides read/write access
31
+ to run data. For logging new runs, use the wandb.init() function from
32
+ the main wandb package.
33
+ """
34
+
35
+ from __future__ import annotations
2
36
 
3
37
  import json
4
38
  import os
5
39
  import tempfile
6
40
  import time
7
41
  import urllib
8
- from typing import (
9
- TYPE_CHECKING,
10
- Any,
11
- Collection,
12
- Dict,
13
- List,
14
- Literal,
15
- Mapping,
16
- Optional,
17
- )
42
+ from typing import TYPE_CHECKING, Any, Collection, Literal, Mapping
18
43
 
19
44
  from wandb_gql import gql
20
45
 
@@ -85,22 +110,101 @@ def _server_provides_internal_id_for_project(client) -> bool:
85
110
  ]
86
111
 
87
112
 
88
- class Runs(SizedPaginator["Run"]):
89
- """An iterable collection of runs associated with a project and optional filter.
113
+ @normalize_exceptions
114
+ def _convert_to_dict(value: Any) -> dict[str, Any]:
115
+ """Converts a value to a dictionary.
90
116
 
91
- This is generally used indirectly via the `Api`.runs method.
117
+ If the value is already a dictionary, the value is returned unchanged.
118
+ If the value is a string, bytes, or bytearray, it is parsed as JSON.
119
+ For any other type, a TypeError is raised.
120
+ """
121
+ if value is None:
122
+ return {}
123
+
124
+ if isinstance(value, dict):
125
+ return value
126
+
127
+ if isinstance(value, (str, bytes, bytearray)):
128
+ try:
129
+ return json.loads(value)
130
+ except json.decoder.JSONDecodeError:
131
+ # ignore invalid utf-8 or control characters
132
+ return json.loads(value, strict=False)
133
+
134
+ raise TypeError(f"Unable to convert {value} to a dict")
135
+
136
+
137
+ class Runs(SizedPaginator["Run"]):
138
+ """A lazy iterator of `Run` objects associated with a project and optional filter.
139
+
140
+ Runs are retrieved in pages from the W&B server as needed.
141
+
142
+ This is generally used indirectly using the `Api.runs` namespace.
143
+
144
+ Args:
145
+ client: (`wandb.apis.public.RetryingClient`) The API client to use
146
+ for requests.
147
+ entity: (str) The entity (username or team) that owns the project.
148
+ project: (str) The name of the project to fetch runs from.
149
+ filters: (Optional[Dict[str, Any]]) A dictionary of filters to apply
150
+ to the runs query.
151
+ order: (str) Order can be `created_at`, `heartbeat_at`, `config.*.value`, or `summary_metrics.*`.
152
+ If you prepend order with a + order is ascending (default).
153
+ If you prepend order with a - order is descending.
154
+ The default order is run.created_at from oldest to newest.
155
+ per_page: (int) The number of runs to fetch per request (default is 50).
156
+ include_sweeps: (bool) Whether to include sweep information in the
157
+ runs. Defaults to True.
158
+
159
+ Examples:
160
+ ```python
161
+ from wandb.apis.public.runs import Runs
162
+ from wandb.apis.public import Api
163
+
164
+ # Get all runs from a project that satisfy the filters
165
+ filters = {"state": "finished", "config.optimizer": "adam"}
166
+
167
+ runs = Api().runs(
168
+ client=api.client,
169
+ entity="entity",
170
+ project="project_name",
171
+ filters=filters,
172
+ )
173
+
174
+ # Iterate over runs and print details
175
+ for run in runs:
176
+ print(f"Run name: {run.name}")
177
+ print(f"Run ID: {run.id}")
178
+ print(f"Run URL: {run.url}")
179
+ print(f"Run state: {run.state}")
180
+ print(f"Run config: {run.config}")
181
+ print(f"Run summary: {run.summary}")
182
+ print(f"Run history (samples=5): {run.history(samples=5)}")
183
+ print("----------")
184
+
185
+ # Get histories for all runs with specific metrics
186
+ histories_df = runs.histories(
187
+ samples=100, # Number of samples per run
188
+ keys=["loss", "accuracy"], # Metrics to fetch
189
+ x_axis="_step", # X-axis metric
190
+ format="pandas", # Return as pandas DataFrame
191
+ )
192
+ ```
92
193
  """
93
194
 
94
195
  def __init__(
95
196
  self,
96
- client: "RetryingClient",
197
+ client: RetryingClient,
97
198
  entity: str,
98
199
  project: str,
99
- filters: Optional[Dict[str, Any]] = None,
100
- order: Optional[str] = None,
200
+ filters: dict[str, Any] | None = None,
201
+ order: str = "+created_at",
101
202
  per_page: int = 50,
102
203
  include_sweeps: bool = True,
103
204
  ):
205
+ if not order:
206
+ order = "+created_at"
207
+
104
208
  self.QUERY = gql(
105
209
  f"""#graphql
106
210
  query Runs($project: String!, $entity: String!, $cursor: String, $perPage: Int = 50, $order: String, $filters: JSONString) {{
@@ -111,7 +215,7 @@ class Runs(SizedPaginator["Run"]):
111
215
  runs(filters: $filters, after: $cursor, first: $perPage, order: $order) {{
112
216
  edges {{
113
217
  node {{
114
- {"" if _server_provides_internal_id_for_project(client) else "internalId"}
218
+ {"projectId" if _server_provides_internal_id_for_project(client) else ""}
115
219
  ...RunFragment
116
220
  }}
117
221
  cursor
@@ -143,27 +247,44 @@ class Runs(SizedPaginator["Run"]):
143
247
  super().__init__(client, variables, per_page)
144
248
 
145
249
  @property
146
- def length(self):
147
- if self.last_response:
148
- return self.last_response["project"]["runCount"]
149
- else:
150
- return None
250
+ def _length(self):
251
+ """Returns the total number of runs.
252
+
253
+ <!-- lazydoc-ignore: internal -->
254
+ """
255
+ if not self.last_response:
256
+ self._load_page()
257
+ return self.last_response["project"]["runCount"]
151
258
 
152
259
  @property
153
- def more(self):
260
+ def more(self) -> bool:
261
+ """Returns whether there are more runs to fetch.
262
+
263
+ <!-- lazydoc-ignore: internal -->
264
+ """
154
265
  if self.last_response:
155
- return self.last_response["project"]["runs"]["pageInfo"]["hasNextPage"]
266
+ return bool(
267
+ self.last_response["project"]["runs"]["pageInfo"]["hasNextPage"]
268
+ )
156
269
  else:
157
270
  return True
158
271
 
159
272
  @property
160
273
  def cursor(self):
274
+ """Returns the cursor position for pagination of runs results.
275
+
276
+ <!-- lazydoc-ignore: internal -->
277
+ """
161
278
  if self.last_response:
162
279
  return self.last_response["project"]["runs"]["edges"][-1]["cursor"]
163
280
  else:
164
281
  return None
165
282
 
166
283
  def convert_objects(self):
284
+ """Converts GraphQL edges to Runs objects.
285
+
286
+ <!-- lazydoc-ignore: internal -->
287
+ """
167
288
  objs = []
168
289
  if self.last_response is None or self.last_response.get("project") is None:
169
290
  raise ValueError("Could not find project {}".format(self.project))
@@ -201,7 +322,7 @@ class Runs(SizedPaginator["Run"]):
201
322
  def histories(
202
323
  self,
203
324
  samples: int = 500,
204
- keys: Optional[List[str]] = None,
325
+ keys: list[str] | None = None,
205
326
  x_axis: str = "_step",
206
327
  format: Literal["default", "pandas", "polars"] = "default",
207
328
  stream: Literal["default", "system"] = "default",
@@ -209,15 +330,19 @@ class Runs(SizedPaginator["Run"]):
209
330
  """Return sampled history metrics for all runs that fit the filters conditions.
210
331
 
211
332
  Args:
212
- samples : (int, optional) The number of samples to return per run
213
- keys : (list[str], optional) Only return metrics for specific keys
214
- x_axis : (str, optional) Use this metric as the xAxis defaults to _step
215
- format : (Literal, optional) Format to return data in, options are "default", "pandas", "polars"
216
- stream : (Literal, optional) "default" for metrics, "system" for machine metrics
333
+ samples: The number of samples to return per run
334
+ keys: Only return metrics for specific keys
335
+ x_axis: Use this metric as the xAxis defaults to _step
336
+ format: Format to return data in, options are "default", "pandas",
337
+ "polars"
338
+ stream: "default" for metrics, "system" for machine metrics
217
339
  Returns:
218
- pandas.DataFrame: If format="pandas", returns a `pandas.DataFrame` of history metrics.
219
- polars.DataFrame: If format="polars", returns a `polars.DataFrame` of history metrics.
220
- list of dicts: If format="default", returns a list of dicts containing history metrics with a run_id key.
340
+ pandas.DataFrame: If `format="pandas"`, returns a `pandas.DataFrame`
341
+ of history metrics.
342
+ polars.DataFrame: If `format="polars"`, returns a `polars.DataFrame`
343
+ of history metrics.
344
+ list of dicts: If `format="default"`, returns a list of dicts
345
+ containing history metrics with a `run_id` key.
221
346
  """
222
347
  if format not in ("default", "pandas", "polars"):
223
348
  raise ValueError(
@@ -301,6 +426,14 @@ class Runs(SizedPaginator["Run"]):
301
426
  class Run(Attrs):
302
427
  """A single run associated with an entity and project.
303
428
 
429
+ Args:
430
+ client: The W&B API client.
431
+ entity: The entity associated with the run.
432
+ project: The project associated with the run.
433
+ run_id: The unique identifier for the run.
434
+ attrs: The attributes of the run.
435
+ include_sweeps: Whether to include sweeps in the run.
436
+
304
437
  Attributes:
305
438
  tags ([str]): a list of tags associated with the run
306
439
  url (str): the url of this run
@@ -326,11 +459,11 @@ class Run(Attrs):
326
459
 
327
460
  def __init__(
328
461
  self,
329
- client: "RetryingClient",
462
+ client: RetryingClient,
330
463
  entity: str,
331
464
  project: str,
332
465
  run_id: str,
333
- attrs: Optional[Mapping] = None,
466
+ attrs: Mapping | None = None,
334
467
  include_sweeps: bool = True,
335
468
  ):
336
469
  """Initialize a Run object.
@@ -354,27 +487,32 @@ class Run(Attrs):
354
487
  except OSError:
355
488
  pass
356
489
  self._summary = None
357
- self._metadata: Optional[Dict[str, Any]] = None
490
+ self._metadata: dict[str, Any] | None = None
358
491
  self._state = _attrs.get("state", "not found")
359
- self.server_provides_internal_id_field: Optional[bool] = None
492
+ self.server_provides_internal_id_field: bool | None = None
493
+ self._is_loaded: bool = False
360
494
 
361
495
  self.load(force=not _attrs)
362
496
 
363
497
  @property
364
498
  def state(self):
499
+ """The state of the run. Can be one of: Finished, Failed, Crashed, or Running."""
365
500
  return self._state
366
501
 
367
502
  @property
368
503
  def entity(self):
504
+ """The entity associated with the run."""
369
505
  return self._entity
370
506
 
371
507
  @property
372
508
  def username(self):
509
+ """This API is deprecated. Use `entity` instead."""
373
510
  wandb.termwarn("Run.username is deprecated. Please use Run.entity instead.")
374
511
  return self._entity
375
512
 
376
513
  @property
377
514
  def storage_id(self):
515
+ """The unique storage identifier for the run."""
378
516
  # For compatibility with wandb.Run, which has storage IDs
379
517
  # in self.storage_id and names in self.id.
380
518
 
@@ -382,33 +520,38 @@ class Run(Attrs):
382
520
 
383
521
  @property
384
522
  def id(self):
523
+ """The unique identifier for the run."""
385
524
  return self._attrs.get("name")
386
525
 
387
526
  @id.setter
388
527
  def id(self, new_id):
528
+ """Set the unique identifier for the run."""
389
529
  attrs = self._attrs
390
530
  attrs["name"] = new_id
391
531
  return new_id
392
532
 
393
533
  @property
394
534
  def name(self):
535
+ """The name of the run."""
395
536
  return self._attrs.get("displayName")
396
537
 
397
538
  @name.setter
398
539
  def name(self, new_name):
540
+ """Set the name of the run."""
399
541
  self._attrs["displayName"] = new_name
400
542
  return new_name
401
543
 
402
544
  @classmethod
403
545
  def create(
404
546
  cls,
405
- api,
406
- run_id=None,
407
- project=None,
408
- entity=None,
547
+ api: public.Api,
548
+ run_id: str | None = None,
549
+ project: str | None = None,
550
+ entity: str | None = None,
409
551
  state: Literal["running", "pending"] = "running",
410
552
  ):
411
553
  """Create a run for the given project."""
554
+ api._sentry.message("Invoking Run.create", level="info")
412
555
  run_id = run_id or runid.generate_id()
413
556
  project = project or api.settings.get("project") or "uncategorized"
414
557
  mutation = gql(
@@ -454,25 +597,20 @@ class Run(Attrs):
454
597
  )
455
598
 
456
599
  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
600
+ if force or not self._attrs:
601
+ self._is_loaded = False
602
+ query = gql(f"""#graphql
603
+ query Run($project: String!, $entity: String!, $name: String!) {{
604
+ project(name: $project, entityName: $entity) {{
605
+ run(name: $name) {{
606
+ {"projectId" if _server_provides_internal_id_for_project(self.client) else ""}
607
+ ...RunFragment
608
+ }}
464
609
  }}
465
610
  }}
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:
611
+ {RUN_FRAGMENT}
612
+ """)
613
+
476
614
  response = self._exec(query)
477
615
  if (
478
616
  response is None
@@ -481,7 +619,6 @@ class Run(Attrs):
481
619
  ):
482
620
  raise ValueError("Could not find run {}".format(self))
483
621
  self._attrs = response["project"]["run"]
484
- self._state = self._attrs["state"]
485
622
 
486
623
  if self._include_sweeps and self.sweep_name and not self.sweep:
487
624
  # There may be a lot of runs. Don't bother pulling them all
@@ -494,32 +631,31 @@ class Run(Attrs):
494
631
  withRuns=False,
495
632
  )
496
633
 
634
+ if not self._is_loaded:
635
+ self._load_from_attrs()
636
+ self._is_loaded = True
637
+
638
+ return self._attrs
639
+
640
+ def _load_from_attrs(self):
641
+ self._state = self._attrs.get("state", None)
642
+ self._attrs["config"] = _convert_to_dict(self._attrs.get("config"))
643
+ self._attrs["summaryMetrics"] = _convert_to_dict(
644
+ self._attrs.get("summaryMetrics")
645
+ )
646
+ self._attrs["systemMetrics"] = _convert_to_dict(
647
+ self._attrs.get("systemMetrics")
648
+ )
649
+
497
650
  if "projectId" in self._attrs:
498
651
  self._project_internal_id = int(self._attrs["projectId"])
499
652
  else:
500
653
  self._project_internal_id = None
501
654
 
502
- try:
503
- self._attrs["summaryMetrics"] = (
504
- json.loads(self._attrs["summaryMetrics"])
505
- if self._attrs.get("summaryMetrics")
506
- else {}
507
- )
508
- except json.decoder.JSONDecodeError:
509
- # ignore invalid utf-8 or control characters
510
- self._attrs["summaryMetrics"] = json.loads(
511
- self._attrs["summaryMetrics"],
512
- strict=False,
513
- )
514
- self._attrs["systemMetrics"] = (
515
- json.loads(self._attrs["systemMetrics"])
516
- if self._attrs.get("systemMetrics")
517
- else {}
518
- )
519
655
  if self._attrs.get("user"):
520
656
  self.user = public.User(self.client, self._attrs["user"])
521
657
  config_user, config_raw = {}, {}
522
- for key, value in json.loads(self._attrs.get("config") or "{}").items():
658
+ for key, value in self._attrs.get("config").items():
523
659
  config = config_raw if key in WANDB_INTERNAL_KEYS else config_user
524
660
  if isinstance(value, dict) and "value" in value:
525
661
  config[key] = value["value"]
@@ -528,10 +664,10 @@ class Run(Attrs):
528
664
  config_raw.update(config_user)
529
665
  self._attrs["config"] = config_user
530
666
  self._attrs["rawconfig"] = config_raw
531
- return self._attrs
532
667
 
533
668
  @normalize_exceptions
534
669
  def wait_until_finished(self):
670
+ """Check the state of the run until it is finished."""
535
671
  query = gql(
536
672
  """
537
673
  query RunState($project: String!, $entity: String!, $name: String!) {
@@ -582,7 +718,12 @@ class Run(Attrs):
582
718
 
583
719
  @normalize_exceptions
584
720
  def delete(self, delete_artifacts=False):
585
- """Delete the given run from the wandb backend."""
721
+ """Delete the given run from the wandb backend.
722
+
723
+ Args:
724
+ delete_artifacts (bool, optional): Whether to delete the artifacts
725
+ associated with the run.
726
+ """
586
727
  mutation = gql(
587
728
  """
588
729
  mutation DeleteRun(
@@ -611,10 +752,15 @@ class Run(Attrs):
611
752
  )
612
753
 
613
754
  def save(self):
755
+ """Persist changes to the run object to the W&B backend."""
614
756
  self.update()
615
757
 
616
758
  @property
617
759
  def json_config(self):
760
+ """Return the run config as a JSON string.
761
+
762
+ <!-- lazydoc-ignore: internal -->
763
+ """
618
764
  config = {}
619
765
  if "_wandb" in self.rawconfig:
620
766
  config["_wandb"] = {"value": self.rawconfig["_wandb"], "desc": None}
@@ -660,17 +806,35 @@ class Run(Attrs):
660
806
  return [json.loads(line) for line in response["project"]["run"][node]]
661
807
 
662
808
  @normalize_exceptions
663
- def files(self, names=None, per_page=50):
664
- """Return a file path for each file named.
809
+ def files(
810
+ self,
811
+ names: list[str] | None = None,
812
+ pattern: str | None = None,
813
+ per_page: int = 50,
814
+ ):
815
+ """Returns a `Files` object for all files in the run which match the given criteria.
816
+
817
+ You can specify a list of exact file names to match, or a pattern to match against.
818
+ If both are provided, the pattern will be ignored.
665
819
 
666
820
  Args:
667
821
  names (list): names of the requested files, if empty returns all files
822
+ pattern (str, optional): Pattern to match when returning files from W&B.
823
+ This pattern uses mySQL's LIKE syntax,
824
+ so matching all files that end with .json would be "%.json".
825
+ If both names and pattern are provided, a ValueError will be raised.
668
826
  per_page (int): number of results per page.
669
827
 
670
828
  Returns:
671
829
  A `Files` object, which is an iterator over `File` objects.
672
830
  """
673
- return public.Files(self.client, self, names or [], per_page)
831
+ return public.Files(
832
+ self.client,
833
+ self,
834
+ names or [],
835
+ pattern=pattern,
836
+ per_page=per_page,
837
+ )
674
838
 
675
839
  @normalize_exceptions
676
840
  def file(self, name):
@@ -686,16 +850,17 @@ class Run(Attrs):
686
850
 
687
851
  @normalize_exceptions
688
852
  def upload_file(self, path, root="."):
689
- """Upload a file.
853
+ """Upload a local file to W&B, associating it with this run.
690
854
 
691
855
  Args:
692
- path (str): name of file to upload.
693
- root (str): the root path to save the file relative to. i.e.
694
- If you want to have the file saved in the run as "my_dir/file.txt"
856
+ path (str): Path to the file to upload. Can be absolute or relative.
857
+ root (str): The root path to save the file relative to. For example,
858
+ if you want to have the file saved in the run as "my_dir/file.txt"
695
859
  and you're currently in "my_dir" you would set root to "../".
860
+ Defaults to current directory (".").
696
861
 
697
862
  Returns:
698
- A `File` matching the name argument.
863
+ A `File` object representing the uploaded file.
699
864
  """
700
865
  api = InternalApi(
701
866
  default_settings={"entity": self.entity, "project": self.project},
@@ -704,8 +869,9 @@ class Run(Attrs):
704
869
  api.set_current_run_id(self.id)
705
870
  root = os.path.abspath(root)
706
871
  name = os.path.relpath(path, root)
872
+ upload_path = util.make_file_path_upload_safe(name)
707
873
  with open(os.path.join(root, name), "rb") as f:
708
- api.push({LogicalPath(name): f})
874
+ api.push({LogicalPath(upload_path): f})
709
875
  return public.Files(self.client, self, [name])[0]
710
876
 
711
877
  @normalize_exceptions
@@ -754,15 +920,6 @@ class Run(Attrs):
754
920
  def scan_history(self, keys=None, page_size=1000, min_step=None, max_step=None):
755
921
  """Returns an iterable collection of all history records for a run.
756
922
 
757
- Example:
758
- Export all the loss values for an example run
759
-
760
- ```python
761
- run = api.run("l2k2/examples-numpy-boston/i0wt6xua")
762
- history = run.scan_history(keys=["Loss"])
763
- losses = [row["Loss"] for row in history]
764
- ```
765
-
766
923
  Args:
767
924
  keys ([str], optional): only fetch these keys, and only fetch rows that have all of keys defined.
768
925
  page_size (int, optional): size of pages to fetch from the api.
@@ -771,6 +928,15 @@ class Run(Attrs):
771
928
 
772
929
  Returns:
773
930
  An iterable collection over history records (dict).
931
+
932
+ Example:
933
+ Export all the loss values for an example run
934
+
935
+ ```python
936
+ run = api.run("entity/project-name/run-id")
937
+ history = run.scan_history(keys=["Loss"])
938
+ losses = [row["Loss"] for row in history]
939
+ ```
774
940
  """
775
941
  if keys is not None and not isinstance(keys, list):
776
942
  wandb.termerror("keys must be specified in a list")
@@ -820,23 +986,27 @@ class Run(Attrs):
820
986
  An iterable collection of all Artifact objects logged as outputs during this run.
821
987
 
822
988
  Example:
823
- >>> import wandb
824
- >>> import tempfile
825
- >>> with tempfile.NamedTemporaryFile(
826
- ... mode="w", delete=False, suffix=".txt"
827
- ... ) as tmp:
828
- ... tmp.write("This is a test artifact")
829
- ... tmp_path = tmp.name
830
- >>> run = wandb.init(project="artifact-example")
831
- >>> artifact = wandb.Artifact("test_artifact", type="dataset")
832
- >>> artifact.add_file(tmp_path)
833
- >>> run.log_artifact(artifact)
834
- >>> run.finish()
835
- >>> api = wandb.Api()
836
- >>> finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
837
- >>> for logged_artifact in finished_run.logged_artifacts():
838
- ... print(logged_artifact.name)
839
- test_artifact
989
+ ```python
990
+ import wandb
991
+ import tempfile
992
+
993
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as tmp:
994
+ tmp.write("This is a test artifact")
995
+ tmp_path = tmp.name
996
+ run = wandb.init(project="artifact-example")
997
+ artifact = wandb.Artifact("test_artifact", type="dataset")
998
+ artifact.add_file(tmp_path)
999
+ run.log_artifact(artifact)
1000
+ run.finish()
1001
+
1002
+ api = wandb.Api()
1003
+
1004
+ finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
1005
+
1006
+ for logged_artifact in finished_run.logged_artifacts():
1007
+ print(logged_artifact.name)
1008
+ ```
1009
+
840
1010
  """
841
1011
  return public.RunArtifacts(self.client, self, mode="logged", per_page=per_page)
842
1012
 
@@ -855,15 +1025,19 @@ class Run(Attrs):
855
1025
  An iterable collection of Artifact objects explicitly used as inputs in this run.
856
1026
 
857
1027
  Example:
858
- >>> import wandb
859
- >>> run = wandb.init(project="artifact-example")
860
- >>> run.use_artifact("test_artifact:latest")
861
- >>> run.finish()
862
- >>> api = wandb.Api()
863
- >>> finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
864
- >>> for used_artifact in finished_run.used_artifacts():
865
- ... print(used_artifact.name)
866
- test_artifact
1028
+ ```python
1029
+ import wandb
1030
+
1031
+ run = wandb.init(project="artifact-example")
1032
+ run.use_artifact("test_artifact:latest")
1033
+ run.finish()
1034
+
1035
+ api = wandb.Api()
1036
+ finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
1037
+ for used_artifact in finished_run.used_artifacts():
1038
+ print(used_artifact.name)
1039
+ test_artifact
1040
+ ```
867
1041
  """
868
1042
  return public.RunArtifacts(self.client, self, mode="used", per_page=per_page)
869
1043
 
@@ -881,7 +1055,7 @@ class Run(Attrs):
881
1055
  feature's artifact swapping functionality.
882
1056
 
883
1057
  Returns:
884
- A `Artifact` object.
1058
+ An `Artifact` object.
885
1059
  """
886
1060
  api = InternalApi(
887
1061
  default_settings={"entity": self.entity, "project": self.project},
@@ -908,9 +1082,9 @@ class Run(Attrs):
908
1082
  @normalize_exceptions
909
1083
  def log_artifact(
910
1084
  self,
911
- artifact: "wandb.Artifact",
912
- aliases: Optional[Collection[str]] = None,
913
- tags: Optional[Collection[str]] = None,
1085
+ artifact: wandb.Artifact,
1086
+ aliases: Collection[str] | None = None,
1087
+ tags: Collection[str] | None = None,
914
1088
  ):
915
1089
  """Declare an artifact as output of a run.
916
1090
 
@@ -954,6 +1128,7 @@ class Run(Attrs):
954
1128
 
955
1129
  @property
956
1130
  def summary(self):
1131
+ """A mutable dict-like property that holds summary values associated with the run."""
957
1132
  if self._summary is None:
958
1133
  from wandb.old.summary import HTTPSummary
959
1134
 
@@ -963,6 +1138,7 @@ class Run(Attrs):
963
1138
 
964
1139
  @property
965
1140
  def path(self):
1141
+ """The path of the run. The path is a list containing the entity, project, and run_id."""
966
1142
  return [
967
1143
  urllib.parse.quote_plus(str(self.entity)),
968
1144
  urllib.parse.quote_plus(str(self.project)),
@@ -971,12 +1147,22 @@ class Run(Attrs):
971
1147
 
972
1148
  @property
973
1149
  def url(self):
1150
+ """The URL of the run.
1151
+
1152
+ The run URL is generated from the entity, project, and run_id. For
1153
+ SaaS users, it takes the form of `https://wandb.ai/entity/project/run_id`.
1154
+ """
974
1155
  path = self.path
975
1156
  path.insert(2, "runs")
976
1157
  return self.client.app_url + "/".join(path)
977
1158
 
978
1159
  @property
979
1160
  def metadata(self):
1161
+ """Metadata about the run from wandb-metadata.json.
1162
+
1163
+ Metadata includes the run's description, tags, start time, memory
1164
+ usage and more.
1165
+ """
980
1166
  if self._metadata is None:
981
1167
  try:
982
1168
  f = self.file("wandb-metadata.json")
@@ -992,6 +1178,7 @@ class Run(Attrs):
992
1178
 
993
1179
  @property
994
1180
  def lastHistoryStep(self): # noqa: N802
1181
+ """Returns the last step logged in the run's history."""
995
1182
  query = gql(
996
1183
  """
997
1184
  query RunHistoryKeys($project: String!, $entity: String!, $name: String!) {