wandb 0.19.6__py3-none-win32.whl → 0.19.7__py3-none-win32.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 +25 -5
  3. wandb/apis/public/_generated/__init__.py +21 -0
  4. wandb/apis/public/_generated/base.py +128 -0
  5. wandb/apis/public/_generated/enums.py +4 -0
  6. wandb/apis/public/_generated/input_types.py +4 -0
  7. wandb/apis/public/_generated/operations.py +15 -0
  8. wandb/apis/public/_generated/server_features_query.py +27 -0
  9. wandb/apis/public/_generated/typing_compat.py +14 -0
  10. wandb/apis/public/api.py +192 -6
  11. wandb/apis/public/artifacts.py +13 -45
  12. wandb/apis/public/registries.py +573 -0
  13. wandb/apis/public/utils.py +36 -0
  14. wandb/bin/gpu_stats.exe +0 -0
  15. wandb/bin/wandb-core +0 -0
  16. wandb/cli/cli.py +11 -20
  17. wandb/env.py +10 -0
  18. wandb/proto/v3/wandb_internal_pb2.py +243 -222
  19. wandb/proto/v3/wandb_server_pb2.py +4 -4
  20. wandb/proto/v3/wandb_settings_pb2.py +1 -1
  21. wandb/proto/v4/wandb_internal_pb2.py +226 -222
  22. wandb/proto/v4/wandb_server_pb2.py +4 -4
  23. wandb/proto/v4/wandb_settings_pb2.py +1 -1
  24. wandb/proto/v5/wandb_internal_pb2.py +226 -222
  25. wandb/proto/v5/wandb_server_pb2.py +4 -4
  26. wandb/proto/v5/wandb_settings_pb2.py +1 -1
  27. wandb/sdk/artifacts/_graphql_fragments.py +126 -0
  28. wandb/sdk/artifacts/artifact.py +43 -88
  29. wandb/sdk/backend/backend.py +1 -1
  30. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +14 -6
  31. wandb/sdk/data_types/helper_types/image_mask.py +12 -6
  32. wandb/sdk/data_types/saved_model.py +35 -46
  33. wandb/sdk/data_types/video.py +7 -16
  34. wandb/sdk/interface/interface.py +26 -10
  35. wandb/sdk/interface/interface_queue.py +5 -8
  36. wandb/sdk/interface/interface_relay.py +1 -6
  37. wandb/sdk/interface/interface_shared.py +21 -99
  38. wandb/sdk/interface/interface_sock.py +2 -13
  39. wandb/sdk/interface/router.py +21 -15
  40. wandb/sdk/interface/router_queue.py +2 -1
  41. wandb/sdk/interface/router_relay.py +2 -1
  42. wandb/sdk/interface/router_sock.py +5 -4
  43. wandb/sdk/internal/handler.py +4 -3
  44. wandb/sdk/internal/internal_api.py +12 -1
  45. wandb/sdk/internal/sender.py +0 -18
  46. wandb/sdk/lib/apikey.py +87 -26
  47. wandb/sdk/lib/asyncio_compat.py +210 -0
  48. wandb/sdk/lib/progress.py +78 -16
  49. wandb/sdk/lib/service_connection.py +1 -1
  50. wandb/sdk/lib/sock_client.py +7 -7
  51. wandb/sdk/mailbox/__init__.py +23 -0
  52. wandb/sdk/mailbox/handles.py +199 -0
  53. wandb/sdk/mailbox/mailbox.py +121 -0
  54. wandb/sdk/mailbox/wait_with_progress.py +134 -0
  55. wandb/sdk/service/server_sock.py +5 -1
  56. wandb/sdk/service/streams.py +66 -74
  57. wandb/sdk/verify/verify.py +54 -2
  58. wandb/sdk/wandb_init.py +61 -61
  59. wandb/sdk/wandb_login.py +7 -4
  60. wandb/sdk/wandb_metadata.py +65 -34
  61. wandb/sdk/wandb_require.py +14 -8
  62. wandb/sdk/wandb_run.py +82 -87
  63. wandb/sdk/wandb_settings.py +3 -3
  64. wandb/sdk/wandb_setup.py +19 -8
  65. wandb/sdk/wandb_sync.py +2 -4
  66. wandb/util.py +3 -1
  67. {wandb-0.19.6.dist-info → wandb-0.19.7.dist-info}/METADATA +2 -2
  68. {wandb-0.19.6.dist-info → wandb-0.19.7.dist-info}/RECORD +71 -58
  69. wandb/sdk/lib/mailbox.py +0 -442
  70. {wandb-0.19.6.dist-info → wandb-0.19.7.dist-info}/WHEEL +0 -0
  71. {wandb-0.19.6.dist-info → wandb-0.19.7.dist-info}/entry_points.txt +0 -0
  72. {wandb-0.19.6.dist-info → wandb-0.19.7.dist-info}/licenses/LICENSE +0 -0
