wandb 0.21.0__py3-none-win_amd64.whl → 0.21.2__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 (153) hide show
  1. wandb/__init__.py +16 -14
  2. wandb/__init__.pyi +427 -450
  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/public/__init__.py +1 -1
  7. wandb/apis/public/api.py +525 -360
  8. wandb/apis/public/artifacts.py +207 -13
  9. wandb/apis/public/automations.py +19 -3
  10. wandb/apis/public/files.py +172 -33
  11. wandb/apis/public/history.py +67 -15
  12. wandb/apis/public/integrations.py +25 -2
  13. wandb/apis/public/jobs.py +90 -2
  14. wandb/apis/public/projects.py +130 -79
  15. wandb/apis/public/query_generator.py +11 -1
  16. wandb/apis/public/registries/_utils.py +14 -16
  17. wandb/apis/public/registries/registries_search.py +183 -304
  18. wandb/apis/public/reports.py +96 -15
  19. wandb/apis/public/runs.py +299 -105
  20. wandb/apis/public/sweeps.py +222 -22
  21. wandb/apis/public/teams.py +41 -4
  22. wandb/apis/public/users.py +45 -4
  23. wandb/automations/_generated/delete_automation.py +1 -3
  24. wandb/automations/_generated/enums.py +13 -11
  25. wandb/beta/workflows.py +66 -30
  26. wandb/bin/gpu_stats.exe +0 -0
  27. wandb/bin/wandb-core +0 -0
  28. wandb/cli/cli.py +127 -3
  29. wandb/env.py +8 -0
  30. wandb/errors/errors.py +4 -1
  31. wandb/integration/lightning/fabric/logger.py +3 -4
  32. wandb/integration/metaflow/__init__.py +6 -0
  33. wandb/integration/metaflow/data_pandas.py +74 -0
  34. wandb/integration/metaflow/data_pytorch.py +75 -0
  35. wandb/integration/metaflow/data_sklearn.py +76 -0
  36. wandb/integration/metaflow/errors.py +13 -0
  37. wandb/integration/metaflow/metaflow.py +167 -223
  38. wandb/integration/openai/fine_tuning.py +1 -2
  39. wandb/integration/weave/__init__.py +6 -0
  40. wandb/integration/weave/interface.py +49 -0
  41. wandb/integration/weave/weave.py +63 -0
  42. wandb/jupyter.py +5 -5
  43. wandb/plot/custom_chart.py +30 -7
  44. wandb/proto/v3/wandb_internal_pb2.py +281 -280
  45. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  46. wandb/proto/v4/wandb_internal_pb2.py +280 -280
  47. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  48. wandb/proto/v5/wandb_internal_pb2.py +280 -280
  49. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  50. wandb/proto/v6/wandb_internal_pb2.py +280 -280
  51. wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
  52. wandb/proto/wandb_deprecated.py +6 -0
  53. wandb/sdk/artifacts/_factories.py +17 -0
  54. wandb/sdk/artifacts/_generated/__init__.py +221 -13
  55. wandb/sdk/artifacts/_generated/artifact_by_id.py +17 -0
  56. wandb/sdk/artifacts/_generated/artifact_by_name.py +22 -0
  57. wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +43 -0
  58. wandb/sdk/artifacts/_generated/artifact_created_by.py +47 -0
  59. wandb/sdk/artifacts/_generated/artifact_file_urls.py +22 -0
  60. wandb/sdk/artifacts/_generated/artifact_type.py +31 -0
  61. wandb/sdk/artifacts/_generated/artifact_used_by.py +43 -0
  62. wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +26 -0
  63. wandb/sdk/artifacts/_generated/delete_artifact.py +28 -0
  64. wandb/sdk/artifacts/_generated/enums.py +5 -0
  65. wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +38 -0
  66. wandb/sdk/artifacts/_generated/fetch_registries.py +32 -0
  67. wandb/sdk/artifacts/_generated/fragments.py +279 -41
  68. wandb/sdk/artifacts/_generated/link_artifact.py +6 -0
  69. wandb/sdk/artifacts/_generated/operations.py +654 -51
  70. wandb/sdk/artifacts/_generated/registry_collections.py +34 -0
  71. wandb/sdk/artifacts/_generated/registry_versions.py +34 -0
  72. wandb/sdk/artifacts/_generated/unlink_artifact.py +25 -0
  73. wandb/sdk/artifacts/_graphql_fragments.py +3 -86
  74. wandb/sdk/artifacts/_internal_artifact.py +19 -8
  75. wandb/sdk/artifacts/_validators.py +14 -4
  76. wandb/sdk/artifacts/artifact.py +512 -618
  77. wandb/sdk/artifacts/artifact_file_cache.py +10 -6
  78. wandb/sdk/artifacts/artifact_manifest.py +10 -9
  79. wandb/sdk/artifacts/artifact_manifest_entry.py +9 -10
  80. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +5 -3
  81. wandb/sdk/artifacts/storage_handlers/http_handler.py +1 -1
  82. wandb/sdk/artifacts/storage_handlers/s3_handler.py +1 -1
  83. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +1 -1
  84. wandb/sdk/data_types/audio.py +38 -10
  85. wandb/sdk/data_types/base_types/media.py +6 -56
  86. wandb/sdk/data_types/graph.py +48 -14
  87. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
  88. wandb/sdk/data_types/helper_types/image_mask.py +1 -3
  89. wandb/sdk/data_types/histogram.py +34 -21
  90. wandb/sdk/data_types/html.py +35 -12
  91. wandb/sdk/data_types/image.py +104 -68
  92. wandb/sdk/data_types/molecule.py +32 -19
  93. wandb/sdk/data_types/object_3d.py +36 -17
  94. wandb/sdk/data_types/plotly.py +18 -5
  95. wandb/sdk/data_types/saved_model.py +4 -6
  96. wandb/sdk/data_types/table.py +59 -30
  97. wandb/sdk/data_types/video.py +53 -26
  98. wandb/sdk/integration_utils/auto_logging.py +2 -2
  99. wandb/sdk/interface/interface_queue.py +1 -4
  100. wandb/sdk/interface/interface_shared.py +26 -37
  101. wandb/sdk/interface/interface_sock.py +24 -14
  102. wandb/sdk/internal/internal_api.py +6 -0
  103. wandb/sdk/internal/job_builder.py +6 -0
  104. wandb/sdk/internal/settings_static.py +2 -3
  105. wandb/sdk/launch/agent/agent.py +8 -1
  106. wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
  107. wandb/sdk/launch/create_job.py +15 -2
  108. wandb/sdk/launch/inputs/internal.py +3 -4
  109. wandb/sdk/launch/inputs/schema.py +1 -0
  110. wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
  111. wandb/sdk/launch/runner/kubernetes_runner.py +323 -1
  112. wandb/sdk/launch/sweeps/scheduler.py +2 -3
  113. wandb/sdk/lib/asyncio_compat.py +19 -16
  114. wandb/sdk/lib/asyncio_manager.py +252 -0
  115. wandb/sdk/lib/deprecate.py +1 -7
  116. wandb/sdk/lib/disabled.py +1 -1
  117. wandb/sdk/lib/hashutil.py +27 -5
  118. wandb/sdk/lib/module.py +7 -13
  119. wandb/sdk/lib/printer.py +2 -2
  120. wandb/sdk/lib/printer_asyncio.py +3 -1
  121. wandb/sdk/lib/progress.py +0 -19
  122. wandb/sdk/lib/retry.py +185 -78
  123. wandb/sdk/lib/service/service_client.py +106 -0
  124. wandb/sdk/lib/service/service_connection.py +20 -26
  125. wandb/sdk/lib/service/service_token.py +30 -13
  126. wandb/sdk/mailbox/mailbox.py +13 -5
  127. wandb/sdk/mailbox/mailbox_handle.py +22 -13
  128. wandb/sdk/mailbox/response_handle.py +42 -106
  129. wandb/sdk/mailbox/wait_with_progress.py +7 -42
  130. wandb/sdk/wandb_init.py +77 -116
  131. wandb/sdk/wandb_login.py +19 -15
  132. wandb/sdk/wandb_metric.py +2 -0
  133. wandb/sdk/wandb_run.py +497 -469
  134. wandb/sdk/wandb_settings.py +145 -4
  135. wandb/sdk/wandb_setup.py +204 -124
  136. wandb/sdk/wandb_sweep.py +14 -13
  137. wandb/sdk/wandb_watch.py +4 -6
  138. wandb/sync/sync.py +10 -0
  139. wandb/util.py +58 -1
  140. wandb/wandb_run.py +1 -2
  141. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/METADATA +1 -1
  142. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/RECORD +145 -129
  143. wandb/sdk/interface/interface_relay.py +0 -38
  144. wandb/sdk/interface/router.py +0 -89
  145. wandb/sdk/interface/router_queue.py +0 -43
  146. wandb/sdk/interface/router_relay.py +0 -50
  147. wandb/sdk/interface/router_sock.py +0 -32
  148. wandb/sdk/lib/sock_client.py +0 -236
  149. wandb/vendor/pynvml/__init__.py +0 -0
  150. wandb/vendor/pynvml/pynvml.py +0 -4779
  151. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/WHEEL +0 -0
  152. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/entry_points.txt +0 -0
  153. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/licenses/LICENSE +0 -0
