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
@@ -7,6 +7,7 @@ import threading
7
7
 
8
8
  from wandb.proto import wandb_internal_pb2 as pb
9
9
  from wandb.proto import wandb_server_pb2 as spb
10
+ from wandb.sdk.lib import asyncio_manager
10
11
 
11
12
  from .mailbox_handle import MailboxHandle
12
13
  from .response_handle import MailboxResponseHandle
@@ -27,7 +28,8 @@ class Mailbox:
27
28
  service process becomes unreachable.
28
29
  """
29
30
 
30
- def __init__(self) -> None:
31
+ def __init__(self, asyncer: asyncio_manager.AsyncioManager) -> None:
32
+ self._asyncer = asyncer
31
33
  self._handles: dict[str, MailboxResponseHandle] = {}
32
34
  self._handles_lock = threading.Lock()
33
35
  self._closed = False
@@ -51,11 +53,17 @@ class Mailbox:
51
53
  cannot be created.
52
54
  """
53
55
  if isinstance(request, spb.ServerRequest):
54
- if address := request.request_id:
56
+ if (address := request.request_id) or (
57
+ address := request.record_publish.control.mailbox_slot
58
+ ):
55
59
  raise ValueError(f"Request already has an address ({address})")
56
60
 
57
61
  address = self._new_address()
58
62
  request.request_id = address
63
+ if request.HasField("record_publish"):
64
+ request.record_publish.control.mailbox_slot = address
65
+ if request.HasField("record_communicate"):
66
+ request.record_communicate.control.mailbox_slot = address
59
67
  else:
60
68
  if address := request.control.mailbox_slot:
61
69
  raise ValueError(f"Request already has an address ({address})")
@@ -67,7 +75,7 @@ class Mailbox:
67
75
  if self._closed:
68
76
  raise MailboxClosedError()
69
77
 
70
- handle = MailboxResponseHandle(address)
78
+ handle = MailboxResponseHandle(address, asyncer=self._asyncer)
71
79
  self._handles[address] = handle
72
80
 
73
81
  return handle
@@ -92,7 +100,7 @@ class Mailbox:
92
100
 
93
101
  return address
94
102
 
95
- def deliver(self, response: spb.ServerResponse) -> None:
103
+ async def deliver(self, response: spb.ServerResponse) -> None:
96
104
  """Deliver a response from the service.
97
105
 
98
106
  If the response address is invalid, this does nothing.
@@ -116,7 +124,7 @@ class Mailbox:
116
124
  # It is not an error if there is no handle for the address:
117
125
  # handles can be abandoned if the result is no longer needed.
118
126
  if handle:
119
- handle.deliver(response)
127
+ await handle.deliver(response)
120
128
 
121
129
  def close(self) -> None:
122
130
  """Indicate no further responses will be delivered.
@@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Callable, Generic
5
5
 
6
6
  from typing_extensions import TypeVar, override
7
7
 
8
+ from wandb.sdk.lib import asyncio_manager
9
+
8
10
  # Necessary to break an import loop.
9
11
  if TYPE_CHECKING:
10
12
  from wandb.sdk.interface import interface
@@ -19,7 +21,18 @@ class HandleAbandonedError(Exception):
19
21
 
20
22
 
21
23
  class MailboxHandle(abc.ABC, Generic[_T]):
22
- """A thread-safe handle that allows waiting for a response to a request."""
24
+ """A handle for waiting on a response to a request."""
25
+
26
+ def __init__(self, asyncer: asyncio_manager.AsyncioManager) -> None:
27
+ self._asyncer = asyncer
28
+
29
+ @property
30
+ def asyncer(self) -> asyncio_manager.AsyncioManager:
31
+ """The asyncio thread to which the handle belongs.
32
+
33
+ The handle's async methods must be run using this object.
34
+ """
35
+ return self._asyncer
23
36
 
24
37
  def map(self, fn: Callable[[_T], _S]) -> MailboxHandle[_S]:
