wandb 0.18.3__py3-none-win32.whl → 0.18.5__py3-none-win32.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) 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 +263 -223
  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 -218
  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 -218
  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 +32 -1
  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 +126 -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 +58 -54
  37. wandb/sdk/wandb_run.py +380 -454
  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.3.dist-info → wandb-0.18.5.dist-info}/METADATA +4 -3
  42. {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/RECORD +45 -43
  43. wandb/bin/nvidia_gpu_stats.exe +0 -0
  44. {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/WHEEL +0 -0
  45. {wandb-0.18.3.dist-info → wandb-0.18.5.dist-info}/entry_points.txt +0 -0
  46. {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
- 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()