wandb/apis/public/jobs.py CHANGED
@@ -1,4 +1,8 @@
1
- """Public API: jobs."""
1
+ """W&B Public API for management Launch Jobs and Launch Queues.
2
+
3
+ This module provides classes for managing W&B jobs, queued runs, and run
4
+ queues.
5
+ """
2
6
 
3
7
  import json
4
8
  import os
@@ -78,6 +82,7 @@ class Job:
78
82
 
79
83
  @property
80
84
  def name(self):
85
+ """The name of the job."""
81
86
  return self._name
82
87
 
83
88
  def _set_configure_launch_project(self, func):
@@ -162,6 +167,7 @@ class Job:
162
167
  launch_project.set_job_entry_point(self._entrypoint)
163
168
 
164
169
  def set_entrypoint(self, entrypoint: List[str]):
170
+ """Set the entrypoint for the job."""
165
171
  self._entrypoint = entrypoint
166
172
 
167
173
  def call(
@@ -176,6 +182,29 @@ class Job:
176
182
  project_queue=None,
177
183
  priority=None,
178
184
  ):
185
+ """Call the job with the given configuration.
186
+
187
+ Args:
188
+ config (dict): The configuration to pass to the job.
189
+ This should be a dictionary containing key-value pairs that
190
+ match the input types defined in the job.
191
+ project (str, optional): The project to log the run to. Defaults
192
+ to the job's project.
193
+ entity (str, optional): The entity to log the run under. Defaults
194
+ to the job's entity.
195
+ queue (str, optional): The name of the queue to enqueue the job to.
196
+ Defaults to None.
197
+ resource (str, optional): The resource type to use for execution.
198
+ Defaults to "local-container".
199
+ resource_args (dict, optional): Additional arguments for the
200
+ resource type. Defaults to None.
201
+ template_variables (dict, optional): Template variables to use for
202
+ the job. Defaults to None.
203
+ project_queue (str, optional): The project that manages the queue.
204
+ Defaults to None.
205
+ priority (int, optional): The priority of the queued run.
206
+ Defaults to None.
207
+ """
179
208
  from wandb.sdk.launch import _launch_add