25
38
  """Returns a transformed handle.
@@ -35,7 +48,11 @@ class MailboxHandle(abc.ABC, Generic[_T]):
35
48
 
36
49
  @abc.abstractmethod
37
50
  def abandon(self) -> None:
38
- """Abandon the handle, indicating it will not receive a response."""
51
+ """Abandon the handle, indicating it will not receive a response.
52
+
53
+ This may not happen immediately: it is possible for an existing
54
+ call to `wait_async` to complete successfully after this method returns.
55
+ """
39
56
 
40
57
  @abc.abstractmethod
41
58
  def cancel(self, iface: interface.InterfaceBase) -> None:
@@ -48,14 +65,12 @@ class MailboxHandle(abc.ABC, Generic[_T]):
48
65
  iface: The interface on which to publish the cancel request.
49
66
  """
50
67
 
51
- @abc.abstractmethod
52
- def check(self) -> _T | None:
53
- """Returns the response if it's ready."""
54
-
55
68
  @abc.abstractmethod
56
69
  def wait_or(self, *, timeout: float | None) -> _T:
57
70
  """Wait for a response or a timeout.
58
71
 
72
+ It is an error to call this from an async function.
73
+
59
74
  Args:
60
75
  timeout: A finite number of seconds or None to never time out.
61
76
  If less than or equal to zero, times out immediately unless
@@ -95,6 +110,7 @@ class _MailboxMappedHandle(Generic[_S], MailboxHandle[_S]):
95
110
  handle: MailboxHandle[_T],
96
111
  fn: Callable[[_T], _S],
97
112
  ) -> None:
113
+ super().__init__(handle.asyncer)
98
114
  self._handle = handle
99
115
  self._fn = fn
100
116
 
@@ -106,13 +122,6 @@ class _MailboxMappedHandle(Generic[_S], MailboxHandle[_S]):
106
122
  def cancel(self, iface: interface.InterfaceBase) -> None:
107
123
  self._handle.cancel(iface)
108
124
 
109
- @override
110
- def check(self) -> _S | None:
111
- if response := self._handle.check():
112
- return self._fn(response)
113
- else:
114
- return None
115
-
116
125
  @override
117
126
  def wait_or(self, *, timeout: float | None) -> _S:
118
127
  return self._fn(self._handle.wait_or(timeout=timeout))
@@ -2,12 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import math
5
- import threading
6
5
  from typing import TYPE_CHECKING
7
6
 
8
7
  from typing_extensions import override
9
8
 
10
9
  from wandb.proto import wandb_server_pb2 as spb
10
+ from wandb.sdk.lib import asyncio_manager
11
11
 
12
12
  from .mailbox_handle import HandleAbandonedError, MailboxHandle
13
13
 
@@ -19,33 +19,36 @@ if TYPE_CHECKING:
19
19
  class MailboxResponseHandle(MailboxHandle[spb.ServerResponse]):
20
20
  """A general handle for any ServerResponse."""
21
21
 
22
- def __init__(self, address: str) -> None:
22
+ def __init__(
23
+ self,
24
+ address: str,
25
+ *,
26
+ asyncer: asyncio_manager.AsyncioManager,
27
+ ) -> None:
28
+ super().__init__(asyncer)
29
+
23
30
  self._address = address
24
- self._lock = threading.Lock()
25
- self._event = threading.Event()
26
31
 
27
32
  self._abandoned = False
28
33
  self._response: spb.ServerResponse | None = None
29
34
 
30
- self._asyncio_events: dict[asyncio.Event, _AsyncioEvent] = dict()
35
+ # Initialized on first use in the asyncio thread.
36
+ self._done_event: asyncio.Event | None = None
31
37
 
32
- def deliver(self, response: spb.ServerResponse) -> None:
33
- """Deliver the response.
38
+ async def deliver(self, response: spb.ServerResponse) -> None:
39
+ if self._abandoned:
40
+ return
34
41
 
35
- This may only be called once. It is an error to respond to the same
36
- request more than once. It is a no-op if the handle has been abandoned.
37
- """
38
- with self._lock:
39
- if self._abandoned:
40
- return
42
+ if self._response:
43
+ raise ValueError(
44
+ f"A response has already been delivered to {self._address}."
45
+ )
41
46
 
42
- if self._response:
43
- raise ValueError(
44
- f"A response has already been delivered to {self._address}."
45
- )
47
+ self._response = response
46
48
 
47
- self._response = response
48
- self._signal_done()
49
+ if not self._done_event:
50
+ self._done_event = asyncio.Event()
51
+ self._done_event.set()
49
52
 
50
53
  @override
51
54
  def cancel(self, iface: interface.InterfaceBase) -> None:
@@ -54,110 +57,43 @@ class MailboxResponseHandle(MailboxHandle[spb.ServerResponse]):
54
57
 
55
58
  @override
56
59
  def abandon(self) -> None:
57
- with self._lock:
60
+ async def impl() -> None:
58
61
  self._abandoned = True
59
- self._signal_done()
60
-
61
- def _signal_done(self) -> None:
62
- """Indicate that the handle either got a response or became abandoned.
63
62
 
