wandb 0.19.7__py3-none-win32.whl → 0.19.8__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.
- wandb/__init__.py +1 -1
- wandb/__init__.pyi +32 -2
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/data_types.py +1 -1
- wandb/filesync/dir_watcher.py +2 -1
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
- wandb/sdk/artifacts/artifact.py +11 -10
- wandb/sdk/backend/backend.py +16 -5
- wandb/sdk/interface/interface.py +65 -43
- wandb/sdk/interface/interface_queue.py +0 -7
- wandb/sdk/interface/interface_relay.py +6 -16
- wandb/sdk/interface/interface_shared.py +47 -40
- wandb/sdk/interface/interface_sock.py +1 -8
- wandb/sdk/interface/router.py +22 -54
- wandb/sdk/interface/router_queue.py +11 -10
- wandb/sdk/interface/router_relay.py +24 -12
- wandb/sdk/interface/router_sock.py +6 -11
- wandb/sdk/internal/sender.py +3 -1
- wandb/sdk/lib/console_capture.py +172 -0
- wandb/sdk/lib/redirect.py +102 -76
- wandb/sdk/lib/service_connection.py +37 -17
- wandb/sdk/lib/sock_client.py +2 -52
- wandb/sdk/mailbox/__init__.py +3 -3
- wandb/sdk/mailbox/mailbox.py +31 -17
- wandb/sdk/mailbox/mailbox_handle.py +127 -0
- wandb/sdk/mailbox/{handles.py → response_handle.py} +34 -66
- wandb/sdk/mailbox/wait_with_progress.py +16 -15
- wandb/sdk/service/server_sock.py +4 -2
- wandb/sdk/service/streams.py +10 -5
- wandb/sdk/wandb_init.py +12 -15
- wandb/sdk/wandb_run.py +8 -10
- wandb/sdk/wandb_settings.py +7 -1
- wandb/sdk/wandb_sync.py +1 -7
- {wandb-0.19.7.dist-info → wandb-0.19.8.dist-info}/METADATA +1 -1
- {wandb-0.19.7.dist-info → wandb-0.19.8.dist-info}/RECORD +44 -44
- wandb/sdk/interface/message_future.py +0 -27
- wandb/sdk/interface/message_future_poll.py +0 -50
- {wandb-0.19.7.dist-info → wandb-0.19.8.dist-info}/WHEEL +0 -0
- {wandb-0.19.7.dist-info → wandb-0.19.8.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.7.dist-info → wandb-0.19.8.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/mailbox/mailbox.py
CHANGED
@@ -6,8 +6,10 @@ import string
|
|
6
6
|
import threading
|
7
7
|
|
8
8
|
from wandb.proto import wandb_internal_pb2 as pb
|
9
|
+
from wandb.proto import wandb_server_pb2 as spb
|
9
10
|
|
10
|
-
from . import
|
11
|
+
from .mailbox_handle import MailboxHandle
|
12
|
+
from .response_handle import MailboxResponseHandle
|
11
13
|
|
12
14
|
_logger = logging.getLogger(__name__)
|
13
15
|
|
@@ -19,22 +21,25 @@ class MailboxClosedError(Exception):
|
|
19
21
|
class Mailbox:
|
20
22
|
"""Matches service responses to requests.
|
21
23
|
|
22
|
-
The mailbox can set an address on a
|
24
|
+
The mailbox can set an address on a server request and create a handle for
|
23
25
|
waiting for a response to that record. Responses are delivered by calling
|
24
26
|
`deliver()`. The `close()` method abandons all handles in case the
|
25
27
|
service process becomes unreachable.
|
26
28
|
"""
|
27
29
|
|
28
30
|
def __init__(self) -> None:
|
29
|
-
self._handles: dict[str,
|
31
|
+
self._handles: dict[str, MailboxResponseHandle] = {}
|
30
32
|
self._handles_lock = threading.Lock()
|
31
33
|
self._closed = False
|
32
34
|
|
33
|
-
def require_response(
|
35
|
+
def require_response(
|
36
|
+
self,
|
37
|
+
request: spb.ServerRequest | pb.Record,
|
38
|
+
) -> MailboxHandle[spb.ServerResponse]:
|
34
39
|
"""Set a response address on a request.
|
35
40
|
|
36
41
|
Args:
|
37
|
-
request: The request on which to set a mailbox slot.
|
42
|
+
request: The request on which to set a request ID or mailbox slot.
|
38
43
|
This is mutated. An address must not already be set.
|
39
44
|
|
40
45
|
Returns:
|
@@ -45,17 +50,24 @@ class Mailbox:
|
|
45
50
|
no new responses are expected to be delivered and new handles
|
46
51
|
cannot be created.
|
47
52
|
"""
|
48
|
-
if
|
49
|
-
|
53
|
+
if isinstance(request, spb.ServerRequest):
|
54
|
+
if address := request.request_id:
|
55
|
+
raise ValueError(f"Request already has an address ({address})")
|
50
56
|
|
51
|
-
|
52
|
-
|
57
|
+
address = self._new_address()
|
58
|
+
request.request_id = address
|
59
|
+
else:
|
60
|
+
if address := request.control.mailbox_slot:
|
61
|
+
raise ValueError(f"Request already has an address ({address})")
|
62
|
+
|
63
|
+
address = self._new_address()
|
64
|
+
request.control.mailbox_slot = address
|
53
65
|
|
54
66
|
with self._handles_lock:
|
55
67
|
if self._closed:
|
56
68
|
raise MailboxClosedError()
|
57
69
|
|
58
|
-
handle =
|
70
|
+
handle = MailboxResponseHandle(address)
|
59
71
|
self._handles[address] = handle
|
60
72
|
|
61
73
|
return handle
|
@@ -80,18 +92,20 @@ class Mailbox:
|
|
80
92
|
|
81
93
|
return address
|
82
94
|
|
83
|
-
def deliver(self,
|
95
|
+
def deliver(self, response: spb.ServerResponse) -> None:
|
84
96
|
"""Deliver a response from the service.
|
85
97
|
|
86
98
|
If the response address is invalid, this does nothing.
|
87
99
|
It is a no-op if the mailbox has been closed.
|
88
100
|
"""
|
89
|
-
address =
|
101
|
+
address = response.request_id
|
90
102
|
if not address:
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
103
|
+
kind: str | None = response.WhichOneof("server_response_type")
|
104
|
+
if kind == "result_communicate":
|
105
|
+
result_type = response.result_communicate.WhichOneof("result_type")
|
106
|
+
kind = f"result_communicate.{result_type}"
|
107
|
+
|
108
|
+
_logger.error(f"Received response with no mailbox slot: {kind}")
|
95
109
|
return
|
96
110
|
|
97
111
|
with self._handles_lock:
|
@@ -102,7 +116,7 @@ class Mailbox:
|
|
102
116
|
# It is not an error if there is no handle for the address:
|
103
117
|
# handles can be abandoned if the result is no longer needed.
|
104
118
|
if handle:
|
105
|
-
handle.deliver(
|
119
|
+
handle.deliver(response)
|
106
120
|
|
107
121
|
def close(self) -> None:
|
108
122
|
"""Indicate no further responses will be delivered.
|
@@ -0,0 +1,127 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import abc
|
4
|
+
import sys
|
5
|
+
from typing import TYPE_CHECKING, Callable, Generic, TypeVar
|
6
|
+
|
7
|
+
# Necessary to break an import loop.
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from wandb.sdk.interface import interface
|
10
|
+
|
11
|
+
if sys.version_info >= (3, 12):
|
12
|
+
from typing import override
|
13
|
+
else:
|
14
|
+
from typing_extensions import override
|
15
|
+
|
16
|
+
|
17
|
+
_T = TypeVar("_T")
|
18
|
+
_S = TypeVar("_S")
|
19
|
+
|
20
|
+
|
21
|
+
class HandleAbandonedError(Exception):
|
22
|
+
"""The handle has no response and has been abandoned."""
|
23
|
+
|
24
|
+
|
25
|
+
class MailboxHandle(abc.ABC, Generic[_T]):
|
26
|
+
"""A thread-safe handle that allows waiting for a response to a request."""
|
27
|
+
|
28
|
+
def map(self, fn: Callable[[_T], _S]) -> MailboxHandle[_S]:
|
29
|
+
"""Returns a transformed handle.
|
30
|
+
|
31
|
+
Methods on the returned handle call methods on this handle, but the
|
32
|
+
response type is derived using the given function.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
fn: A function to apply to this handle's result to get the new
|
36
|
+
handle's result. The function should be pure and fast.
|
37
|
+
"""
|
38
|
+
return _MailboxMappedHandle(self, fn)
|
39
|
+
|
40
|
+
@abc.abstractmethod
|
41
|
+
def abandon(self) -> None:
|
42
|
+
"""Abandon the handle, indicating it will not receive a response."""
|
43
|
+
|
44
|
+
@abc.abstractmethod
|
45
|
+
def cancel(self, iface: interface.InterfaceBase) -> None:
|
46
|
+
"""Cancel the handle, requesting any associated work to not complete.
|
47
|
+
|
48
|
+
This automatically abandons the handle, as a response is no longer
|
49
|
+
guaranteed.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
iface: The interface on which to publish the cancel request.
|
53
|
+
"""
|
54
|
+
|
55
|
+
@abc.abstractmethod
|
56
|
+
def check(self) -> _T | None:
|
57
|
+
"""Returns the response if it's ready."""
|
58
|
+
|
59
|
+
@abc.abstractmethod
|
60
|
+
def wait_or(self, *, timeout: float | None) -> _T:
|
61
|
+
"""Wait for a response or a timeout.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
timeout: A finite number of seconds or None to never time out.
|
65
|
+
If less than or equal to zero, times out immediately unless
|
66
|
+
the response is available.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
The response if it arrives before the timeout or has already arrived.
|
70
|
+
|
71
|
+
Raises:
|
72
|
+
TimeoutError: If the timeout is reached.
|
73
|
+
HandleAbandonedError: If the handle becomes abandoned.
|
74
|
+
"""
|
75
|
+
|
76
|
+
@abc.abstractmethod
|
77
|
+
async def wait_async(self, *, timeout: float | None) -> _T:
|
78
|
+
"""Wait for a response or timeout.
|
79
|
+
|
80
|
+
This must run in an `asyncio` event loop.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
timeout: A finite number of seconds or None to never time out.
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
The response if it arrives before the timeout or has already arrived.
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
TimeoutError: If the timeout is reached.
|
90
|
+
HandleAbandonedError: If the handle becomes abandoned.
|
91
|
+
"""
|
92
|
+
|
93
|
+
|
94
|
+
class _MailboxMappedHandle(Generic[_S], MailboxHandle[_S]):
|
95
|
+
"""A mailbox handle whose result is derived from another handle."""
|
96
|
+
|
97
|
+
def __init__(
|
98
|
+
self,
|
99
|
+
handle: MailboxHandle[_T],
|
100
|
+
fn: Callable[[_T], _S],
|
101
|
+
) -> None:
|
102
|
+
self._handle = handle
|
103
|
+
self._fn = fn
|
104
|
+
|
105
|
+
@override
|
106
|
+
def abandon(self) -> None:
|
107
|
+
self._handle.abandon()
|
108
|
+
|
109
|
+
@override
|
110
|
+
def cancel(self, iface: interface.InterfaceBase) -> None:
|
111
|
+
self._handle.cancel(iface)
|
112
|
+
|
113
|
+
@override
|
114
|
+
def check(self) -> _S | None:
|
115
|
+
if response := self._handle.check():
|
116
|
+
return self._fn(response)
|
117
|
+
else:
|
118
|
+
return None
|
119
|
+
|
120
|
+
@override
|
121
|
+
def wait_or(self, *, timeout: float | None) -> _S:
|
122
|
+
return self._fn(self._handle.wait_or(timeout=timeout))
|
123
|
+
|
124
|
+
@override
|
125
|
+
async def wait_async(self, *, timeout: float | None) -> _S:
|
126
|
+
response = await self._handle.wait_async(timeout=timeout)
|
127
|
+
return self._fn(response)
|
@@ -2,22 +2,26 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import math
|
5
|
+
import sys
|
5
6
|
import threading
|
6
7
|
from typing import TYPE_CHECKING
|
7
8
|
|
8
|
-
from wandb.proto import
|
9
|
+
from wandb.proto import wandb_server_pb2 as spb
|
10
|
+
|
11
|
+
from .mailbox_handle import HandleAbandonedError, MailboxHandle
|
9
12
|
|
10
13
|
# Necessary to break an import loop.
|
11
14
|
if TYPE_CHECKING:
|
12
15
|
from wandb.sdk.interface import interface
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
if sys.version_info >= (3, 12):
|
18
|
+
from typing import override
|
19
|
+
else:
|
20
|
+
from typing_extensions import override
|
17
21
|
|
18
22
|
|
19
|
-
class MailboxHandle:
|
20
|
-
"""A
|
23
|
+
class MailboxResponseHandle(MailboxHandle[spb.ServerResponse]):
|
24
|
+
"""A general handle for any ServerResponse."""
|
21
25
|
|
22
26
|
def __init__(self, address: str) -> None:
|
23
27
|
self._address = address
|
@@ -25,11 +29,11 @@ class MailboxHandle:
|
|
25
29
|
self._event = threading.Event()
|
26
30
|
|
27
31
|
self._abandoned = False
|
28
|
-
self.
|
32
|
+
self._response: spb.ServerResponse | None = None
|
29
33
|
|
30
34
|
self._asyncio_events: dict[asyncio.Event, _AsyncioEvent] = dict()
|
31
35
|
|
32
|
-
def deliver(self,
|
36
|
+
def deliver(self, response: spb.ServerResponse) -> None:
|
33
37
|
"""Deliver the response.
|
34
38
|
|
35
39
|
This may only be called once. It is an error to respond to the same
|
@@ -39,34 +43,27 @@ class MailboxHandle:
|
|
39
43
|
if self._abandoned:
|
40
44
|
return
|
41
45
|
|
42
|
-
if self.
|
46
|
+
if self._response:
|
43
47
|
raise ValueError(
|
44
48
|
f"A response has already been delivered to {self._address}."
|
45
49
|
)
|
46
50
|
|
47
|
-
self.
|
51
|
+
self._response = response
|
48
52
|
self._signal_done()
|
49
53
|
|
54
|
+
@override
|
50
55
|
def cancel(self, iface: interface.InterfaceBase) -> None:
|
51
|
-
"""Cancel the handle, requesting any associated work to not complete.
|
52
|
-
|
53
|
-
This automatically abandons the handle, as a response is no longer
|
54
|
-
guaranteed.
|
55
|
-
|
56
|
-
Args:
|
57
|
-
interface: The interface on which to publish the cancel request.
|
58
|
-
"""
|
59
56
|
iface.publish_cancel(self._address)
|
60
57
|
self.abandon()
|
61
58
|
|
59
|
+
@override
|
62
60
|
def abandon(self) -> None:
|
63
|
-
"""Abandon the handle, indicating it will not receive a response."""
|
64
61
|
with self._lock:
|
65
62
|
self._abandoned = True
|
66
63
|
self._signal_done()
|
67
64
|
|
68
65
|
def _signal_done(self) -> None:
|
69
|
-
"""Indicate that the handle either got a
|
66
|
+
"""Indicate that the handle either got a response or became abandoned.
|
70
67
|
|
71
68
|
The lock must be held.
|
72
69
|
"""
|
@@ -78,29 +75,13 @@ class MailboxHandle:
|
|
78
75
|
asyncio_event.set_threadsafe()
|
79
76
|
self._asyncio_events.clear()
|
80
77
|
|
81
|
-
|
82
|
-
|
78
|
+
@override
|
79
|
+
def check(self) -> spb.ServerResponse | None:
|
83
80
|
with self._lock:
|
84
|
-
return self.
|
85
|
-
|
86
|
-
def wait_or(self, *, timeout: float | None) -> pb.Result:
|
87
|
-
"""Wait for a response or a timeout.
|
88
|
-
|
89
|
-
This is called `wait_or` because it replaces a method called `wait`
|
90
|
-
with different semantics.
|
91
|
-
|
92
|
-
Args:
|
93
|
-
timeout: A finite number of seconds or None to never time out.
|
94
|
-
If less than or equal to zero, times out immediately unless
|
95
|
-
the result is available.
|
81
|
+
return self._response
|
96
82
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
Raises:
|
101
|
-
TimeoutError: If the timeout is reached.
|
102
|
-
HandleAbandonedError: If the handle becomes abandoned.
|
103
|
-
"""
|
83
|
+
@override
|
84
|
+
def wait_or(self, *, timeout: float | None) -> spb.ServerResponse:
|
104
85
|
if timeout is not None and not math.isfinite(timeout):
|
105
86
|
raise ValueError("Timeout must be finite or None.")
|
106
87
|
|
@@ -110,27 +91,14 @@ class MailboxHandle:
|
|
110
91
|
)
|
111
92
|
|
112
93
|
with self._lock:
|
113
|
-
if self.
|
114
|
-
return self.
|
94
|
+
if self._response:
|
95
|
+
return self._response
|
115
96
|
|
116
97
|
assert self._abandoned
|
117
98
|
raise HandleAbandonedError()
|
118
99
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
This must run in an `asyncio` event loop.
|
123
|
-
|
124
|
-
Args:
|
125
|
-
timeout: A finite number of seconds or None to never time out.
|
126
|
-
|
127
|
-
Returns:
|
128
|
-
The result if it arrives before the timeout or has already arrived.
|
129
|
-
|
130
|
-
Raises:
|
131
|
-
TimeoutError: If the timeout is reached.
|
132
|
-
HandleAbandonedError: If the handle becomes abandoned.
|
133
|
-
"""
|
100
|
+
@override
|
101
|
+
async def wait_async(self, *, timeout: float | None) -> spb.ServerResponse:
|
134
102
|
if timeout is not None and not math.isfinite(timeout):
|
135
103
|
raise ValueError("Timeout must be finite or None.")
|
136
104
|
|
@@ -142,8 +110,8 @@ class MailboxHandle:
|
|
142
110
|
|
143
111
|
except (asyncio.TimeoutError, TimeoutError) as e:
|
144
112
|
with self._lock:
|
145
|
-
if self.
|
146
|
-
return self.
|
113
|
+
if self._response:
|
114
|
+
return self._response
|
147
115
|
elif self._abandoned:
|
148
116
|
raise HandleAbandonedError()
|
149
117
|
else:
|
@@ -153,8 +121,8 @@ class MailboxHandle:
|
|
153
121
|
|
154
122
|
else:
|
155
123
|
with self._lock:
|
156
|
-
if self.
|
157
|
-
return self.
|
124
|
+
if self._response:
|
125
|
+
return self._response
|
158
126
|
|
159
127
|
assert self._abandoned
|
160
128
|
raise HandleAbandonedError()
|
@@ -167,20 +135,20 @@ class MailboxHandle:
|
|
167
135
|
loop: asyncio.AbstractEventLoop,
|
168
136
|
event: asyncio.Event,
|
169
137
|
) -> None:
|
170
|
-
"""Add an event to signal when a
|
138
|
+
"""Add an event to signal when a response is received.
|
171
139
|
|
172
|
-
If a
|
140
|
+
If a response already exists, this notifies the event loop immediately.
|
173
141
|
"""
|
174
142
|
asyncio_event = _AsyncioEvent(loop, event)
|
175
143
|
|
176
144
|
with self._lock:
|
177
|
-
if self.
|
145
|
+
if self._response or self._abandoned:
|
178
146
|
asyncio_event.set_threadsafe()
|
179
147
|
else:
|
180
148
|
self._asyncio_events[event] = asyncio_event
|
181
149
|
|
182
150
|
def _forget_asyncio_event(self, event: asyncio.Event) -> None:
|
183
|
-
"""Cancel signalling an event when a
|
151
|
+
"""Cancel signalling an event when a response is received."""
|
184
152
|
with self._lock:
|
185
153
|
self._asyncio_events.pop(event, None)
|
186
154
|
|
@@ -1,21 +1,22 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import time
|
4
|
-
from typing import Any, Callable, Coroutine, List, cast
|
4
|
+
from typing import Any, Callable, Coroutine, List, TypeVar, cast
|
5
5
|
|
6
|
-
from wandb.proto import wandb_internal_pb2 as pb
|
7
6
|
from wandb.sdk.lib import asyncio_compat
|
8
7
|
|
9
|
-
from . import
|
8
|
+
from .mailbox_handle import MailboxHandle
|
9
|
+
|
10
|
+
_T = TypeVar("_T")
|
10
11
|
|
11
12
|
|
12
13
|
def wait_with_progress(
|
13
|
-
handle:
|
14
|
+
handle: MailboxHandle[_T],
|
14
15
|
*,
|
15
16
|
timeout: float | None,
|
16
17
|
progress_after: float,
|
17
18
|
display_progress: Callable[[], Coroutine[Any, Any, None]],
|
18
|
-
) ->
|
19
|
+
) -> _T:
|
19
20
|
"""Wait for a handle, possibly displaying progress to the user.
|
20
21
|
|
21
22
|
Equivalent to passing a single handle to `wait_all_with_progress`.
|
@@ -29,12 +30,12 @@ def wait_with_progress(
|
|
29
30
|
|
30
31
|
|
31
32
|
def wait_all_with_progress(
|
32
|
-
handle_list: list[
|
33
|
+
handle_list: list[MailboxHandle[_T]],
|
33
34
|
*,
|
34
35
|
timeout: float | None,
|
35
36
|
progress_after: float,
|
36
37
|
display_progress: Callable[[], Coroutine[Any, Any, None]],
|
37
|
-
) -> list[
|
38
|
+
) -> list[_T]:
|
38
39
|
"""Wait for multiple handles, possibly displaying progress to the user.
|
39
40
|
|
40
41
|
Args:
|
@@ -70,7 +71,7 @@ def wait_all_with_progress(
|
|
70
71
|
except TimeoutError:
|
71
72
|
pass
|
72
73
|
|
73
|
-
async def progress_loop_with_timeout() -> list[
|
74
|
+
async def progress_loop_with_timeout() -> list[_T]:
|
74
75
|
with asyncio_compat.cancel_on_exit(display_progress()):
|
75
76
|
if timeout is not None:
|
76
77
|
elapsed_time = time.monotonic() - start_time
|
@@ -87,10 +88,10 @@ def wait_all_with_progress(
|
|
87
88
|
|
88
89
|
|
89
90
|
def _wait_handles(
|
90
|
-
handle_list: list[
|
91
|
+
handle_list: list[MailboxHandle[_T]],
|
91
92
|
*,
|
92
93
|
timeout: float,
|
93
|
-
) -> list[
|
94
|
+
) -> list[_T]:
|
94
95
|
"""Wait for multiple mailbox handles.
|
95
96
|
|
96
97
|
Returns:
|
@@ -100,7 +101,7 @@ def _wait_handles(
|
|
100
101
|
TimeoutError: If the overall timeout expires.
|
101
102
|
HandleAbandonedError: If any handle becomes abandoned.
|
102
103
|
"""
|
103
|
-
results: list[
|
104
|
+
results: list[_T] = []
|
104
105
|
|
105
106
|
start_time = time.monotonic()
|
106
107
|
for handle in handle_list:
|
@@ -112,15 +113,15 @@ def _wait_handles(
|
|
112
113
|
|
113
114
|
|
114
115
|
async def _wait_handles_async(
|
115
|
-
handle_list: list[
|
116
|
+
handle_list: list[MailboxHandle[_T]],
|
116
117
|
*,
|
117
118
|
timeout: float | None,
|
118
|
-
) -> list[
|
119
|
+
) -> list[_T]:
|
119
120
|
"""Asynchronously wait for multiple mailbox handles.
|
120
121
|
|
121
122
|
Just like _wait_handles.
|
122
123
|
"""
|
123
|
-
results: list[
|
124
|
+
results: list[_T | None] = [None for _ in handle_list]
|
124
125
|
|
125
126
|
async def wait_single(index: int) -> None:
|
126
127
|
handle = handle_list[index]
|
@@ -131,4 +132,4 @@ async def _wait_handles_async(
|
|
131
132
|
task_group.start_soon(wait_single(index))
|
132
133
|
|
133
134
|
# NOTE: `list` is not subscriptable until Python 3.10, so we use List.
|
134
|
-
return cast(List[
|
135
|
+
return cast(List[_T], results)
|
wandb/sdk/service/server_sock.py
CHANGED
@@ -44,7 +44,10 @@ class SockServerInterfaceReaderThread(threading.Thread):
|
|
44
44
|
_stopped: "Event"
|
45
45
|
|
46
46
|
def __init__(
|
47
|
-
self,
|
47
|
+
self,
|
48
|
+
clients: ClientDict,
|
49
|
+
iface: "InterfaceRelay",
|
50
|
+
stopped: "Event",
|
48
51
|
) -> None:
|
49
52
|
self._iface = iface
|
50
53
|
self._clients = clients
|
@@ -53,7 +56,6 @@ class SockServerInterfaceReaderThread(threading.Thread):
|
|
53
56
|
self._stopped = stopped
|
54
57
|
|
55
58
|
def run(self) -> None:
|
56
|
-
assert self._iface.relay_q
|
57
59
|
while not self._stopped.is_set():
|
58
60
|
try:
|
59
61
|
result = self._iface.relay_q.get(timeout=1)
|
wandb/sdk/service/streams.py
CHANGED
@@ -21,16 +21,14 @@ import psutil
|
|
21
21
|
import wandb
|
22
22
|
import wandb.util
|
23
23
|
from wandb.proto import wandb_internal_pb2 as pb
|
24
|
+
from wandb.sdk.interface.interface_relay import InterfaceRelay
|
25
|
+
from wandb.sdk.interface.router_relay import MessageRelayRouter
|
24
26
|
from wandb.sdk.internal.settings_static import SettingsStatic
|
25
27
|
from wandb.sdk.lib import asyncio_compat, progress
|
26
28
|
from wandb.sdk.lib import printer as printerlib
|
27
29
|
from wandb.sdk.mailbox import Mailbox, MailboxHandle, wait_all_with_progress
|
28
30
|
from wandb.sdk.wandb_run import Run
|
29
31
|
|
30
|
-
from ..interface.interface_relay import InterfaceRelay
|
31
|
-
|
32
|
-
# from wandb.sdk.wandb_settings import Settings
|
33
|
-
|
34
32
|
|
35
33
|
class StreamThread(threading.Thread):
|
36
34
|
"""Class to running internal process as a thread."""
|
@@ -62,6 +60,12 @@ class StreamRecord:
|
|
62
60
|
self._record_q = queue.Queue()
|
63
61
|
self._result_q = queue.Queue()
|
64
62
|
self._relay_q = queue.Queue()
|
63
|
+
self._router = MessageRelayRouter(
|
64
|
+
request_queue=self._record_q,
|
65
|
+
response_queue=self._result_q,
|
66
|
+
relay_queue=self._relay_q,
|
67
|
+
mailbox=self._mailbox,
|
68
|
+
)
|
65
69
|
self._iface = InterfaceRelay(
|
66
70
|
record_q=self._record_q,
|
67
71
|
result_q=self._result_q,
|
@@ -80,6 +84,7 @@ class StreamRecord:
|
|
80
84
|
|
81
85
|
def join(self) -> None:
|
82
86
|
self._iface.join()
|
87
|
+
self._router.join()
|
83
88
|
if self._thread:
|
84
89
|
self._thread.join()
|
85
90
|
|
@@ -290,7 +295,7 @@ class StreamMux:
|
|
290
295
|
|
291
296
|
# fixme: for now we have a single printer for all streams,
|
292
297
|
# and jupyter is disabled if at least single stream's setting set `_jupyter` to false
|
293
|
-
exit_handles: list[MailboxHandle] = []
|
298
|
+
exit_handles: list[MailboxHandle[pb.Result]] = []
|
294
299
|
|
295
300
|
# only finish started streams, non started streams failed early
|
296
301
|
started_streams: dict[str, StreamRecord] = {}
|
wandb/sdk/wandb_init.py
CHANGED
@@ -42,7 +42,7 @@ from . import wandb_login, wandb_setup
|
|
42
42
|
from .backend.backend import Backend
|
43
43
|
from .lib import SummaryDisabled, filesystem, module, paths, printer, telemetry
|
44
44
|
from .lib.deprecate import Deprecated, deprecate
|
45
|
-
from .mailbox import
|
45
|
+
from .mailbox import wait_with_progress
|
46
46
|
from .wandb_helper import parse_config
|
47
47
|
from .wandb_run import Run, TeardownHook, TeardownStage
|
48
48
|
from .wandb_settings import Settings
|
@@ -682,11 +682,11 @@ class _WandbInit:
|
|
682
682
|
drun._Run__metadata = wandb.sdk.wandb_metadata.Metadata()
|
683
683
|
|
684
684
|
# methods
|
685
|
-
drun.log = lambda data, *_, **__: drun.summary.update(data) # type: ignore
|
686
|
-
drun.finish = lambda *_, **__: module.unset_globals() # type: ignore
|
687
|
-
drun.join = drun.finish # type: ignore
|
688
|
-
drun.define_metric = lambda *_, **__: wandb.sdk.wandb_metric.Metric("dummy") # type: ignore
|
689
|
-
drun.save = lambda *_, **__: False # type: ignore
|
685
|
+
drun.log = lambda data, *_, **__: drun.summary.update(data) # type: ignore[method-assign]
|
686
|
+
drun.finish = lambda *_, **__: module.unset_globals() # type: ignore[method-assign]
|
687
|
+
drun.join = drun.finish # type: ignore[method-assign]
|
688
|
+
drun.define_metric = lambda *_, **__: wandb.sdk.wandb_metric.Metric("dummy") # type: ignore[method-assign]
|
689
|
+
drun.save = lambda *_, **__: False # type: ignore[method-assign]
|
690
690
|
for symbol in (
|
691
691
|
"alert",
|
692
692
|
"finish_artifact",
|
@@ -733,7 +733,7 @@ class _WandbInit:
|
|
733
733
|
def __call__(self, *args: Any, **kwargs: Any) -> _ChainableNoOp:
|
734
734
|
return _ChainableNoOp()
|
735
735
|
|
736
|
-
drun.log_artifact = _ChainableNoOpField()
|
736
|
+
drun.log_artifact = _ChainableNoOpField() # type: ignore[method-assign]
|
737
737
|
# attributes
|
738
738
|
drun._start_time = time.time()
|
739
739
|
drun._starting_step = 0
|
@@ -793,12 +793,7 @@ class _WandbInit:
|
|
793
793
|
else:
|
794
794
|
service = None
|
795
795
|
|
796
|
-
|
797
|
-
backend = Backend(
|
798
|
-
settings=settings,
|
799
|
-
service=service,
|
800
|
-
mailbox=mailbox,
|
801
|
-
)
|
796
|
+
backend = Backend(settings=settings, service=service)
|
802
797
|
backend.ensure_launched()
|
803
798
|
self._logger.info("backend started and connected")
|
804
799
|
|
@@ -1085,8 +1080,7 @@ def _attach(
|
|
1085
1080
|
)
|
1086
1081
|
|
1087
1082
|
# TODO: consolidate this codepath with wandb.init()
|
1088
|
-
|
1089
|
-
backend = Backend(settings=settings, service=service, mailbox=mailbox)
|
1083
|
+
backend = Backend(settings=settings, service=service)
|
1090
1084
|
backend.ensure_launched()
|
1091
1085
|
logger.info("attach backend started and connected")
|
1092
1086
|
|
@@ -1468,6 +1462,9 @@ def init( # noqa: C901
|
|
1468
1462
|
_monkeypatch_tensorboard()
|
1469
1463
|
init_telemetry.feature.tensorboard_sync = True
|
1470
1464
|
|
1465
|
+
if run_settings.x_server_side_derived_summary:
|
1466
|
+
init_telemetry.feature.server_side_derived_summary = True
|
1467
|
+
|
1471
1468
|
return wi.init(run_settings, run_config)
|
1472
1469
|
|
1473
1470
|
except KeyboardInterrupt as e:
|