wandb 0.18.2__py3-none-win32.whl → 0.18.4__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 (45) hide show
  1. wandb/__init__.py +16 -7
  2. wandb/__init__.pyi +96 -63
  3. wandb/analytics/sentry.py +91 -88
  4. wandb/apis/public/api.py +18 -4
  5. wandb/apis/public/runs.py +53 -2
  6. wandb/bin/gpu_stats.exe +0 -0
  7. wandb/bin/wandb-core +0 -0
  8. wandb/cli/beta.py +178 -0
  9. wandb/cli/cli.py +5 -171
  10. wandb/data_types.py +3 -0
  11. wandb/env.py +74 -73
  12. wandb/errors/term.py +300 -43
  13. wandb/proto/v3/wandb_internal_pb2.py +271 -221
  14. wandb/proto/v3/wandb_server_pb2.py +57 -37
  15. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  16. wandb/proto/v4/wandb_internal_pb2.py +226 -216
  17. wandb/proto/v4/wandb_server_pb2.py +41 -37
  18. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  19. wandb/proto/v5/wandb_internal_pb2.py +226 -216
  20. wandb/proto/v5/wandb_server_pb2.py +41 -37
  21. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  22. wandb/sdk/__init__.py +3 -3
  23. wandb/sdk/artifacts/_validators.py +41 -8
  24. wandb/sdk/artifacts/artifact.py +35 -4
  25. wandb/sdk/artifacts/artifact_file_cache.py +1 -2
  26. wandb/sdk/data_types/_dtypes.py +7 -3
  27. wandb/sdk/data_types/video.py +15 -6
  28. wandb/sdk/interface/interface.py +2 -0
  29. wandb/sdk/internal/internal_api.py +122 -5
  30. wandb/sdk/internal/sender.py +16 -3
  31. wandb/sdk/launch/inputs/internal.py +1 -1
  32. wandb/sdk/lib/module.py +12 -0
  33. wandb/sdk/lib/printer.py +291 -105
  34. wandb/sdk/lib/progress.py +274 -0
  35. wandb/sdk/service/streams.py +21 -11
  36. wandb/sdk/wandb_init.py +59 -54
  37. wandb/sdk/wandb_run.py +413 -480
  38. wandb/sdk/wandb_settings.py +2 -0
  39. wandb/sdk/wandb_watch.py +17 -11
  40. wandb/util.py +6 -2
  41. {wandb-0.18.2.dist-info → wandb-0.18.4.dist-info}/METADATA +5 -4
  42. {wandb-0.18.2.dist-info → wandb-0.18.4.dist-info}/RECORD +45 -42
  43. {wandb-0.18.2.dist-info → wandb-0.18.4.dist-info}/WHEEL +0 -0
  44. {wandb-0.18.2.dist-info → wandb-0.18.4.dist-info}/entry_points.txt +0 -0
  45. {wandb-0.18.2.dist-info → wandb-0.18.4.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
- from typing import Any
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
- _show_info = True
15
- _show_warnings = True
16
- _show_errors = True
17
- _logger = None
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
- def termsetup(settings, logger) -> None:
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 to standard error with formatting.
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
- Arguments:
38
- string (str, optional): The string to print
39
- newline (bool, optional): Print a newline at the end of the string
40
- repeat (bool, optional): If set to False only prints the string once per process
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=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(string: str, **kwargs: Any) -> None:
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=string,
55
- newline=True,
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(string: str, **kwargs: Any) -> None:
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=string,
66
- newline=True,
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
- global _logger
82
- silent = silent or _silent
83
- if string:
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
- line = "\n".join([f"{LOG_STRING}: {s}" for s in string.split("\n")])
86
- else:
87
- line = string
88
- else:
89
- line = ""
90
- if not repeat and line in PRINTED_MESSAGES:
91
- return
92
- # Repeated line tracking limited to 1k messages
93
- if len(PRINTED_MESSAGES) < 1000:
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(line)
309
+ _logger.warning(click.unstyle(string))
100
310
  else:
101
- _logger.info(line)
102
- else:
103
- click.echo(line, file=sys.stderr, nl=newline)
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()