64
- The lock must be held.
65
- """
66
- # Unblock threads blocked on `wait_or`.
67
- self._event.set()
63
+ if not self._done_event:
64
+ self._done_event = asyncio.Event()
65
+ self._done_event.set()
68
66
 
69
- # Unblock asyncio loops blocked on `wait_async`.
70
- for asyncio_event in self._asyncio_events.values():
71
- asyncio_event.set_threadsafe()
72
- self._asyncio_events.clear()
73
-
74
- @override
75
- def check(self) -> spb.ServerResponse | None:
76
- with self._lock:
77
- return self._response
67
+ self.asyncer.run_soon(impl)
78
68
 
79
69
  @override
80
70
  def wait_or(self, *, timeout: float | None) -> spb.ServerResponse:
81
- if timeout is not None and not math.isfinite(timeout):
82
- raise ValueError("Timeout must be finite or None.")
83
-
84
- if not self._event.wait(timeout=timeout):
85
- raise TimeoutError(
86
- f"Timed out waiting for response on {self._address}",
87
- )
88
-
89
- with self._lock:
90
- if self._response:
91
- return self._response
92
-
93
- assert self._abandoned
94
- raise HandleAbandonedError()
71
+ return self.asyncer.run(lambda: self.wait_async(timeout=timeout))
95
72
 
96
73
  @override
97
74
  async def wait_async(self, *, timeout: float | None) -> spb.ServerResponse:
98
75
  if timeout is not None and not math.isfinite(timeout):
99
76
  raise ValueError("Timeout must be finite or None.")
100
77
 
101
- evt = asyncio.Event()
102
- self._add_asyncio_event(asyncio.get_event_loop(), evt)
78
+ if not self._done_event:
79
+ self._done_event = asyncio.Event()
103
80
 
104
81
  try:
105
- await asyncio.wait_for(evt.wait(), timeout=timeout)
82
+ await asyncio.wait_for(self._done_event.wait(), timeout=timeout)
106
83
 
107
84
  except (asyncio.TimeoutError, TimeoutError) as e:
108
- with self._lock:
109
- if self._response:
110
- return self._response
111
- elif self._abandoned:
112
- raise HandleAbandonedError()
113
- else:
114
- raise TimeoutError(
115
- f"Timed out waiting for response on {self._address}"
116
- ) from e
117
-
118
- else:
119
- with self._lock:
120
- if self._response:
121
- return self._response
122
-
123
- assert self._abandoned
85
+ if self._response:
86
+ return self._response
87
+ elif self._abandoned:
124
88
  raise HandleAbandonedError()
125
-
126
- finally:
127
- self._forget_asyncio_event(evt)
128
-
129
- def _add_asyncio_event(
130
- self,
131
- loop: asyncio.AbstractEventLoop,
132
- event: asyncio.Event,
133
- ) -> None:
134
- """Add an event to signal when a response is received.
135
-
136
- If a response already exists, this notifies the event loop immediately.
137
- """
138
- asyncio_event = _AsyncioEvent(loop, event)
139
-
140
- with self._lock:
141
- if self._response or self._abandoned:
142
- asyncio_event.set_threadsafe()
143
89
  else:
144
- self._asyncio_events[event] = asyncio_event
145
-
146
- def _forget_asyncio_event(self, event: asyncio.Event) -> None:
147
- """Cancel signalling an event when a response is received."""
148
- with self._lock:
149
- self._asyncio_events.pop(event, None)
90
+ raise TimeoutError(
91
+ f"Timed out waiting for response on {self._address}"
92
+ ) from e
150
93
 
94
+ else:
95
+ if self._response:
96
+ return self._response
151
97
 
152
- class _AsyncioEvent:
153
- def __init__(
154
- self,
155
- loop: asyncio.AbstractEventLoop,
156
- event: asyncio.Event,
157
- ):
158
- self._loop = loop
159
- self._event = event
160
-
161
- def set_threadsafe(self) -> None:
162
- """Set the asyncio event in its own loop."""
163
- self._loop.call_soon_threadsafe(self._event.set)
98
+ assert self._abandoned
99
+ raise HandleAbandonedError()
@@ -14,7 +14,6 @@ def wait_with_progress(
14
14
  handle: MailboxHandle[_T],
15
15
  *,
16
16
  timeout: float | None,
17
- progress_after: float,
18
17
  display_progress: Callable[[], Coroutine[Any, Any, None]],
19
18
  ) -> _T:
20
19
  """Wait for a handle, possibly displaying progress to the user.