@@ -236,39 +236,6 @@ class Metadata(BaseModel, validate_assignment=True):
236
236
 
237
237
  NOTE: Definitions must be kept in sync with wandb_internal.proto::MetadataRequest.
238
238
 
239
- Attributes:
240
- os: Operating system.
241
- python: Python version.
242
- heartbeat_at: Timestamp of last heartbeat.
243
- started_at: Timestamp of run start.
244
- docker: Docker image.
245
- cuda: CUDA version.
246
- args: Command-line arguments.
247
- state: Run state.
248
- program: Program name.
249
- code_path: Path to code.
250
- git: Git repository information.
251
- email: Email address.
252
- root: Root directory.
253
- host: Host name.
254
- username: Username.
255
- executable: Python executable path.
256
- code_path_local: Local code path.
257
- colab: Colab URL.
258
- cpu_count: CPU count.
259
- cpu_count_logical: Logical CPU count.
260
- gpu_type: GPU type.
261
- disk: Disk information.
262
- memory: Memory information.
263
- cpu: CPU information.
264
- apple: Apple silicon information.
265
- gpu_nvidia: NVIDIA GPU information.
266
- gpu_amd: AMD GPU information.
267
- slurm: Slurm environment information.
268
- cuda_version: CUDA version.
269
- trainium: Trainium information.
270
- tpu: TPU information.
271
-
272
239
  Examples:
273
240
  Update Run metadata:
274
241
 
