wandb 0.19.4rc1__py3-none-win32.whl → 0.19.6rc4__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 +1 -8
- wandb/_iterutils.py +46 -0
- wandb/apis/internal.py +4 -0
- wandb/apis/normalize.py +13 -5
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +9 -2
- wandb/proto/v3/wandb_internal_pb2.py +36 -36
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_internal_pb2.py +36 -36
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_internal_pb2.py +36 -36
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/sdk/artifacts/artifact.py +120 -8
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +12 -5
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +1 -1
- wandb/sdk/backend/backend.py +7 -11
- wandb/sdk/data_types/base_types/wb_value.py +10 -10
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +2 -2
- wandb/sdk/data_types/helper_types/image_mask.py +2 -2
- wandb/sdk/data_types/image.py +0 -3
- wandb/sdk/data_types/saved_model.py +1 -1
- wandb/sdk/data_types/utils.py +2 -6
- wandb/sdk/interface/interface.py +26 -12
- wandb/sdk/interface/interface_sock.py +7 -11
- wandb/sdk/internal/internal_api.py +9 -1
- wandb/sdk/internal/sender.py +2 -2
- wandb/sdk/internal/system/assets/cpu.py +1 -1
- wandb/sdk/lib/apikey.py +7 -19
- wandb/sdk/lib/mailbox.py +0 -14
- wandb/sdk/lib/retry.py +6 -3
- wandb/sdk/lib/run_moment.py +19 -7
- wandb/sdk/lib/server.py +20 -0
- wandb/sdk/lib/service_connection.py +2 -2
- wandb/sdk/wandb_init.py +71 -46
- wandb/sdk/wandb_login.py +86 -110
- wandb/sdk/wandb_metadata.py +60 -31
- wandb/sdk/wandb_run.py +32 -45
- wandb/sdk/wandb_settings.py +465 -143
- wandb/sdk/wandb_setup.py +10 -22
- wandb/util.py +44 -12
- {wandb-0.19.4rc1.dist-info → wandb-0.19.6rc4.dist-info}/METADATA +1 -1
- {wandb-0.19.4rc1.dist-info → wandb-0.19.6rc4.dist-info}/RECORD +47 -46
- {wandb-0.19.4rc1.dist-info → wandb-0.19.6rc4.dist-info}/WHEEL +0 -0
- {wandb-0.19.4rc1.dist-info → wandb-0.19.6rc4.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.4rc1.dist-info → wandb-0.19.6rc4.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/backend/backend.py
CHANGED
@@ -25,8 +25,6 @@ if TYPE_CHECKING:
|
|
25
25
|
from wandb.proto.wandb_internal_pb2 import Record, Result
|
26
26
|
from wandb.sdk.lib import service_connection
|
27
27
|
|
28
|
-
from ..wandb_run import Run
|
29
|
-
|
30
28
|
RecordQueue = Union["queue.Queue[Record]", multiprocessing.Queue[Record]]
|
31
29
|
ResultQueue = Union["queue.Queue[Result]", multiprocessing.Queue[Result]]
|
32
30
|
|
@@ -54,7 +52,7 @@ class Backend:
|
|
54
52
|
interface: Optional[InterfaceBase]
|
55
53
|
_internal_pid: Optional[int]
|
56
54
|
wandb_process: Optional[multiprocessing.process.BaseProcess]
|
57
|
-
_settings:
|
55
|
+
_settings: Settings
|
58
56
|
record_q: Optional["RecordQueue"]
|
59
57
|
result_q: Optional["ResultQueue"]
|
60
58
|
_mailbox: Mailbox
|
@@ -62,7 +60,7 @@ class Backend:
|
|
62
60
|
def __init__(
|
63
61
|
self,
|
64
62
|
mailbox: Mailbox,
|
65
|
-
settings:
|
63
|
+
settings: Settings,
|
66
64
|
log_level: Optional[int] = None,
|
67
65
|
service: "Optional[service_connection.ServiceConnection]" = None,
|
68
66
|
) -> None:
|
@@ -84,12 +82,7 @@ class Backend:
|
|
84
82
|
self._save_mod_path: Optional[str] = None
|
85
83
|
self._save_mod_spec = None
|
86
84
|
|
87
|
-
def _hack_set_run(self, run: "Run") -> None:
|
88
|
-
assert self.interface
|
89
|
-
self.interface._hack_set_run(run)
|
90
|
-
|
91
85
|
def _multiprocessing_setup(self) -> None:
|
92
|
-
assert self._settings
|
93
86
|
if self._settings.start_method == "thread":
|
94
87
|
return
|
95
88
|
|
@@ -141,10 +134,13 @@ class Backend:
|
|
141
134
|
def ensure_launched(self) -> None:
|
142
135
|
"""Launch backend worker if not running."""
|
143
136
|
if self._service:
|
144
|
-
|
137
|
+
assert self._settings.run_id
|
138
|
+
self.interface = self._service.make_interface(
|
139
|
+
self._mailbox,
|
140
|
+
stream_id=self._settings.run_id,
|
141
|
+
)
|
145
142
|
return
|
146
143
|
|
147
|
-
assert self._settings
|
148
144
|
settings = self._settings.model_copy()
|
149
145
|
settings.x_log_level = self._log_level or logging.DEBUG
|
150
146
|
|
@@ -24,16 +24,16 @@ def _server_accepts_client_ids() -> bool:
|
|
24
24
|
# The latest SDK version that is < "0.11.0" was released on 2021/06/29.
|
25
25
|
# AS OF NOW, 2024/11/06, we assume that all customer's server deployments accept
|
26
26
|
# client IDs.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
27
|
+
|
28
|
+
if util._is_offline():
|
29
|
+
# If there are any users with issues on an older backend, customers can disable the
|
30
|
+
# setting `allow_offline_artifacts` to revert the SDK's behavior back to not
|
31
|
+
# using client IDs in offline mode.
|
32
|
+
if wandb.run and not wandb.run.settings.allow_offline_artifacts:
|
33
|
+
return False
|
34
|
+
# Assume client IDs are accepted
|
35
|
+
else:
|
36
|
+
return True
|
37
37
|
|
38
38
|
# If the script is online, request the max_cli_version and ensure the server
|
39
39
|
# is of a high enough version.
|
@@ -48,7 +48,7 @@ class BoundingBoxes2D(JSONMetadata):
|
|
48
48
|
|
49
49
|
Examples:
|
50
50
|
### Log bounding boxes for a single image
|
51
|
-
|
51
|
+
|
52
52
|
```python
|
53
53
|
import numpy as np
|
54
54
|
import wandb
|
@@ -94,7 +94,7 @@ class BoundingBoxes2D(JSONMetadata):
|
|
94
94
|
```
|
95
95
|
|
96
96
|
### Log a bounding box overlay to a Table
|
97
|
-
|
97
|
+
|
98
98
|
```python
|
99
99
|
import numpy as np
|
100
100
|
import wandb
|
@@ -33,7 +33,7 @@ class ImageMask(Media):
|
|
33
33
|
|
34
34
|
Examples:
|
35
35
|
### Logging a single masked image
|
36
|
-
|
36
|
+
|
37
37
|
```python
|
38
38
|
import numpy as np
|
39
39
|
import wandb
|
@@ -69,7 +69,7 @@ class ImageMask(Media):
|
|
69
69
|
```
|
70
70
|
|
71
71
|
### Log a masked image inside a Table
|
72
|
-
|
72
|
+
|
73
73
|
```python
|
74
74
|
import numpy as np
|
75
75
|
import wandb
|
wandb/sdk/data_types/image.py
CHANGED
@@ -77,7 +77,6 @@ class Image(BatchableMedia):
|
|
77
77
|
|
78
78
|
Examples:
|
79
79
|
### Create a wandb.Image from a numpy array
|
80
|
-
<!--yeadoc-test:log-image-numpy-->
|
81
80
|
```python
|
82
81
|
import numpy as np
|
83
82
|
import wandb
|
@@ -92,7 +91,6 @@ class Image(BatchableMedia):
|
|
92
91
|
```
|
93
92
|
|
94
93
|
### Create a wandb.Image from a PILImage
|
95
|
-
<!--yeadoc-test:log-image-pillow-->
|
96
94
|
```python
|
97
95
|
import numpy as np
|
98
96
|
from PIL import Image as PILImage
|
@@ -111,7 +109,6 @@ class Image(BatchableMedia):
|
|
111
109
|
```
|
112
110
|
|
113
111
|
### log .jpg rather than .png (default)
|
114
|
-
<!--yeadoc-test:log-image-format-->
|
115
112
|
```python
|
116
113
|
import numpy as np
|
117
114
|
import wandb
|
@@ -362,7 +362,7 @@ class _PytorchSavedModel(_PicklingSavedModel["torch.nn.Module"]):
|
|
362
362
|
|
363
363
|
@staticmethod
|
364
364
|
def _deserialize(dir_or_file_path: str) -> "torch.nn.Module":
|
365
|
-
return _get_torch().load(dir_or_file_path)
|
365
|
+
return _get_torch().load(dir_or_file_path, weights_only=False)
|
366
366
|
|
367
367
|
@staticmethod
|
368
368
|
def _validate_obj(obj: Any) -> bool:
|
wandb/sdk/data_types/utils.py
CHANGED
@@ -148,12 +148,8 @@ def val_to_json(
|
|
148
148
|
"partitioned-table",
|
149
149
|
"joined-table",
|
150
150
|
]:
|
151
|
-
#
|
152
|
-
|
153
|
-
# files in an artifact
|
154
|
-
# we sanitize the key to meet the constraints
|
155
|
-
# in this case, leaving only alphanumerics or underscores.
|
156
|
-
sanitized_key = re.sub(r"[^a-zA-Z0-9_]+", "", key)
|
151
|
+
# Sanitize the key to meet the constraints of artifact names.
|
152
|
+
sanitized_key = re.sub(r"[^a-zA-Z0-9_\-.]+", "", key)
|
157
153
|
art = wandb.Artifact(f"run-{run.id}-{sanitized_key}", "run_table")
|
158
154
|
art.add(val, key)
|
159
155
|
run.log_artifact(art)
|
wandb/sdk/interface/interface.py
CHANGED
@@ -90,16 +90,11 @@ def file_enum_to_policy(enum: "pb.FilesItem.PolicyType.V") -> "PolicyName":
|
|
90
90
|
|
91
91
|
|
92
92
|
class InterfaceBase:
|
93
|
-
_run: Optional["Run"]
|
94
93
|
_drop: bool
|
95
94
|
|
96
95
|
def __init__(self) -> None:
|
97
|
-
self._run = None
|
98
96
|
self._drop = False
|
99
97
|
|
100
|
-
def _hack_set_run(self, run: "Run") -> None:
|
101
|
-
self._run = run
|
102
|
-
|
103
98
|
def publish_header(self) -> None:
|
104
99
|
header = pb.HeaderRecord()
|
105
100
|
self._publish_header(header)
|
@@ -232,7 +227,12 @@ class InterfaceBase:
|
|
232
227
|
update.value_json = json.dumps(v)
|
233
228
|
return summary
|
234
229
|
|
235
|
-
def _summary_encode(
|
230
|
+
def _summary_encode(
|
231
|
+
self,
|
232
|
+
value: Any,
|
233
|
+
path_from_root: str,
|
234
|
+
run: "Run",
|
235
|
+
) -> dict:
|
236
236
|
"""Normalize, compress, and encode sub-objects for backend storage.
|
237
237
|
|
238
238
|
value: Object to encode.
|
@@ -250,12 +250,14 @@ class InterfaceBase:
|
|
250
250
|
json_value = {}
|
251
251
|
for key, value in value.items(): # noqa: B020
|
252
252
|
json_value[key] = self._summary_encode(
|
253
|
-
value,
|
253
|
+
value,
|
254
|
+
path_from_root + "." + key,
|
255
|
+
run=run,
|
254
256
|
)
|
255
257
|
return json_value
|
256
258
|
else:
|
257
259
|
friendly_value, converted = json_friendly(
|
258
|
-
val_to_json(
|
260
|
+
val_to_json(run, path_from_root, value, namespace="summary")
|
259
261
|
)
|
260
262
|
json_value, compressed = maybe_compress_summary(
|
261
263
|
friendly_value, get_h5_typename(value)
|
@@ -267,7 +269,11 @@ class InterfaceBase:
|
|
267
269
|
|
268
270
|
return json_value
|
269
271
|
|
270
|
-
def _make_summary(
|
272
|
+
def _make_summary(
|
273
|
+
self,
|
274
|
+
summary_record: sr.SummaryRecord,
|
275
|
+
run: "Run",
|
276
|
+
) -> pb.SummaryRecord:
|
271
277
|
pb_summary_record = pb.SummaryRecord()
|
272
278
|
|
273
279
|
for item in summary_record.update:
|
@@ -282,7 +288,11 @@ class InterfaceBase:
|
|
282
288
|
pb_summary_item.key = item.key[0]
|
283
289
|
|
284
290
|
path_from_root = ".".join(item.key)
|
285
|
-
json_value = self._summary_encode(
|
291
|
+
json_value = self._summary_encode(
|
292
|
+
item.value,
|
293
|
+
path_from_root,
|
294
|
+
run=run,
|
295
|
+
)
|
286
296
|
json_value, _ = json_friendly(json_value) # type: ignore
|
287
297
|
|
288
298
|
pb_summary_item.value_json = json.dumps(
|
@@ -303,8 +313,12 @@ class InterfaceBase:
|
|
303
313
|
|
304
314
|
return pb_summary_record
|
305
315
|
|
306
|
-
def publish_summary(
|
307
|
-
|
316
|
+
def publish_summary(
|
317
|
+
self,
|
318
|
+
run: "Run",
|
319
|
+
summary_record: sr.SummaryRecord,
|
320
|
+
) -> None:
|
321
|
+
pb_summary_record = self._make_summary(summary_record, run=run)
|
308
322
|
self._publish_summary(pb_summary_record)
|
309
323
|
|
310
324
|
@abstractmethod
|
@@ -16,32 +16,28 @@ from .router_sock import MessageSockRouter
|
|
16
16
|
if TYPE_CHECKING:
|
17
17
|
from wandb.proto import wandb_internal_pb2 as pb
|
18
18
|
|
19
|
-
from ..wandb_run import Run
|
20
|
-
|
21
19
|
|
22
20
|
logger = logging.getLogger("wandb")
|
23
21
|
|
24
22
|
|
25
23
|
class InterfaceSock(InterfaceShared):
|
26
|
-
_stream_id: Optional[str]
|
27
|
-
_sock_client: SockClient
|
28
24
|
_mailbox: Mailbox
|
29
25
|
|
30
|
-
def __init__(
|
26
|
+
def __init__(
|
27
|
+
self,
|
28
|
+
sock_client: SockClient,
|
29
|
+
mailbox: Mailbox,
|
30
|
+
stream_id: str,
|
31
|
+
) -> None:
|
31
32
|
# _sock_client is used when abstract method _init_router() is called by constructor
|
32
33
|
self._sock_client = sock_client
|
33
34
|
super().__init__(mailbox=mailbox)
|
34
35
|
self._process_check = False
|
35
|
-
self._stream_id =
|
36
|
+
self._stream_id = stream_id
|
36
37
|
|
37
38
|
def _init_router(self) -> None:
|
38
39
|
self._router = MessageSockRouter(self._sock_client, mailbox=self._mailbox)
|
39
40
|
|
40
|
-
def _hack_set_run(self, run: "Run") -> None:
|
41
|
-
super()._hack_set_run(run)
|
42
|
-
assert run._settings.run_id
|
43
|
-
self._stream_id = run._settings.run_id
|
44
|
-
|
45
41
|
def _assign(self, record: Any) -> None:
|
46
42
|
assert self._stream_id
|
47
43
|
record._info.stream_id = self._stream_id
|
@@ -243,6 +243,7 @@ class Api:
|
|
243
243
|
),
|
244
244
|
environ: MutableMapping = os.environ,
|
245
245
|
retry_callback: Optional[Callable[[int, str], Any]] = None,
|
246
|
+
api_key: Optional[str] = None,
|
246
247
|
) -> None:
|
247
248
|
self._environ = environ
|
248
249
|
self._global_context = context.Context()
|
@@ -284,7 +285,9 @@ class Api:
|
|
284
285
|
self._extra_http_headers.update(_thread_local_api_settings.headers or {})
|
285
286
|
|
286
287
|
auth = None
|
287
|
-
if
|
288
|
+
if api_key:
|
289
|
+
auth = ("api", api_key)
|
290
|
+
elif self.access_token is not None:
|
288
291
|
self._extra_http_headers["Authorization"] = f"Bearer {self.access_token}"
|
289
292
|
elif _thread_local_api_settings.cookies is None:
|
290
293
|
auth = ("api", self.api_key or "")
|
@@ -400,6 +403,11 @@ class Api:
|
|
400
403
|
wandb.termerror(f"Error while calling W&B API: {error} ({response})")
|
401
404
|
raise
|
402
405
|
|
406
|
+
def validate_api_key(self) -> bool:
|
407
|
+
"""Returns whether the API key stored on initialization is valid."""
|
408
|
+
res = self.execute(gql("query { viewer { id } }"))
|
409
|
+
return res is not None and res["viewer"] is not None
|
410
|
+
|
403
411
|
def set_current_run_id(self, run_id: str) -> None:
|
404
412
|
self._current_run_id = run_id
|
405
413
|
|
wandb/sdk/internal/sender.py
CHANGED
@@ -52,9 +52,9 @@ from wandb.sdk.lib import (
|
|
52
52
|
filesystem,
|
53
53
|
proto_util,
|
54
54
|
redirect,
|
55
|
+
retry,
|
55
56
|
telemetry,
|
56
57
|
)
|
57
|
-
from wandb.sdk.lib.mailbox import ContextCancelledError
|
58
58
|
from wandb.sdk.lib.proto_util import message_to_dict
|
59
59
|
|
60
60
|
if TYPE_CHECKING:
|
@@ -388,7 +388,7 @@ class SendManager:
|
|
388
388
|
try:
|
389
389
|
self._api.set_local_context(api_context)
|
390
390
|
send_handler(record)
|
391
|
-
except
|
391
|
+
except retry.RetryCancelledError:
|
392
392
|
logger.debug(f"Record cancelled: {record_type}")
|
393
393
|
self._context_keeper.release(context_id)
|
394
394
|
finally:
|
@@ -118,7 +118,7 @@ class CPU:
|
|
118
118
|
self.name: str = self.__class__.__name__.lower()
|
119
119
|
self.metrics: List[Metric] = [
|
120
120
|
ProcessCpuPercent(settings.x_stats_pid),
|
121
|
-
CpuPercent(),
|
121
|
+
# CpuPercent(),
|
122
122
|
ProcessCpuThreads(settings.x_stats_pid),
|
123
123
|
]
|
124
124
|
self.metrics_monitor: MetricsMonitor = MetricsMonitor(
|
wandb/sdk/lib/apikey.py
CHANGED
@@ -104,7 +104,6 @@ def prompt_api_key( # noqa: C901
|
|
104
104
|
log_string = term.LOG_STRING_NOCOLOR
|
105
105
|
key = wandb.jupyter.attempt_colab_login(app_url) # type: ignore
|
106
106
|
if key is not None:
|
107
|
-
write_key(settings, key, api=api)
|
108
107
|
return key # type: ignore
|
109
108
|
|
110
109
|
if anon_mode == "must":
|
@@ -123,24 +122,20 @@ def prompt_api_key( # noqa: C901
|
|
123
122
|
choices, input_timeout=settings.login_timeout, jupyter=jupyter
|
124
123
|
)
|
125
124
|
|
125
|
+
key = None
|
126
126
|
api_ask = (
|
127
|
-
f"{log_string}: Paste an API key from your profile and hit enter
|
128
|
-
|
127
|
+
f"{log_string}: Paste an API key from your profile and hit enter"
|
128
|
+
if jupyter
|
129
|
+
else f"{log_string}: Paste an API key from your profile and hit enter, or press ctrl+c to quit"
|
129
130
|
)
|
130
131
|
if result == LOGIN_CHOICE_ANON:
|
131
132
|
key = api.create_anonymous_api_key()
|
132
|
-
|
133
|
-
write_key(settings, key, api=api, anonymous=True)
|
134
|
-
return key # type: ignore
|
135
133
|
elif result == LOGIN_CHOICE_NEW:
|
136
134
|
key = browser_callback(signup=True) if browser_callback else None
|
137
135
|
|
138
136
|
if not key:
|
139
137
|
wandb.termlog(f"Create an account here: {app_url}/authorize?signup=true")
|
140
138
|
key = input_callback(api_ask).strip()
|
141
|
-
|
142
|
-
write_key(settings, key, api=api)
|
143
|
-
return key # type: ignore
|
144
139
|
elif result == LOGIN_CHOICE_EXISTS:
|
145
140
|
key = browser_callback() if browser_callback else None
|
146
141
|
|
@@ -158,8 +153,6 @@ def prompt_api_key( # noqa: C901
|
|
158
153
|
f"You can find your API key in your browser here: {app_url}/authorize"
|
159
154
|
)
|
160
155
|
key = input_callback(api_ask).strip()
|
161
|
-
write_key(settings, key, api=api)
|
162
|
-
return key # type: ignore
|
163
156
|
elif result == LOGIN_CHOICE_NOTTY:
|
164
157
|
# TODO: Needs refactor as this needs to be handled by caller
|
165
158
|
return False
|
@@ -172,8 +165,9 @@ def prompt_api_key( # noqa: C901
|
|
172
165
|
browser_callback() if jupyter and browser_callback else (None, False)
|
173
166
|
)
|
174
167
|
|
175
|
-
|
176
|
-
|
168
|
+
if not key:
|
169
|
+
raise ValueError("No API key specified.")
|
170
|
+
return key
|
177
171
|
|
178
172
|
|
179
173
|
def write_netrc(host: str, entity: str, key: str) -> Optional[bool]:
|
@@ -232,7 +226,6 @@ def write_key(
|
|
232
226
|
settings: "Settings",
|
233
227
|
key: Optional[str],
|
234
228
|
api: Optional["InternalApi"] = None,
|
235
|
-
anonymous: bool = False,
|
236
229
|
) -> None:
|
237
230
|
if not key:
|
238
231
|
raise ValueError("No API key specified.")
|
@@ -249,11 +242,6 @@ def write_key(
|
|
249
242
|
"API key must be 40 characters long, yours was {}".format(len(key))
|
250
243
|
)
|
251
244
|
|
252
|
-
if anonymous:
|
253
|
-
api.set_setting("anonymous", "must", globally=True, persist=True)
|
254
|
-
else:
|
255
|
-
api.clear_setting("anonymous", globally=True, persist=True)
|
256
|
-
|
257
245
|
write_netrc(settings.base_url, "user", key)
|
258
246
|
|
259
247
|
|
wandb/sdk/lib/mailbox.py
CHANGED
@@ -24,10 +24,6 @@ class MailboxError(Error):
|
|
24
24
|
"""Generic Mailbox Exception."""
|
25
25
|
|
26
26
|
|
27
|
-
class ContextCancelledError(MailboxError):
|
28
|
-
"""Context cancelled Exception."""
|
29
|
-
|
30
|
-
|
31
27
|
class _MailboxWaitAll:
|
32
28
|
_event: threading.Event
|
33
29
|
_lock: threading.Lock
|
@@ -341,16 +337,6 @@ class Mailbox:
|
|
341
337
|
def enable_keepalive(self) -> None:
|
342
338
|
self._keepalive = True
|
343
339
|
|
344
|
-
def wait(
|
345
|
-
self,
|
346
|
-
handle: MailboxHandle,
|
347
|
-
*,
|
348
|
-
timeout: float,
|
349
|
-
on_progress: Optional[Callable[[MailboxProgress], None]] = None,
|
350
|
-
cancel: bool = False,
|
351
|
-
) -> Optional[pb.Result]:
|
352
|
-
return handle.wait(timeout=timeout, on_progress=on_progress, cancel=cancel)
|
353
|
-
|
354
340
|
def _time(self) -> float:
|
355
341
|
return time.monotonic()
|
356
342
|
|
wandb/sdk/lib/retry.py
CHANGED
@@ -12,10 +12,9 @@ from typing import Any, Awaitable, Callable, Generic, Optional, Tuple, Type, Typ
|
|
12
12
|
from requests import HTTPError
|
13
13
|
|
14
14
|
import wandb
|
15
|
+
import wandb.errors
|
15
16
|
from wandb.util import CheckRetryFnType
|
16
17
|
|
17
|
-
from .mailbox import ContextCancelledError
|
18
|
-
|
19
18
|
logger = logging.getLogger(__name__)
|
20
19
|
|
21
20
|
|
@@ -26,6 +25,10 @@ SLEEP_FN = time.sleep
|
|
26
25
|
SLEEP_ASYNC_FN = asyncio.sleep
|
27
26
|
|
28
27
|
|
28
|
+
class RetryCancelledError(wandb.errors.Error):
|
29
|
+
"""A retry did not occur because it was cancelled."""
|
30
|
+
|
31
|
+
|
29
32
|
class TransientError(Exception):
|
30
33
|
"""Exception type designated for errors that may only be temporary.
|
31
34
|
|
@@ -191,7 +194,7 @@ class Retry(Generic[_R]):
|
|
191
194
|
sleep + random.random() * 0.25 * sleep, cancel_event=retry_cancel_event
|
192
195
|
)
|
193
196
|
if cancelled:
|
194
|
-
raise
|
197
|
+
raise RetryCancelledError("retry timeout")
|
195
198
|
sleep *= 2
|
196
199
|
if sleep > self.MAX_SLEEP_SECONDS:
|
197
200
|
sleep = self.MAX_SLEEP_SECONDS
|
wandb/sdk/lib/run_moment.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from dataclasses import dataclass
|
2
|
-
from typing import Literal,
|
4
|
+
from typing import Literal, cast
|
3
5
|
from urllib import parse
|
4
6
|
|
5
7
|
_STEP = Literal["_step"]
|
@@ -7,15 +9,25 @@ _STEP = Literal["_step"]
|
|
7
9
|
|
8
10
|
@dataclass
|
9
11
|
class RunMoment:
|
10
|
-
"""A moment in a run.
|
12
|
+
"""A moment in a run.
|
13
|
+
|
14
|
+
Defines a branching point in a finished run to fork or resume from.
|
15
|
+
A run moment is identified by a run ID and a metric value.
|
16
|
+
Currently, only the metric '_step' is supported.
|
17
|
+
"""
|
11
18
|
|
12
|
-
run: str
|
19
|
+
run: str
|
20
|
+
"""run ID"""
|
13
21
|
|
14
|
-
|
15
|
-
|
22
|
+
value: int | float
|
23
|
+
"""Value of the metric."""
|
16
24
|
|
17
|
-
# only step for now, in future this will be relaxed to be any metric
|
18
25
|
metric: _STEP = "_step"
|
26
|
+
"""Metric to use to determine the moment in the run.
|
27
|
+
|
28
|
+
Currently, only the metric '_step' is supported.
|
29
|
+
In future, this will be relaxed to be any metric.
|
30
|
+
"""
|
19
31
|
|
20
32
|
def __post_init__(self):
|
21
33
|
if self.metric != "_step":
|
@@ -30,7 +42,7 @@ class RunMoment:
|
|
30
42
|
raise ValueError(f"Only string run names are supported, got '{self.run}'.")
|
31
43
|
|
32
44
|
@classmethod
|
33
|
-
def from_uri(cls, uri: str) ->
|
45
|
+
def from_uri(cls, uri: str) -> RunMoment:
|
34
46
|
parsable = "runmoment://" + uri
|
35
47
|
parse_err = ValueError(
|
36
48
|
f"Could not parse passed run moment string '{uri}', "
|
wandb/sdk/lib/server.py
CHANGED
@@ -25,6 +25,7 @@ class Server:
|
|
25
25
|
def query_with_timeout(self, timeout: int | float = 5) -> None:
|
26
26
|
if self._settings.x_disable_viewer:
|
27
27
|
return
|
28
|
+
|
28
29
|
async_viewer = util.async_call(self._api.viewer_server_info, timeout=timeout)
|
29
30
|
try:
|
30
31
|
viewer_tuple, viewer_thread = async_viewer()
|
@@ -36,3 +37,22 @@ class Server:
|
|
36
37
|
# TODO(jhr): should we kill the thread?
|
37
38
|
self._viewer, self._serverinfo = viewer_tuple
|
38
39
|
self._flags = json.loads(self._viewer.get("flags", "{}"))
|
40
|
+
|
41
|
+
@property
|
42
|
+
def viewer(self) -> dict[str, Any]:
|
43
|
+
"""Returns information about the currently authenticated user.
|
44
|
+
|
45
|
+
If the API key is valid, the following is returned:
|
46
|
+
- id
|
47
|
+
- entity
|
48
|
+
- username
|
49
|
+
- flags
|
50
|
+
- teams
|
51
|
+
|
52
|
+
If the API key is not valid or the server is not reachable,
|
53
|
+
an empty dict is returned.
|
54
|
+
"""
|
55
|
+
if not self._viewer and not self._settings._offline:
|
56
|
+
self.query_with_timeout()
|
57
|
+
|
58
|
+
return self._viewer
|
@@ -124,9 +124,9 @@ class ServiceConnection:
|
|
124
124
|
self._torn_down = False
|
125
125
|
self._cleanup = cleanup
|
126
126
|
|
127
|
-
def make_interface(self, mailbox: Mailbox) -> InterfaceBase:
|
127
|
+
def make_interface(self, mailbox: Mailbox, stream_id: str) -> InterfaceBase:
|
128
128
|
"""Returns an interface for communicating with the service."""
|
129
|
-
return InterfaceSock(self._client, mailbox)
|
129
|
+
return InterfaceSock(self._client, mailbox, stream_id=stream_id)
|
130
130
|
|
131
131
|
def send_record(self, record: pb.Record) -> None:
|
132
132
|
"""Sends data to the service."""
|