180
209
 
181
210
  run_config = {}
@@ -212,7 +241,19 @@ class Job:
212
241
 
213
242
 
214
243
  class QueuedRun:
215
- """A single queued run associated with an entity and project. Call `run = queued_run.wait_until_running()` or `run = queued_run.wait_until_finished()` to access the run."""
244
+ """A single queued run associated with an entity and project.
245
+
246
+ Args:
247
+ entity: The entity associated with the queued run.
248
+ project (str): The project where runs executed by the queue are logged to.
249
+ queue_name (str): The name of the queue.
250
+ run_queue_item_id (int): The id of the run queue item.
251
+ project_queue (str): The project that manages the queue.
252
+ priority (str): The priority of the queued run.
253
+
254
+ Call `run = queued_run.wait_until_running()` or
255
+ `run = queued_run.wait_until_finished()` to access the run.
256
+ """
216
257
 
217
258
  def __init__(
218
259
  self,
@@ -236,22 +277,27 @@ class QueuedRun:
236
277
 
237
278
  @property
238
279
  def queue_name(self):
280
+ """The name of the queue."""
239
281
  return self._queue_name
240
282
 
241
283
  @property
242
284
  def id(self):
285
+ """The id of the queued run."""
243
286
  return self._run_queue_item_id
244
287
 
245
288
  @property
246
289
  def project(self):
290
+ """The project associated with the queued run."""
247
291
  return self._project
248
292
 
249
293
  @property
250
294
  def entity(self):
295
+ """The entity associated with the queued run."""
251
296
  return self._entity
252
297
 
253
298
  @property
254
299
  def state(self):
300
+ """The state of the queued run."""
255
301
  item = self._get_item()
256
302
  if item:
257
303
  return item["state"].lower()
@@ -327,6 +373,7 @@ class QueuedRun:
327
373
 
328
374
  @normalize_exceptions
329
375
  def wait_until_finished(self):
376
+ """Wait for the queued run to complete and return the finished run."""
330
377
  if not self._run:
331
378
  self.wait_until_running()
332
379
 
@@ -388,6 +435,7 @@ class QueuedRun:
388
435
 
389
436
  @normalize_exceptions
390
437
  def wait_until_running(self):
438
+ """Wait until the queued run is running and return the run."""
391
439
  if self._run is not None:
392
440
  return self._run
393
441
 
@@ -426,6 +474,20 @@ RunQueuePrioritizationMode = Literal["DISABLED", "V0"]
426
474
 
427
475
 
428
476
  class RunQueue:
477
+ """Class that represents a run queue in W&B.
478
+
479
+ Args:
480
+ client: W&B API client instance.
481
+ name: Name of the run queue
482
+ entity: The entity (user or team) that owns this queue
483
+ prioritization_mode: Queue priority mode
484
+ Can be "DISABLED" or "V0". Defaults to `None`.
485
+ _access: Access level for the queue
486
+ Can be "project" or "user". Defaults to `None`.
487
+ _default_resource_config_id: ID of default resource config
488
+ _default_resource_config: Default resource configuration
489
+ """
490
+
429
491
  def __init__(
430
492
  self,
431
493
  client: "RetryingClient",
@@ -450,32 +512,41 @@ class RunQueue:
450
512
 
451
513
  @property
452
514
  def name(self):
515
+ """The name of the queue."""
453
516
  return self._name
454
517
 
455
518
  @property
456
519
  def entity(self):
520
+ """The entity that owns the queue."""
457
521
  return self._entity
458
522
 
459
523
  @property
460
524
  def prioritization_mode(self) -> RunQueuePrioritizationMode:
525
+ """The prioritization mode of the queue.
526
+
527
+ Can be set to "DISABLED" or "V0".
528
+ """
461
529
  if self._prioritization_mode is None:
462
530
  self._get_metadata()
463
531
  return self._prioritization_mode
464
532
 
465
533
  @property
466
534
  def access(self) -> RunQueueAccessType:
535
+ """The access level of the queue."""
467
536
  if self._access is None:
468
537
  self._get_metadata()
469
538
  return self._access
470
539
 
471
540
  @property
472
541
  def external_links(self) -> Dict[str, str]:
542
+ """External resource links for the queue."""
473
543
  if self._external_links is None:
474
544
  self._get_metadata()
475
545
  return self._external_links
476
546
 
477
547
  @property
478
548
  def type(self) -> RunQueueResourceType:
549
+ """The resource type for execution."""
479
550
  if self._type is None:
480
551
  if self._default_resource_config_id is None:
481
552
  self._get_metadata()
@@ -484,6 +555,7 @@ class RunQueue:
484
555
 
485
556
  @property
486
557
  def default_resource_config(self):
558
+ """The default configuration for resources."""
487
559
  if self._default_resource_config is None:
488
560
  if self._default_resource_config_id is None:
489
561
  self._get_metadata()
@@ -492,6 +564,7 @@ class RunQueue:
492
564
 
493
565
  @property
494
566
  def template_variables(self):
567
+ """Variables for resource templates."""
495
568
  if self._template_variables is None:
496
569
  if self._default_resource_config_id is None:
497
570
  self._get_metadata()
@@ -500,6 +573,7 @@ class RunQueue:
500
573
 
501
574
  @property
502
575
  def id(self) -> str:
576
+ """The id of the queue."""
503
577
  if self._id is None:
504
578
  self._get_metadata()
505
579
  return self._id
@@ -648,6 +722,20 @@ class RunQueue:
648
722
  config: Optional[dict] = None,
649
723
  template_variables: Optional[dict] = None,
650
724
  ) -> "RunQueue":
725
+ """Create a RunQueue.
726
+
727
+ Args:
728
+ name: The name of the run queue to create.
729
+ resource: The resource type for execution.
730
+ entity: The entity (user or team) that will own the queue.
731
+ Defaults to the default entity of the API client.
732
+ prioritization_mode: The prioritization mode for the queue.
733
+ Can be "DISABLED" or "V0". Defaults to None.
734
+ config: Optional dictionary for the default resource
735
+ configuration. Defaults to None.
736
+ template_variables: Optional dictionary for template variables
737
+ used in the resource configuration.
738
+ """
651
739
  public_api = Api()
652
740
  return public_api.create_run_queue(
653
741
  name, resource, entity, prioritization_mode, config, template_variables
@@ -1,6 +1,35 @@
1
- """Public API: projects."""
1
+ """W&B Public API for Project objects.
2
2
 
3
- from contextlib import suppress
3
+ This module provides classes for interacting with W&B projects and their
4
+ associated data.
5
+
6
+ Example:
7
+ ```python
8
+ from wandb.apis.public import Api
9
+
10
+ # Get all projects for an entity
11
+ projects = Api().projects("entity")
12
+
13
+ # Access project data
14
+ for project in projects:
15
+ print(f"Project: {project.name}")
16
+ print(f"URL: {project.url}")
17
+
18
+ # Get artifact types
19
+ for artifact_type in project.artifacts_types():
20
+ print(f"Artifact Type: {artifact_type.name}")
21
+
22
+ # Get sweeps
23
+ for sweep in project.sweeps():
24
+ print(f"Sweep ID: {sweep.id}")
25
+ print(f"State: {sweep.state}")
26
+ ```
27
+
28
+ Note:
29
+ This module is part of the W&B Public API and provides methods to access
30
+ and manage projects. For creating new projects, use wandb.init()
31
+ with a new project name.
32
+ """
4
33
 
5
34
  from requests import HTTPError
6
35
  from wandb_gql import gql
@@ -10,6 +39,7 @@ from wandb.apis.attrs import Attrs
10
39
  from wandb.apis.normalize import normalize_exceptions
11
40
  from wandb.apis.paginator import Paginator
12
41
  from wandb.apis.public.api import RetryingClient
42
+ from wandb.apis.public.sweeps import Sweeps
13
43
  from wandb.sdk.lib import ipython
14
44
 
15
45
  PROJECT_FRAGMENT = """fragment ProjectFragment on Project {
@@ -22,10 +52,32 @@ PROJECT_FRAGMENT = """fragment ProjectFragment on Project {
22
52
 
23
53
 
24
54
  class Projects(Paginator["Project"]):
25
- """An iterable collection of `Project` objects."""
55
+ """An lazy iterator of `Project` objects.
26
56
 
27
- QUERY = gql(
28
- """
57
+ An iterable interface to access projects created and saved by the entity.
58
+
59
+ Args:
60
+ client (`wandb.apis.internal.Api`): The API client instance to use.
61
+ entity (str): The entity name (username or team) to fetch projects for.
62
+ per_page (int): Number of projects to fetch per request (default is 50).
63
+
64
+ Example:
65
+ ```python
66
+ from wandb.apis.public.api import Api
67
+
68
+ # Find projects that belong to this entity
69
+ projects = Api().projects(entity="entity")
70
+
71
+ # Iterate over files
72
+ for project in projects:
73
+ print(f"Project: {project.name}")
74
+ print(f"- URL: {project.url}")
75
+ print(f"- Created at: {project.created_at}")
76
+ print(f"- Is benchmark: {project.is_benchmark}")
77
+ ```
78
+ """
79
+
80
+ QUERY = gql(f"""#graphql
29
81
  query Projects($entity: String, $cursor: String, $perPage: Int = 50) {{
30
82
  models(entityName: $entity, after: $cursor, first: $perPage) {{
31
83
  edges {{
@@ -40,9 +92,8 @@ class Projects(Paginator["Project"]):
40
92
  }}
41
93
  }}
42
94
  }}
43
- {}
44
- """.format(PROJECT_FRAGMENT)
45
- )
95
+ {PROJECT_FRAGMENT}
96
+ """)
46
97
 
47
98
  def __init__(
48
99
  self,
@@ -66,11 +117,22 @@ class Projects(Paginator["Project"]):
66
117
 
67
118
  @property
68
119
  def length(self) -> None:
120
+ """Returns the total number of projects.
121
+
122
+ Note: This property is not available for projects.
123
+
124
+ <!-- lazydoc-ignore: internal -->
125
+ """
69
126
  # For backwards compatibility, even though this isn't a SizedPaginator
70
127
  return None
71
128
 
72
129
  @property
73
130
  def more(self):
131
+ """Returns `True` if there are more projects to fetch. Returns
132
+ `False` if there are no more projects to fetch.
133
+
134
+ <!-- lazydoc-ignore: internal -->
135
+ """
74
136
  if self.last_response:
75
137
  return self.last_response["models"]["pageInfo"]["hasNextPage"]
76
138
  else:
@@ -78,12 +140,20 @@ class Projects(Paginator["Project"]):
78
140
 
79
141
  @property
80
142
  def cursor(self):
143
+ """Returns the cursor position for pagination of project results.
144
+
145
+ <!-- lazydoc-ignore: internal -->
146
+ """
81
147
  if self.last_response:
82
148
  return self.last_response["models"]["edges"][-1]["cursor"]
83
149
  else:
84
150
  return None
85
151
 
86
152
  def convert_objects(self):
153
+ """Converts GraphQL edges to File objects.
154
+
155
+ <!-- lazydoc-ignore: internal -->
156
+ """
87
157
  return [
88
158
  Project(self.client, self.entity, p["node"]["name"], p["node"])
89
159
  for p in self.last_response["models"]["edges"]
@@ -94,17 +164,22 @@ class Projects(Paginator["Project"]):
94
164
 
95
165
 
96
166
  class Project(Attrs):
97
- """A project is a namespace for runs."""
98
-
99
- QUERY = gql(
100
- """
101
- query Project($project: String!, $entity: String!) {
102
- project(name: $project, entityName: $entity) {
103
- id
104
- }
105
- }
106
- """
107
- )
167
+ """A project is a namespace for runs.
168
+
169
+ Args:
170
+ client: W&B API client instance.
171
+ name (str): The name of the project.
172
+ entity (str): The entity name that owns the project.
173
+ """
174
+
175
+ QUERY = gql(f"""#graphql
176
+ query Project($project: String!, $entity: String!) {{
177
+ project(name: $project, entityName: $entity) {{
178
+ ...ProjectFragment
179
+ }}
180
+ }}
181
+ {PROJECT_FRAGMENT}
182
+ """)
108
183
 
109
184
  def __init__(
110
185
  self,
@@ -122,20 +197,37 @@ class Project(Attrs):
122
197
  attrs: The attributes of the project.
123
198
  """
124
199
  super().__init__(dict(attrs))
200
+ self._is_loaded = bool(attrs)
125
201
  self.client = client
126
202
  self.name = project
127
203
  self.entity = entity
128
204
 
205
+ def _load(self):
206
+ variable_values = {"project": self.name, "entity": self.entity}
207
+ try:
208
+ response = self.client.execute(self.QUERY, variable_values)
209
+ except HTTPError as e:
210
+ raise ValueError(f"Unable to fetch project ID: {variable_values!r}") from e
211
+
212
+ self._attrs = response["project"]
213
+ self._is_loaded = True
214
+
129
215
  @property
130
216
  def path(self):
217
+ """Returns the path of the project. The path is a list containing the
218
+ entity and project name."""
131
219
  return [self.entity, self.name]
132
220
 
133
221
  @property
134
222
  def url(self):
223
+ """Returns the URL of the project."""
135
224
  return self.client.app_url + "/".join(self.path + ["workspace"])
136
225
 
137
226
  def to_html(self, height=420, hidden=False):
138
- """Generate HTML containing an iframe displaying this project."""
227
+ """Generate HTML containing an iframe displaying this project.
228
+
229
+ <!-- lazydoc-ignore: internal -->
230
+ """
139
231
  url = self.url + "?jupyter=true"
140
232
  style = f"border:none;width:100%;height:{height}px;"
141
233
  prefix = ""
@@ -152,74 +244,33 @@ class Project(Attrs):
152
244
 
153
245
  @normalize_exceptions
154
246
  def artifacts_types(self, per_page=50):
247
+ """Returns all artifact types associated with this project."""
155
248
  return public.ArtifactTypes(self.client, self.entity, self.name)
156
249
 
157
250
  @normalize_exceptions
158
- def sweeps(self):
159
- query = gql(
160
- """
161
- query GetSweeps($project: String!, $entity: String!) {{
162
- project(name: $project, entityName: $entity) {{
163
- totalSweeps
164
- sweeps {{
165
- edges {{
166
- node {{
167
- ...SweepFragment
168
- }}
169
- cursor
170
- }}
171
- pageInfo {{
172
- endCursor
173
- hasNextPage
174
- }}
175
- }}
176
- }}
177
- }}
178
- {}
179
- """.format(public.SWEEP_FRAGMENT)
180
- )
181
- variable_values = {"project": self.name, "entity": self.entity}
182
- ret = self.client.execute(query, variable_values)
183
- if not ret.get("project") or ret["project"]["totalSweeps"] < 1:
184
- return []
251
+ def sweeps(self, per_page=50):
252
+ """Return a paginated collection of sweeps in this project.
185
253
 
186
- return [
187
- # match format of existing public sweep apis
188
- public.Sweep(
189
- self.client,
190
- self.entity,
191
- self.name,
192
- e["node"]["name"],
193
- )
194
- for e in ret["project"]["sweeps"]["edges"]
195
- ]
254
+ Args:
255
+ per_page: The number of sweeps to fetch per request to the API.
196
256
 
197
- _PROJECT_ID = gql(
257
+ Returns:
258
+ A `Sweeps` object, which is an iterable collection of `Sweep` objects.
198
259
  """
199
- query ProjectID($projectName: String!, $entityName: String!) {
200
- project(name: $projectName, entityName: $entityName) {
201
- id
202
- }
203
- }
204
- """
205
- )
260
+ return Sweeps(self.client, self.entity, self.name, per_page=per_page)
206
261
 
207
262
  @property
208
263
  def id(self) -> str:
209
- # This is a workaround to ensure that the project ID can be retrieved
210
- # on demand, as it generally is not set or fetched on instantiation.
211
- # This is necessary if using this project as the scope of a new Automation.
212
- with suppress(LookupError):
213
- return self._attrs["id"]
264
+ if not self._is_loaded:
265
+ self._load()
214
266
 
215
- variable_values = {"projectName": self.name, "entityName": self.entity}
216
- try:
217
- data = self.client.execute(self._PROJECT_ID, variable_values)
267
+ if "id" not in self._attrs:
268
+ raise ValueError(f"Project {self.name} not found")
218
269
 
219
- if not data.get("project") or not data["project"].get("id"):
220
- raise ValueError(f"Project {self.name} not found")
270
+ return self._attrs["id"]
221
271
 
222
- self._attrs["id"] = data["project"]["id"]
223
- return self._attrs["id"]
224
- except (HTTPError, LookupError, TypeError) as e:
225
- raise ValueError(f"Unable to fetch project ID: {variable_values!r}") from e
272
+ def __getattr__(self, name: str):
273
+ if not self._is_loaded:
274
+ self._load()
275
+
276
+ return super().__getattr__(name)
@@ -1,5 +1,8 @@
1
1
  class QueryGenerator:
2
- """QueryGenerator is a helper object to write filters for runs."""
2
+ """QueryGenerator is a helper object to write filters for runs.
3
+
4
+ <!-- lazydoc-ignore-class: internal -->
5
+ """
3
6
 
4
7
  INDIVIDUAL_OP_TO_MONGO = {
5
8
  "!=": "$ne",
@@ -21,6 +24,7 @@ class QueryGenerator:
21
24
 
22
25
  @classmethod
23
26
  def format_order_key(cls, key: str):
27
+ """Format a key for sorting."""
24
28
  if key.startswith(("+", "-")):
25
29
  direction = key[0]
26
30
  key = key[1:]
@@ -50,6 +54,7 @@ class QueryGenerator:
50
54
  return {self.INDIVIDUAL_OP_TO_MONGO[op]: value}
51
55
 
52
56
  def key_to_server_path(self, key):
57
+ """Convert a key dictionary to the corresponding server path string."""
53
58
  if key["section"] == "config":
54
59
  return "config." + key["name"]
55
60
  elif key["section"] == "summary":
@@ -63,6 +68,7 @@ class QueryGenerator:
63
68
  raise ValueError("Invalid key: {}".format(key))
64
69
 
65
70
  def server_path_to_key(self, path):
71
+ """Convert a server path string to the corresponding key dictionary."""
66
72
  if path.startswith("config."):
67
73
  return {"section": "config", "name": path.split("config.", 1)[1]}
68
74
  elif path.startswith("summary_metrics."):
@@ -75,6 +81,7 @@ class QueryGenerator:
75
81
  return {"section": "run", "name": path}
76
82
 
77
83
  def keys_to_order(self, keys):
84
+ """Convert a list of key dictionaries to an order string."""
78
85
  orders = []
79
86
  for key in keys["keys"]:
80
87
  order = self.key_to_server_path(key["key"])
@@ -87,6 +94,7 @@ class QueryGenerator:
87
94
  return orders
88
95
 
89
96
  def order_to_keys(self, order):
97
+ """Convert an order string to a list of key dictionaries."""
90
98
  keys = []
91
99
  for k in order: # orderstr.split(","):
92
100
  name = k[1:]
@@ -127,6 +135,7 @@ class QueryGenerator:
127
135
  return {path: self._to_mongo_op_value(filter["op"], filter["value"])}
128
136
 
129
137
  def filter_to_mongo(self, filter):
138
+ """Returns dictionary with filter format converted to MongoDB filter."""
130
139
  if self._is_individual(filter):
131
140
  return self._to_mongo_individual(filter)
132
141
  elif self._is_group(filter):
@@ -137,6 +146,7 @@ class QueryGenerator:
137
146
  }
138
147
 
139
148
  def mongo_to_filter(self, filter):
149
+ """Returns dictionary with MongoDB filter converted to filter format."""
140
150
  # Returns {"op": "OR", "filters": [{"op": "AND", "filters": []}]}
141
151
  if filter is None:
142
152
  return None # this covers the case where self.filter_to_mongo returns None.
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from enum import Enum
2
4
  from functools import lru_cache
3
- from typing import TYPE_CHECKING, Any, List, Literal, Mapping, Optional, Sequence
5
+ from typing import TYPE_CHECKING, Any, Literal, Mapping, Sequence
4
6
 
5
7
  from wandb.sdk.artifacts._validators import (
6
8
  REGISTRY_PREFIX,
@@ -21,15 +23,12 @@ class Visibility(str, Enum):
21
23
 
22
24
  @classmethod
23
25
  def _missing_(cls, value: object) -> Any:
24
- return next(
25
- (e for e in cls if e.name == value),
26
- None,
27
- )
26
+ return next((e for e in cls if e.name == value), None)
28
27
 
29
28
 
30
29
  def format_gql_artifact_types_input(
31
- artifact_types: Optional[List[str]] = None,
32
- ):
30
+ artifact_types: list[str] | None,
31
+ ) -> list[dict[str, str]]:
33
32
  """Format the artifact types for the GQL input.
34
33
 
35
34
  Args:
@@ -40,8 +39,7 @@ def format_gql_artifact_types_input(
40
39
  """
41
40
  if artifact_types is None:
42
41
  return []
43
- new_types = validate_artifact_types_list(artifact_types)
44
- return [{"name": type} for type in new_types]
42
+ return [{"name": typ} for typ in validate_artifact_types_list(artifact_types)]
45
43
 
46
44
 
47
45
  def gql_to_registry_visibility(
@@ -67,14 +65,14 @@ def registry_visibility_to_gql(
67
65
  """Convert the registry visibility to the GQL visibility."""
68
66
  try:
69
67
  return Visibility[visibility].value
70
- except KeyError:
68
+ except LookupError:
69
+ allowed_str = ", ".join(map(repr, (e.name for e in Visibility)))
71
70
  raise ValueError(
72
- f"Invalid visibility: {visibility!r}. "
73
- f"Must be one of: {', '.join(map(repr, (e.name for e in Visibility)))}"
71
+ f"Invalid visibility: {visibility!r}. Must be one of: {allowed_str}"
74
72
  )
75
73
 
76
74
 
77
- def ensure_registry_prefix_on_names(query, in_name=False):
75
+ def ensure_registry_prefix_on_names(query: Any, in_name: bool = False) -> Any:
78
76
  """Traverse the filter to prepend the `name` key value with the registry prefix unless the value is a regex.
79
77
 
80
78
  - in_name: True if we are under a "name" key (or propagating from one).
@@ -105,7 +103,7 @@ def ensure_registry_prefix_on_names(query, in_name=False):
105
103
 
106
104
 
107
105
  @lru_cache(maxsize=10)
108
- def fetch_org_entity_from_organization(client: "Client", organization: str) -> str:
106
+ def fetch_org_entity_from_organization(client: Client, organization: str) -> str:
109
107
  """Fetch the org entity from the organization.
110
108
 
111
109
  Args:
@@ -127,7 +125,7 @@ def fetch_org_entity_from_organization(client: "Client", organization: str) -> s
127
125
  response = client.execute(query, variable_values={"organization": organization})
128
126
  except Exception as e:
129
127
  raise ValueError(
130
- f"Error fetching org entity for organization: {organization}"
128
+ f"Error fetching org entity for organization: {organization!r}"
131
129
  ) from e
132
130
 
133
131
  if (
@@ -135,6 +133,6 @@ def fetch_org_entity_from_organization(client: "Client", organization: str) -> s
135
133
  or not (org_entity := org["orgEntity"])
136
134
  or not (org_name := org_entity["name"])
137
135
  ):
138
- raise ValueError(f"Organization entity for {organization} not found.")
136
+ raise ValueError(f"Organization entity for {organization!r} not found.")
139
137
 
140
138
  return org_name