@@ -296,44 +263,108 @@ class Metadata(BaseModel, validate_assignment=True):
296
263
  ```
297
264
  """
298
265
 
299
- # TODO: Pydantic configuration.
300
266
  model_config = ConfigDict(
301
267
  extra="ignore", # ignore extra fields
302
268
  validate_default=True, # validate default values
269
+ use_attribute_docstrings=True, # for field descriptions
270
+ revalidate_instances="always",
303
271
  )
304
272
 
305
273
  os: str | None = None
274
+ """Operating system."""
275
+
306
276
  python: str | None = None
277
+ """Python version."""
278
+
307
279
  heartbeat_at: datetime | None = Field(default=None, alias="heartbeatAt")
280
+ """Timestamp of last heartbeat."""
281
+
308
282
  started_at: datetime | None = Field(default=None, alias="startedAt")
283
+ """Timestamp of run start."""
284
+
309
285
  docker: str | None = None
286
+ """Docker image."""
287
+
310
288
  cuda: str | None = None
289
+ """CUDA version."""
290
+
311
291
  args: list[str] = Field(default_factory=list)
292
+ """Command-line arguments."""
293
+
312
294
  state: str | None = None
295
+ """Run state."""
296
+
313
297
  program: str | None = None
298
+ """Program name."""
299
+
314
300
  code_path: str | None = Field(default=None, alias="codePath")
301
+ """Path to code."""
302
+
315
303
  git: GitRepoRecord | None = None
304
+ """Git repository information."""
305
+
316
306
  email: str | None = None
307
+ """Email address."""
308
+
317
309
  root: str | None = None
310
+ """Root directory."""
311
+
318
312
  host: str | None = None
313
+ """Host name."""
314
+
319
315
  username: str | None = None
316
+ """Username."""
317
+
320
318
  executable: str | None = None
319
+ """Python executable path."""
320
+
321
321
  code_path_local: str | None = Field(default=None, alias="codePathLocal")
322
+ """Local code path."""
323
+
322
324
  colab: str | None = None
325
+ """Colab URL."""
326
+
323
327
  cpu_count: int | None = Field(default=None, alias="cpuCount")
328
+ """CPU count."""
329
+
324
330
  cpu_count_logical: int | None = Field(default=None, alias="cpuCountLogical")
331
+ """Logical CPU count."""
332
+
325
333
  gpu_type: str | None = Field(default=None, alias="gpuType")
334
+ """GPU type."""
335
+
326
336
  gpu_count: int | None = Field(default=None, alias="gpuCount")
337
+ """GPU count."""
338
+
327
339
  disk: dict[str, DiskInfo] = Field(default_factory=dict)
340
+ """Disk information."""
341
+
328
342
  memory: MemoryInfo | None = None
343
+ """Memory information."""
344
+
329
345
  cpu: CpuInfo | None = None
346
+ """CPU information."""
347
+
330
348
  apple: AppleInfo | None = None
349
+ """Apple silicon information."""
350
+
331
351
  gpu_nvidia: list[GpuNvidiaInfo] = Field(default_factory=list, alias="gpuNvidia")
352
+ """NVIDIA GPU information."""
353
+
332
354
  gpu_amd: list[GpuAmdInfo] = Field(default_factory=list, alias="gpuAmd")
355
+ """AMD GPU information."""
356
+
333
357
  slurm: dict[str, str] = Field(default_factory=dict)
358
+ """Slurm environment information."""
359
+
334
360
  cuda_version: str | None = Field(default=None, alias="cudaVersion")
361
+ """CUDA version."""
362
+
335
363
  trainium: TrainiumInfo | None = None
364
+ """Trainium information."""
365
+
336
366
  tpu: TPUInfo | None = None
367
+ """TPU information."""
337
368
 
338
369
  def __init__(self, **data):
339
370
  super().__init__(**data)
@@ -9,8 +9,10 @@ Example:
9
9
  wandb.require("incremental-artifacts@beta")
10
10
  """
11
11
 
12
+ from __future__ import annotations
13
+
12
14
  import os
13
- from typing import Optional, Sequence, Union
15
+ from typing import Iterable
14
16
 
15
17
  import wandb
16
18
  from wandb.env import _REQUIRE_LEGACY_SERVICE
@@ -21,9 +23,9 @@ from wandb.sdk import wandb_run
21
23
  class _Requires:
22
24
  """Internal feature class."""
23
25
 
24
- _features: Sequence[str]
26
+ _features: tuple[str, ...]
25
27
 
26
- def __init__(self, features: Union[str, Sequence[str]]) -> None:
28
+ def __init__(self, features: str | Iterable[str]) -> None:
27
29
  self._features = (
28
30
  tuple([features]) if isinstance(features, str) else tuple(features)
29
31
  )
@@ -67,17 +69,21 @@ class _Requires:
67
69
 
68
70
 
69
71
  def require(
70
- requirement: Optional[Union[str, Sequence[str]]] = None,
71
- experiment: Optional[Union[str, Sequence[str]]] = None,
72
+ requirement: str | Iterable[str] | None = None,
73
+ experiment: str | Iterable[str] | None = None,
72
74
  ) -> None:
73
75
  """Indicate which experimental features are used by the script.
74
76
 
77
+ This should be called before any other `wandb` functions, ideally right
78
+ after importing `wandb`.
79
+
75
80
  Args:
76
- requirement: (str or list) Features to require
77
- experiment: (str or list) Features to require
81
+ requirement: The name of a feature to require or an iterable of
82
+ feature names.
83
+ experiment: An alias for `requirement`.
78
84
 
79
85
  Raises:
80
- wandb.errors.UnsupportedError: if not supported
86
+ wandb.errors.UnsupportedError: If a feature name is unknown.
81
87
  """
82
88
  features = requirement or experiment
83
89
  if not features:
wandb/sdk/wandb_run.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import atexit
4
5
  import functools
5
6
  import glob
@@ -43,6 +44,7 @@ from wandb.proto.wandb_internal_pb2 import (
43
44
  )
44
45
  from wandb.sdk.artifacts.artifact import Artifact
45
46
  from wandb.sdk.internal import job_builder
47
+ from wandb.sdk.lib import asyncio_compat
46
48
  from wandb.sdk.lib.import_hooks import (
47
49
  register_post_import_hook,
48
50
  unregister_post_import_hook,
@@ -82,7 +84,12 @@ from .lib import (
82
84
  telemetry,
83
85
  )
84
86
  from .lib.exit_hooks import ExitHooks
85
- from .lib.mailbox import MailboxError, MailboxHandle, MailboxProbe, MailboxProgress
87
+ from .mailbox import (
88
+ HandleAbandonedError,
89
+ MailboxClosedError,
90
+ MailboxHandle,
91
+ wait_with_progress,
92
+ )
86
93
  from .wandb_alerts import AlertLevel
87
94
  from .wandb_metadata import Metadata
88
95
  from .wandb_settings import Settings
@@ -222,30 +229,31 @@ class RunStatusChecker:
222
229
  while not join_requested:
223
230
  time_probe = time.monotonic()
224
231
  if not local_handle:
225
- local_handle = request()
232
+ try:
233
+ local_handle = request()
234
+ except MailboxClosedError:
235
+ # This can happen if the service process dies.
236
+ break
226
237
  assert local_handle
227
238
 
228
239
  with lock:
229
240
  if self._join_event.is_set():
230
241
  break
231
242
  set_handle(local_handle)
243
+
232
244
  try:
233
- result = local_handle.wait(timeout=timeout, release=False)
234
- except MailboxError:
235
- # background threads are oportunistically getting results
236
- # from the internal process but the internal process could
237
- # be shutdown at any time. In this case assume that the
238
- # thread should exit silently. This is possible
239
- # because we do not have an atexit handler for the user
240
- # process which quiesces active threads.
245
+ result = local_handle.wait_or(timeout=timeout)
246
+ except HandleAbandonedError:
247
+ # This can happen if the service process dies.
241
248
  break
249
+ except TimeoutError:
250
+ result = None
251
+
242
252
  with lock:
243
253
  set_handle(None)
244
254
 
245
255
  if result:
246
256
  process(result)
247
- # if request finished, clear the handle to send on the next interval
248
- local_handle.abandon()
249
257
  local_handle = None
250
258
 
251
259
  time_elapsed = time.monotonic() - time_probe
@@ -1300,12 +1308,6 @@ class Run:
1300
1308
  if self._backend and self._backend.interface:
1301
1309
  self._backend.interface.publish_summary(self, summary_record)
1302
1310
 
1303
- def _on_progress_get_summary(self, handle: MailboxProgress) -> None:
1304
- pass
1305
- # TODO(jhr): enable printing for get_summary in later mailbox dev phase
1306
- # line = "Waiting for run.summary data..."
1307
- # self._printer.display(line)
1308
-
1309
1311
  def _summary_get_current_summary_callback(self) -> dict[str, Any]:
1310
1312
  if self._is_finished:
1311
1313
  # TODO: WB-18420: fetch summary from backend and stage it before run is finished
@@ -1314,12 +1316,12 @@ class Run:
1314
1316
  if not self._backend or not self._backend.interface:
1315
1317
  return {}
1316
1318
  handle = self._backend.interface.deliver_get_summary()
1317
- result = handle.wait(
1318
- timeout=self._settings.summary_timeout,
1319
- on_progress=self._on_progress_get_summary,
1320
- )
1321
- if not result:
1319
+
1320
+ try:
1321
+ result = handle.wait_or(timeout=self._settings.summary_timeout)
1322
+ except TimeoutError:
1322
1323
  return {}
1324
+
1323
1325
  get_summary_response = result.response.get_summary_response
1324
1326
  return proto_util.dict_from_proto_list(get_summary_response.item)
1325
1327
 
@@ -1503,7 +1505,7 @@ class Run:
1503
1505
  if run_obj.notes:
1504
1506
  self._settings.run_notes = run_obj.notes
1505
1507
  if run_obj.tags:
1506
- self._settings.run_tags = run_obj.tags
1508
+ self._settings.run_tags = tuple(run_obj.tags)
1507
1509
  if run_obj.sweep_id:
1508
1510
  self._settings.sweep_id = run_obj.sweep_id
1509
1511
  if run_obj.host:
@@ -2111,15 +2113,6 @@ class Run:
2111
2113
  with telemetry.context(run=self) as tel:
2112
2114
  tel.feature.finish = True
2113
2115
 
2114
- # Pop this run (hopefully) from the run stack, to support the "reinit"
2115
- # functionality of wandb.init().
2116
- #
2117
- # TODO: It's not clear how _global_run_stack could have length other
2118
- # than 1 at this point in the code. If you're reading this, consider
2119
- # refactoring this thing.
2120
- if self._wl and len(self._wl._global_run_stack) > 0:
2121
- self._wl._global_run_stack.pop()
2122
-
2123
2116
  # Run hooks that need to happen before the last messages to the
2124
2117
  # internal service, like Jupyter hooks.
2125
2118
  for hook in self._teardown_hooks:
@@ -2175,8 +2168,7 @@ class Run:
2175
2168
  return RunStatus()
2176
2169
 
2177
2170
  handle_run_status = self._backend.interface.deliver_request_run_status()
2178
- result = handle_run_status.wait(timeout=-1)
2179
- assert result
2171
+ result = handle_run_status.wait_or(timeout=None)
2180
2172
  sync_data = result.response.run_status_response
2181
2173
 
2182
2174
  sync_time = None
@@ -2563,31 +2555,38 @@ class Run:
2563
2555
  else:
2564
2556
  return artifact
2565
2557
 
2566
- def _on_probe_exit(self, probe_handle: MailboxProbe) -> None:
2567
- handle = probe_handle.get_mailbox_handle()
2568
- if handle:
2569
- result = handle.wait(timeout=0, release=False)
2570
- if not result:
2571
- return
2572
- probe_handle.set_probe_result(result)
2573
- assert self._backend and self._backend.interface
2574
- handle = self._backend.interface.deliver_poll_exit()
2575
- probe_handle.set_mailbox_handle(handle)
2576
-
2577
- def _on_progress_exit(
2558
+ async def _display_finish_stats(
2578
2559
  self,
2579
2560
  progress_printer: progress.ProgressPrinter,
2580
- progress_handle: MailboxProgress,
2581
2561
  ) -> None:
2582
- probe_handles = progress_handle.get_probe_handles()
2583
- if not probe_handles or len(probe_handles) != 1:
2584
- return
2562
+ last_result: Result | None = None
2585
2563
 
2586
- result = probe_handles[0].get_probe_result()
2587
- if not result:
2588
- return
2564
+ async def loop_update_printer() -> None:
2565
+ while True:
2566
+ if last_result:
2567
+ progress_printer.update(
2568
+ [last_result.response.poll_exit_response],
2569
+ )
2570
+ await asyncio.sleep(0.1)
2571
+
2572
+ async def loop_poll_exit() -> None:
2573
+ nonlocal last_result
2574
+ assert self._backend and self._backend.interface
2575
+
2576
+ while True:
2577
+ handle = self._backend.interface.deliver_poll_exit()
2578
+
2579
+ time_start = time.monotonic()
2580
+ last_result = await handle.wait_async(timeout=None)
2589
2581
 
2590
- progress_printer.update([result.response.poll_exit_response])
2582
+ # Update at most once a second.
2583
+ time_elapsed = time.monotonic() - time_start
2584
+ if time_elapsed < 1:
2585
+ await asyncio.sleep(1 - time_elapsed)
2586
+
2587
+ async with asyncio_compat.open_task_group() as task_group:
2588
+ task_group.start_soon(loop_update_printer())
2589
+ task_group.start_soon(loop_poll_exit())
2591
2590
 
2592
2591
  def _on_finish(self) -> None:
2593
2592
  trigger.call("on_finished")
@@ -2604,25 +2603,24 @@ class Run:
2604
2603
  else:
2605
2604
  exit_handle = self._backend.interface.deliver_finish_without_exit()
2606
2605
 
2607
- exit_handle.add_probe(on_probe=self._on_probe_exit)
2608
-
2609
2606
  with progress.progress_printer(
2610
2607
  self._printer,
2611
- self._settings,
2608
+ default_text="Finishing up...",
2612
2609
  ) as progress_printer:
2613
2610
  # Wait for the run to complete.
2614
- _ = exit_handle.wait(
2615
- timeout=-1,
2616
- on_progress=functools.partial(
2617
- self._on_progress_exit,
2611
+ wait_with_progress(
2612
+ exit_handle,
2613
+ timeout=None,
2614
+ progress_after=1,
2615
+ display_progress=functools.partial(
2616
+ self._display_finish_stats,
2618
2617
  progress_printer,
2619
2618
  ),
2620
2619
  )
2621
2620
 
2622
2621
  # Print some final statistics.
2623
2622
  poll_exit_handle = self._backend.interface.deliver_poll_exit()
2624
- result = poll_exit_handle.wait(timeout=-1)
2625
- assert result
2623
+ result = poll_exit_handle.wait_or(timeout=None)
2626
2624
  progress.print_sync_dedupe_stats(
2627
2625
  self._printer,
2628
2626
  result.response.poll_exit_response,
@@ -2630,8 +2628,7 @@ class Run:
2630
2628
 
2631
2629
  self._poll_exit_response = result.response.poll_exit_response
2632
2630
  internal_messages_handle = self._backend.interface.deliver_internal_messages()
2633
- result = internal_messages_handle.wait(timeout=-1)
2634
- assert result
2631
+ result = internal_messages_handle.wait_or(timeout=None)
2635
2632
  self._internal_messages_response = result.response.internal_messages_response
2636
2633
 
2637
2634
  # dispatch all our final requests
@@ -2641,12 +2638,10 @@ class Run:
2641
2638
  self._backend.interface.deliver_request_sampled_history()
2642
2639
  )
2643
2640
 
2644
- result = sampled_history_handle.wait(timeout=-1)
2645
- assert result
2641
+ result = sampled_history_handle.wait_or(timeout=None)
2646
2642
  self._sampled_history = result.response.sampled_history_response
2647
2643
 
2648
- result = final_summary_handle.wait(timeout=-1)
2649
- assert result
2644
+ result = final_summary_handle.wait_or(timeout=None)
2650
2645
  self._final_summary = result.response.get_summary_response
2651
2646
 
2652
2647
  if self._backend:
@@ -2952,13 +2947,10 @@ class Run:
2952
2947
  wandb.termwarn(
2953
2948
  "Artifact TTL will be disabled for source artifacts that are linked to portfolios."
2954
2949
  )
2955
- result = handle.wait(timeout=-1)
2956
- if result is None:
2957
- handle.abandon()
2958
- else:
2959
- response = result.response.link_artifact_response
2960
- if response.error_message:
2961
- wandb.termerror(response.error_message)
2950
+ result = handle.wait_or(timeout=None)
2951
+ response = result.response.link_artifact_response
2952
+ if response.error_message:
2953
+ wandb.termerror(response.error_message)
2962
2954
 
2963
2955
  @_run_decorator._noop_on_finish()
2964
2956
  @_run_decorator._attach
@@ -3258,7 +3250,7 @@ class Run:
3258
3250
  self._assert_can_log_artifact(artifact)
3259
3251
  if self._backend and self._backend.interface:
3260
3252
  if not self._settings._offline:
3261
- future = self._backend.interface.communicate_artifact(
3253
+ handle = self._backend.interface.deliver_artifact(
3262
3254
  self,
3263
3255
  artifact,
3264
3256
  aliases,
@@ -3268,7 +3260,7 @@ class Run:
3268
3260
  is_user_created=is_user_created,
3269
3261
  use_after_commit=use_after_commit,
3270
3262
  )
3271
- artifact._set_save_future(future, self._public_api().client)
3263
+ artifact._set_save_handle(handle, self._public_api().client)
3272
3264
  else:
3273
3265
  self._backend.interface.publish_artifact(
3274
3266
  self,
@@ -3670,16 +3662,18 @@ class Run:
3670
3662
  return {}
3671
3663
 
3672
3664
  handle = self._backend.interface.deliver_get_system_metrics()
3673
- result = handle.wait(timeout=1)
3674
3665
 
3675
- if result:
3666
+ try:
3667
+ result = handle.wait_or(timeout=1)
3668
+ except TimeoutError:
3669
+ return {}
3670
+ else:
3676
3671
  try:
3677
3672
  response = result.response.get_system_metrics_response
3678
- if response:
3679
- return pb_to_dict(response)
3673
+ return pb_to_dict(response) if response else {}
3680
3674
  except Exception as e:
3681
3675
  logger.error("Error getting system metrics: %s", e)
3682
- return {}
3676
+ return {}
3683
3677
 
3684
3678
  @property
3685
3679
  @_run_decorator._attach
@@ -3698,10 +3692,11 @@ class Run:
3698
3692
  self.__metadata._set_callback(self._metadata_callback)
3699
3693
 
3700
3694
  handle = self._backend.interface.deliver_get_system_metadata()
3701
- result = handle.wait(timeout=1)
3702
3695
 
3703
- if not result:
3704
- logger.error("Error getting run metadata: no result")
3696
+ try:
3697
+ result = handle.wait_or(timeout=1)
3698
+ except TimeoutError:
3699
+ logger.error("Error getting run metadata: timeout")
3705
3700
  return None
3706
3701
 
3707
3702
  try:
@@ -165,6 +165,9 @@ class Settings(BaseModel, validate_assignment=True):
165
165
  entity: str | None = None
166
166
  """The W&B entity, such as a user or a team."""
167
167
 
168
+ organization: str | None = None
169
+ """The W&B organization."""
170
+
168
171
  force: bool = False
169
172
  """Whether to pass the `force` flag to `wandb.login()`."""
170
173
 
@@ -564,9 +567,6 @@ class Settings(BaseModel, validate_assignment=True):
564
567
  x_service_wait: float = 30.0
565
568
  """Time in seconds to wait for the wandb-core internal service to start."""
566
569
 
567
- x_show_operation_stats: bool = True
568
- """Whether to show statistics about internal operations such as data uploads."""
569
-
570
570
  x_start_time: float | None = None
571
571
  """The start time of the run in seconds since the Unix epoch."""
572
572
 
wandb/sdk/wandb_setup.py CHANGED
@@ -28,7 +28,6 @@ from .lib import config_util, server
28
28
 
29
29
  if TYPE_CHECKING:
30
30
  from wandb.sdk.lib.service_connection import ServiceConnection
31
- from wandb.sdk.wandb_run import Run
32
31
  from wandb.sdk.wandb_settings import Settings
33
32
 
34
33
 
@@ -90,9 +89,6 @@ class _WandbSetup:
90
89
  self._server: server.Server | None = None
91
90
  self._pid = pid
92
91
 
93
- # keep track of multiple runs, so we can unwind with join()s
94
- self._global_run_stack: list[Run] = []
95
-
96
92
  # TODO(jhr): defer strict checks until settings are fully initialized
97
93
  # and logging is ready
98
94
  self._logger: Logger = _EarlyLogger()
@@ -298,18 +294,33 @@ def singleton() -> _WandbSetup | None:
298
294
  return None
299
295
 
300
296
 
301
- def _setup(settings: Settings | None = None) -> _WandbSetup:
302
- """Set up library context."""
297
+ def _setup(
298
+ settings: Settings | None = None,
299
+ start_service: bool = True,
300
+ ) -> _WandbSetup:
301
+ """Set up library context.
302
+
303
+ Args:
304
+ settings: Global settings to set, or updates to the global settings
305
+ if the singleton has already been initialized.
306
+ start_service: Whether to start up the service process.
307
+ NOTE: A service process will only be started if allowed by the
308
+ global settings (after the given updates). The service will not
309
+ start up if the mode resolves to "disabled".
310
+ """
303
311
  global _singleton
304
312
 
305
313
  pid = os.getpid()
306
314
 
307
315
  if _singleton and _singleton._pid == pid:
308
316
  _singleton._update(settings=settings)
309
- return _singleton
310
317
  else:
311
318
  _singleton = _WandbSetup(settings=settings, pid=pid)
312
- return _singleton
319
+
320
+ if start_service and not _singleton.settings._noop:
321
+ _singleton.ensure_service()
322
+
323
+ return _singleton
313
324
 
314
325
 
315
326
  def setup(settings: Settings | None = None) -> _WandbSetup:
wandb/sdk/wandb_sync.py CHANGED
@@ -6,8 +6,8 @@ from wandb.errors.term import termerror, termlog
6
6
 
7
7
  from . import wandb_setup
8
8
  from .backend.backend import Backend
9
- from .lib.mailbox import Mailbox
10
9
  from .lib.runid import generate_id
10
+ from .mailbox import Mailbox
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from wandb.proto import wandb_internal_pb2
@@ -60,10 +60,8 @@ def _sync(
60
60
  assert backend.interface
61
61
  backend.interface._stream_id = stream_id # type: ignore
62
62
 
63
- mailbox.enable_keepalive()
64
63
  handle = backend.interface.deliver_finish_sync()
65
- result = handle.wait(timeout=-1)
66
- assert result and result.response
64
+ result = handle.wait_or(timeout=None)
67
65
  response = result.response.sync_response
68
66
  if response.url:
69
67
  termlog(f"Synced {p} to {util.app_url(response.url)}")
wandb/util.py CHANGED
@@ -788,6 +788,8 @@ class JSONEncoderUncompressed(json.JSONEncoder):
788
788
  def default(self, obj: Any) -> Any:
789
789
  if is_numpy_array(obj):
790
790
  return obj.tolist()
791
+ elif np and isinstance(obj, np.number):
792
+ return obj.item()
791
793
  elif np and isinstance(obj, np.generic):
792
794
  obj = obj.item()
793
795
  return json.JSONEncoder.default(self, obj)
@@ -872,7 +874,7 @@ def parse_backend_error_messages(response: requests.Response) -> List[str]:
872
874
  # Backend error values are returned in one of two ways:
873
875
  # - A string containing the error message
874
876
  # - A JSON object with a "message" field that is a string
875
- def get_message(err: Any) -> Optional[str]:
877
+ def get_message(error: Any) -> Optional[str]:
876
878
  if isinstance(error, str):
877
879
  return error
878
880
  elif (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wandb
3
- Version: 0.19.6
3
+ Version: 0.19.7
4
4
  Summary: A CLI and library for interacting with the Weights & Biases API.
5
5
  Project-URL: Source, https://github.com/wandb/wandb
6
6
  Project-URL: Bug Reports, https://github.com/wandb/wandb/issues
@@ -110,7 +110,7 @@ Requires-Dist: typing-extensions; extra == 'launch'
110
110
  Provides-Extra: media
111
111
  Requires-Dist: bokeh; extra == 'media'
112
112
  Requires-Dist: imageio; extra == 'media'
113
- Requires-Dist: moviepy; extra == 'media'
113
+ Requires-Dist: moviepy>=1.0.0; extra == 'media'
114
114
  Requires-Dist: numpy; extra == 'media'
115
115
  Requires-Dist: pillow; extra == 'media'
116
116
  Requires-Dist: plotly>=5.18.0; extra == 'media'