wandb 0.18.3__py3-none-macosx_11_0_arm64.whl → 0.18.5__py3-none-macosx_11_0_arm64.whl
Sign up to get free protection for your applications and to get access to all the features.
- wandb/__init__.py +16 -7
- wandb/__init__.pyi +96 -63
- wandb/analytics/sentry.py +91 -88
- wandb/apis/public/api.py +18 -4
- wandb/apis/public/runs.py +53 -2
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/beta.py +178 -0
- wandb/cli/cli.py +5 -171
- wandb/data_types.py +3 -0
- wandb/env.py +74 -73
- wandb/errors/term.py +300 -43
- wandb/proto/v3/wandb_internal_pb2.py +263 -223
- wandb/proto/v3/wandb_server_pb2.py +57 -37
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_internal_pb2.py +226 -218
- wandb/proto/v4/wandb_server_pb2.py +41 -37
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_internal_pb2.py +226 -218
- wandb/proto/v5/wandb_server_pb2.py +41 -37
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/sdk/__init__.py +3 -3
- wandb/sdk/artifacts/_validators.py +41 -8
- wandb/sdk/artifacts/artifact.py +32 -1
- wandb/sdk/artifacts/artifact_file_cache.py +1 -2
- wandb/sdk/data_types/_dtypes.py +7 -3
- wandb/sdk/data_types/video.py +15 -6
- wandb/sdk/interface/interface.py +2 -0
- wandb/sdk/internal/internal_api.py +126 -5
- wandb/sdk/internal/sender.py +16 -3
- wandb/sdk/launch/inputs/internal.py +1 -1
- wandb/sdk/lib/module.py +12 -0
- wandb/sdk/lib/printer.py +291 -105
- wandb/sdk/lib/progress.py +274 -0
- wandb/sdk/service/streams.py +21 -11
- wandb/sdk/wandb_init.py +58 -54
- wandb/sdk/wandb_run.py +380 -454
- wandb/sdk/wandb_settings.py +2 -0
- wandb/sdk/wandb_watch.py +17 -11
- wandb/util.py +6 -2
- {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/METADATA +4 -3
- {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/RECORD +45 -43
- wandb/bin/apple_gpu_stats +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/WHEEL +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/entry_points.txt +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/licenses/LICENSE +0 -0
wandb/errors/term.py
CHANGED
@@ -1,23 +1,78 @@
|
|
1
|
+
"""Global functions for printing to stderr for wandb."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import contextlib
|
1
6
|
import logging
|
7
|
+
import os
|
2
8
|
import sys
|
3
|
-
|
9
|
+
import threading
|
10
|
+
from typing import TYPE_CHECKING, Iterator
|
11
|
+
|
12
|
+
if sys.version_info < (3, 8):
|
13
|
+
from typing_extensions import Protocol
|
14
|
+
else:
|
15
|
+
from typing import Protocol
|
4
16
|
|
5
17
|
import click
|
6
18
|
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
import wandb
|
21
|
+
|
7
22
|
LOG_STRING = click.style("wandb", fg="blue", bold=True)
|
8
23
|
LOG_STRING_NOCOLOR = "wandb"
|
9
24
|
ERROR_STRING = click.style("ERROR", bg="red", fg="green")
|
10
25
|
WARN_STRING = click.style("WARNING", fg="yellow")
|
11
|
-
PRINTED_MESSAGES = set() # type: ignore
|
12
26
|
|
13
|
-
_silent = False
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
27
|
+
_silent: bool = False
|
28
|
+
"""If true, _logger is used instead of printing to stderr."""
|
29
|
+
|
30
|
+
_logger: SupportsLeveledLogging | None = None
|
31
|
+
"""A fallback logger for _silent mode."""
|
32
|
+
|
33
|
+
_show_info: bool = True
|
34
|
+
"""If false, then termlog() uses silent mode (see _silent)."""
|
35
|
+
|
36
|
+
_show_warnings: bool = True
|
37
|
+
"""If false, then termwarn() uses silent mode (see _silent)."""
|
38
|
+
|
39
|
+
_show_errors: bool = True
|
40
|
+
"""If false, then termerror() uses silent mode (see _silent)."""
|
41
|
+
|
42
|
+
|
43
|
+
_printed_messages: set[str] = set()
|
44
|
+
"""Messages logged with repeat=False."""
|
45
|
+
|
46
|
+
_dynamic_text_lock = threading.Lock()
|
47
|
+
"""Lock held for dynamic text operations.
|
48
|
+
|
49
|
+
All uses of `_dynamic_blocks` and calls to functions that start with
|
50
|
+
the `_l_` prefix must be guarded by this lock.
|
51
|
+
"""
|
18
52
|
|
53
|
+
_dynamic_blocks: list[DynamicBlock] = []
|
54
|
+
"""Active dynamic text areas, created with dynamic_text()."""
|
19
55
|
|
20
|
-
|
56
|
+
|
57
|
+
class SupportsLeveledLogging(Protocol):
|
58
|
+
"""Portion of the standard logging.Logger used in this module."""
|
59
|
+
|
60
|
+
def info(self, msg: str) -> None: ...
|
61
|
+
def warning(self, msg: str) -> None: ...
|
62
|
+
def error(self, msg: str) -> None: ...
|
63
|
+
|
64
|
+
|
65
|
+
def termsetup(
|
66
|
+
settings: wandb.Settings,
|
67
|
+
logger: SupportsLeveledLogging | None,
|
68
|
+
) -> None:
|
69
|
+
"""Configure the global logging functions.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
settings: The settings object passed to wandb.setup() or wandb.init().
|
73
|
+
logger: A fallback logger to use for "silent" mode. In this mode,
|
74
|
+
the logger is used instead of printing to stderr.
|
75
|
+
"""
|
21
76
|
global _silent, _show_info, _show_warnings, _show_errors, _logger
|
22
77
|
_silent = settings.silent
|
23
78
|
_show_info = settings.show_info
|
@@ -26,21 +81,98 @@ def termsetup(settings, logger) -> None:
|
|
26
81
|
_logger = logger
|
27
82
|
|
28
83
|
|
84
|
+
@contextlib.contextmanager
|
85
|
+
def dynamic_text() -> Iterator[DynamicBlock | None]:
|
86
|
+
"""A context manager that provides a handle to a new dynamic text area.
|
87
|
+
|
88
|
+
The text goes to stderr. Returns None if dynamic text is not supported.
|
89
|
+
|
90
|
+
Dynamic text must only be used while `wandb` has control of the terminal,
|
91
|
+
or else text written by other programs will be overwritten. It's
|
92
|
+
appropriate to use during a blocking operation.
|
93
|
+
|
94
|
+
```
|
95
|
+
with term.dynamic_text() as text_area:
|
96
|
+
if text_area:
|
97
|
+
text_area.set_text("Writing to a terminal.")
|
98
|
+
for i in range(2000):
|
99
|
+
text_area.set_text(f"Still going... ({i}/2000)")
|
100
|
+
time.sleep(0.001)
|
101
|
+
else:
|
102
|
+
wandb.termlog("Writing to a file or dumb terminal.")
|
103
|
+
time.sleep(1)
|
104
|
+
wandb.termlog("Finished 1000/2000 tasks, still working...")
|
105
|
+
time.sleep(1)
|
106
|
+
wandb.termlog("Done!", err=True)
|
107
|
+
```
|
108
|
+
"""
|
109
|
+
# For now, dynamic text always corresponds to the "INFO" level.
|
110
|
+
if _silent or not _show_info:
|
111
|
+
yield None
|
112
|
+
return
|
113
|
+
|
114
|
+
# NOTE: In Jupyter notebooks, this will return False. Notebooks
|
115
|
+
# support ANSI color sequences and the '\r' character, but not
|
116
|
+
# cursor motions or line clear commands.
|
117
|
+
if not _sys_stderr_isatty():
|
118
|
+
yield None
|
119
|
+
return
|
120
|
+
|
121
|
+
# This is a convention to indicate that the terminal doesn't support
|
122
|
+
# clearing the screen / positioning the cursor.
|
123
|
+
if os.environ.get("TERM") == "dumb":
|
124
|
+
yield None
|
125
|
+
return
|
126
|
+
|
127
|
+
# NOTE: On Windows < 10, ANSI escape sequences such as \x1b[Am and \x1b[2K,
|
128
|
+
# used to move the cursor and clear text, aren't supported by the built-in
|
129
|
+
# console. However, we rely on the click library's use of colorama which
|
130
|
+
# emulates support for such sequences.
|
131
|
+
#
|
132
|
+
# For this reason, we don't have special checks for Windows.
|
133
|
+
|
134
|
+
block = DynamicBlock()
|
135
|
+
|
136
|
+
with _dynamic_text_lock:
|
137
|
+
_dynamic_blocks.append(block)
|
138
|
+
|
139
|
+
yield block
|
140
|
+
|
141
|
+
with _dynamic_text_lock:
|
142
|
+
block._lines_to_print = []
|
143
|
+
_l_rerender_dynamic_blocks()
|
144
|
+
_dynamic_blocks.remove(block)
|
145
|
+
|
146
|
+
|
147
|
+
def _sys_stderr_isatty() -> bool:
|
148
|
+
"""Returns sys.stderr.isatty().
|
149
|
+
|
150
|
+
Defined here for patching in tests.
|
151
|
+
"""
|
152
|
+
return sys.stderr.isatty()
|
153
|
+
|
154
|
+
|
29
155
|
def termlog(
|
30
156
|
string: str = "",
|
31
157
|
newline: bool = True,
|
32
158
|
repeat: bool = True,
|
33
159
|
prefix: bool = True,
|
34
160
|
) -> None:
|
35
|
-
"""Log
|
161
|
+
r"""Log an informational message to stderr.
|
162
|
+
|
163
|
+
The message may contain ANSI color sequences and the \n character.
|
164
|
+
Colors are stripped if stderr is not a TTY.
|
36
165
|
|
37
|
-
|
38
|
-
string
|
39
|
-
newline
|
40
|
-
repeat
|
166
|
+
Args:
|
167
|
+
string: The message to display.
|
168
|
+
newline: Whether to add a newline to the end of the string.
|
169
|
+
repeat: If false, then the string is not printed if an exact match has
|
170
|
+
already been printed through any of the other logging functions
|
171
|
+
in this file.
|
172
|
+
prefix: Whether to include the 'wandb:' prefix.
|
41
173
|
"""
|
42
174
|
_log(
|
43
|
-
string
|
175
|
+
string,
|
44
176
|
newline=newline,
|
45
177
|
repeat=repeat,
|
46
178
|
prefix=prefix,
|
@@ -48,28 +180,105 @@ def termlog(
|
|
48
180
|
)
|
49
181
|
|
50
182
|
|
51
|
-
def termwarn(
|
183
|
+
def termwarn(
|
184
|
+
string: str,
|
185
|
+
newline: bool = True,
|
186
|
+
repeat: bool = True,
|
187
|
+
prefix: bool = True,
|
188
|
+
) -> None:
|
189
|
+
"""Log a warning to stderr.
|
190
|
+
|
191
|
+
The arguments are the same as for `termlog()`.
|
192
|
+
"""
|
52
193
|
string = "\n".join([f"{WARN_STRING} {s}" for s in string.split("\n")])
|
53
194
|
_log(
|
54
|
-
string
|
55
|
-
newline=
|
195
|
+
string,
|
196
|
+
newline=newline,
|
197
|
+
repeat=repeat,
|
198
|
+
prefix=prefix,
|
56
199
|
silent=not _show_warnings,
|
57
200
|
level=logging.WARNING,
|
58
|
-
**kwargs,
|
59
201
|
)
|
60
202
|
|
61
203
|
|
62
|
-
def termerror(
|
204
|
+
def termerror(
|
205
|
+
string: str,
|
206
|
+
newline: bool = True,
|
207
|
+
repeat: bool = True,
|
208
|
+
prefix: bool = True,
|
209
|
+
) -> None:
|
210
|
+
"""Log an error to stderr.
|
211
|
+
|
212
|
+
The arguments are the same as for `termlog()`.
|
213
|
+
"""
|
63
214
|
string = "\n".join([f"{ERROR_STRING} {s}" for s in string.split("\n")])
|
64
215
|
_log(
|
65
|
-
string
|
66
|
-
newline=
|
216
|
+
string,
|
217
|
+
newline=newline,
|
218
|
+
repeat=repeat,
|
219
|
+
prefix=prefix,
|
67
220
|
silent=not _show_errors,
|
68
221
|
level=logging.ERROR,
|
69
|
-
**kwargs,
|
70
222
|
)
|
71
223
|
|
72
224
|
|
225
|
+
class DynamicBlock:
|
226
|
+
"""A handle to a changeable text area in the terminal."""
|
227
|
+
|
228
|
+
def __init__(self):
|
229
|
+
self._lines_to_print = []
|
230
|
+
self._num_lines_printed = 0
|
231
|
+
|
232
|
+
def set_text(self, text: str, prefix=True) -> None:
|
233
|
+
r"""Replace the text in this block.
|
234
|
+
|
235
|
+
Args:
|
236
|
+
text: The text to put in the block, with lines separated
|
237
|
+
by \n characters. The text should not end in \n unless
|
238
|
+
a blank line at the end of the block is desired.
|
239
|
+
prefix: Whether to include the "wandb:" prefix.
|
240
|
+
"""
|
241
|
+
with _dynamic_text_lock:
|
242
|
+
self._lines_to_print = text.splitlines()
|
243
|
+
|
244
|
+
if prefix:
|
245
|
+
self._lines_to_print = [
|
246
|
+
f"{LOG_STRING}: {line}" for line in self._lines_to_print
|
247
|
+
]
|
248
|
+
|
249
|
+
_l_rerender_dynamic_blocks()
|
250
|
+
|
251
|
+
def _l_clear(self) -> None:
|
252
|
+
"""Send terminal commands to clear all previously printed lines.
|
253
|
+
|
254
|
+
The lock must be held, and the cursor must be on the line after this
|
255
|
+
block of text.
|
256
|
+
"""
|
257
|
+
# NOTE: We rely on the fact that click.echo() uses colorama which
|
258
|
+
# emulates these ANSI sequences on older Windows versions.
|
259
|
+
#
|
260
|
+
# \r move cursor to start of line
|
261
|
+
# \x1b[Am move cursor up
|
262
|
+
# \x1b[2K delete line (sometimes moves cursor)
|
263
|
+
# \r move cursor to start of line
|
264
|
+
move_up_and_delete_line = "\r\x1b[Am\x1b[2K\r"
|
265
|
+
click.echo(
|
266
|
+
move_up_and_delete_line * self._num_lines_printed,
|
267
|
+
file=sys.stderr,
|
268
|
+
nl=False,
|
269
|
+
)
|
270
|
+
self._num_lines_printed = 0
|
271
|
+
|
272
|
+
def _l_print(self) -> None:
|
273
|
+
"""Print out this block of text.
|
274
|
+
|
275
|
+
The lock must be held.
|
276
|
+
"""
|
277
|
+
if self._lines_to_print:
|
278
|
+
click.echo("\n".join(self._lines_to_print), file=sys.stderr)
|
279
|
+
self._num_lines_printed += len(self._lines_to_print)
|
280
|
+
|
281
|
+
|
73
282
|
def _log(
|
74
283
|
string="",
|
75
284
|
newline=True,
|
@@ -77,27 +286,75 @@ def _log(
|
|
77
286
|
prefix=True,
|
78
287
|
silent=False,
|
79
288
|
level=logging.INFO,
|
80
|
-
):
|
81
|
-
|
82
|
-
|
83
|
-
|
289
|
+
) -> None:
|
290
|
+
with _dynamic_text_lock, _l_above_dynamic_text():
|
291
|
+
if not repeat:
|
292
|
+
if string in _printed_messages:
|
293
|
+
return
|
294
|
+
|
295
|
+
if len(_printed_messages) < 1000:
|
296
|
+
_printed_messages.add(string)
|
297
|
+
|
84
298
|
if prefix:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
PRINTED_MESSAGES.add(line)
|
95
|
-
if silent:
|
96
|
-
if level == logging.ERROR:
|
97
|
-
_logger.error(line)
|
299
|
+
string = "\n".join([f"{LOG_STRING}: {s}" for s in string.split("\n")])
|
300
|
+
|
301
|
+
silent = silent or _silent
|
302
|
+
if not silent:
|
303
|
+
click.echo(string, file=sys.stderr, nl=newline)
|
304
|
+
elif not _logger:
|
305
|
+
pass # No fallback logger, so nothing to do.
|
306
|
+
elif level == logging.ERROR:
|
307
|
+
_logger.error(click.unstyle(string))
|
98
308
|
elif level == logging.WARNING:
|
99
|
-
_logger.warning(
|
309
|
+
_logger.warning(click.unstyle(string))
|
100
310
|
else:
|
101
|
-
_logger.info(
|
102
|
-
|
103
|
-
|
311
|
+
_logger.info(click.unstyle(string))
|
312
|
+
|
313
|
+
|
314
|
+
def _l_rerender_dynamic_blocks() -> None:
|
315
|
+
"""Clear and re-print all dynamic text.
|
316
|
+
|
317
|
+
The lock must be held. The cursor must be positioned at the start of
|
318
|
+
the first line after the dynamic text area.
|
319
|
+
"""
|
320
|
+
with _l_above_dynamic_text():
|
321
|
+
# We just want the side-effect of rerendering the dynamic text.
|
322
|
+
pass
|
323
|
+
|
324
|
+
|
325
|
+
@contextlib.contextmanager
|
326
|
+
def _l_above_dynamic_text():
|
327
|
+
"""A context manager for inserting static text above any dynamic text.
|
328
|
+
|
329
|
+
The lock must be held. The cursor must be positioned at the start of the
|
330
|
+
first line after the dynamic text area.
|
331
|
+
|
332
|
+
The dynamic text is re-rendered.
|
333
|
+
"""
|
334
|
+
_l_clear_dynamic_blocks()
|
335
|
+
|
336
|
+
try:
|
337
|
+
yield
|
338
|
+
finally:
|
339
|
+
_l_print_dynamic_blocks()
|
340
|
+
|
341
|
+
|
342
|
+
def _l_clear_dynamic_blocks() -> None:
|
343
|
+
"""Delete all dynamic text.
|
344
|
+
|
345
|
+
The lock must be held, and the cursor must be positioned at the start
|
346
|
+
of the first line after the dynamic text area. After this, the cursor
|
347
|
+
is positioned at the start of the first line after all static text.
|
348
|
+
"""
|
349
|
+
for block in reversed(_dynamic_blocks):
|
350
|
+
block._l_clear()
|
351
|
+
|
352
|
+
|
353
|
+
def _l_print_dynamic_blocks() -> None:
|
354
|
+
"""Output all dynamic text.
|
355
|
+
|
356
|
+
The lock must be held. After this, the cursor is positioned at the start
|
357
|
+
of the first line after the dynamic text area.
|
358
|
+
"""
|
359
|
+
for block in _dynamic_blocks:
|
360
|
+
block._l_print()
|