@@ -24,7 +23,6 @@ def wait_with_progress(
24
23
  return wait_all_with_progress(
25
24
  [handle],
26
25
  timeout=timeout,
27
- progress_after=progress_after,
28
26
  display_progress=display_progress,
29
27
  )[0]
30
28
 
@@ -33,7 +31,6 @@ def wait_all_with_progress(
33
31
  handle_list: list[MailboxHandle[_T]],
34
32
  *,
35
33
  timeout: float | None,
36
- progress_after: float,
37
34
  display_progress: Callable[[], Coroutine[Any, Any, None]],
38
35
  ) -> list[_T]:
39
36
  """Wait for multiple handles, possibly displaying progress to the user.
@@ -42,18 +39,14 @@ def wait_all_with_progress(
42
39
  handle_list: The handles to wait for.
43
40
  timeout: A number of seconds after which to raise a TimeoutError,
44
41
  or None if this should never timeout.
45
- progress_after: A number of seconds after which to start the
46
- display_progress callback. Starting the callback creates a thread
47
- and starts an asyncio loop, so we want to avoid doing it if
48
- the handle is resolved quickly.
49
42
  display_progress: An asyncio function that displays progress to
50
- the user. This function is executed on a new thread and cancelled
51
- if the timeout is exceeded.
43
+ the user. This function runs using the handles' AsyncioManager.
52
44
 
53
45
  Returns:
54
46
  A list where the Nth item is the Nth handle's result.
55
47
 
56
48
  Raises:
49
+ ValueError: If the handles live in different asyncio threads.
57
50
  TimeoutError: If the overall timeout expires.
58
51
  HandleAbandonedError: If any handle becomes abandoned.
59
52
  Exception: Any exception from the display function is propagated.
@@ -61,16 +54,13 @@ def wait_all_with_progress(
61
54
  if not handle_list:
62
55
  return []
63
56
 
64
- if timeout is not None and timeout <= progress_after:
65
- return _wait_handles(handle_list, timeout=timeout)
57
+ asyncer = handle_list[0].asyncer
58
+ for handle in handle_list:
59
+ if handle.asyncer is not asyncer:
60
+ raise ValueError("Handles have different AsyncioManagers.")
66
61
 
67
62
  start_time = time.monotonic()
68
63
 
69
- try:
70
- return _wait_handles(handle_list, timeout=progress_after)
71
- except TimeoutError:
72
- pass
73
-
74
64
  async def progress_loop_with_timeout() -> list[_T]:
75
65
  with asyncio_compat.cancel_on_exit(display_progress()):
76
66
  if timeout is not None:
@@ -84,32 +74,7 @@ def wait_all_with_progress(
84
74
  timeout=remaining_timeout,
85
75
  )
86
76
 
87
- return asyncio_compat.run(progress_loop_with_timeout)
88
-
89
-
90
- def _wait_handles(
91
- handle_list: list[MailboxHandle[_T]],
92
- *,
93
- timeout: float,
94
- ) -> list[_T]:
95
- """Wait for multiple mailbox handles.
96
-
97
- Returns:
98
- Each handle's result, in the same order as the given handles.
99
-
100
- Raises:
101
- TimeoutError: If the overall timeout expires.
102
- HandleAbandonedError: If any handle becomes abandoned.
103
- """
104
- results: list[_T] = []
105
-
106
- start_time = time.monotonic()
107
- for handle in handle_list:
108
- elapsed_time = time.monotonic() - start_time
109
- remaining_timeout = timeout - elapsed_time
110
- results.append(handle.wait_or(timeout=remaining_timeout))
111
-
112
- return results
77
+ return asyncer.run(progress_loop_with_timeout)
113
78
 
114
79
 
115
80
  async def _wait_handles_async(