wandb 0.18.3__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.
- 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.exe +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 +122 -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.4.dist-info}/METADATA +4 -3
- {wandb-0.18.3.dist-info → wandb-0.18.4.dist-info}/RECORD +45 -43
- wandb/bin/nvidia_gpu_stats.exe +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.4.dist-info}/WHEEL +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.4.dist-info}/entry_points.txt +0 -0
- {wandb-0.18.3.dist-info → wandb-0.18.4.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/lib/printer.py
CHANGED
@@ -1,14 +1,25 @@
|
|
1
|
-
|
1
|
+
"""Terminal, Jupyter and file output for W&B."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import abc
|
6
|
+
import contextlib
|
3
7
|
import itertools
|
4
8
|
import platform
|
5
9
|
import sys
|
6
|
-
from
|
7
|
-
|
10
|
+
from typing import Callable, Iterator
|
11
|
+
|
12
|
+
import wandb.util
|
13
|
+
|
14
|
+
if sys.version_info >= (3, 12):
|
15
|
+
from typing import override
|
16
|
+
else:
|
17
|
+
from typing_extensions import override
|
8
18
|
|
9
19
|
import click
|
10
20
|
|
11
21
|
import wandb
|
22
|
+
from wandb.errors import term
|
12
23
|
|
13
24
|
from . import ipython, sparkline
|
14
25
|
|
@@ -42,43 +53,82 @@ _name_to_level = {
|
|
42
53
|
"NOTSET": NOTSET,
|
43
54
|
}
|
44
55
|
|
56
|
+
_PROGRESS_SYMBOL_ANIMATION = "⢿⣻⣽⣾⣷⣯⣟⡿"
|
57
|
+
"""Sequence of characters for a progress spinner.
|
45
58
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if wandb.util.is_unicode_safe(sys.stdout):
|
50
|
-
return sparkline.sparkify(series)
|
51
|
-
return None
|
59
|
+
Unicode characters from the Braille Patterns block arranged
|
60
|
+
to form a subtle clockwise spinning animation.
|
61
|
+
"""
|
52
62
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
_PROGRESS_SYMBOL_COLOR = 0xB2
|
64
|
+
"""Color from the 256-color palette for the progress symbol."""
|
65
|
+
|
66
|
+
|
67
|
+
class Printer(abc.ABC):
|
68
|
+
"""An object that shows styled text to the user."""
|
69
|
+
|
70
|
+
@contextlib.contextmanager
|
71
|
+
@abc.abstractmethod
|
72
|
+
def dynamic_text(self) -> Iterator[DynamicText | None]:
|
73
|
+
"""A context manager providing a handle to a block of changeable text.
|
74
|
+
|
75
|
+
Since `wandb` may be outputting to a terminal, it's important to only
|
76
|
+
use this when `wandb` is performing blocking calls, or else text output
|
77
|
+
by non-`wandb` code may get overwritten.
|
57
78
|
|
79
|
+
Returns None if dynamic text is not supported, such as if stderr is not
|
80
|
+
a TTY and we're not in a Jupyter notebook.
|
81
|
+
"""
|
82
|
+
|
83
|
+
@abc.abstractmethod
|
58
84
|
def display(
|
59
85
|
self,
|
60
|
-
text:
|
86
|
+
text: str | list[str] | tuple[str],
|
61
87
|
*,
|
62
|
-
level:
|
63
|
-
off: Optional[bool] = None,
|
64
|
-
default_text: Optional[Union[str, List[str], Tuple[str]]] = None,
|
88
|
+
level: str | int | None = None,
|
65
89
|
) -> None:
|
66
|
-
|
67
|
-
return
|
68
|
-
self._display(text, level=level, default_text=default_text)
|
90
|
+
"""Display text to the user.
|
69
91
|
|
70
|
-
|
71
|
-
|
92
|
+
Args:
|
93
|
+
text: The text to display. If given an iterable of strings, they're
|
94
|
+
joined with newlines.
|
95
|
+
level: The logging level, for controlling verbosity.
|
96
|
+
"""
|
97
|
+
|
98
|
+
@abc.abstractmethod
|
99
|
+
def progress_update(
|
72
100
|
self,
|
73
|
-
text:
|
74
|
-
|
75
|
-
level: Optional[Union[str, int]] = None,
|
76
|
-
default_text: Optional[Union[str, List[str], Tuple[str]]] = None,
|
101
|
+
text: str,
|
102
|
+
percent_done: float | None = None,
|
77
103
|
) -> None:
|
78
|
-
|
104
|
+
r"""Set the text on the progress indicator.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
text: The text to set, which must end with \r.
|
108
|
+
percent_done: The current progress, between 0 and 1.
|
109
|
+
"""
|
110
|
+
|
111
|
+
@abc.abstractmethod
|
112
|
+
def progress_close(self, text: str | None = None) -> None:
|
113
|
+
"""Close the progress indicator.
|
114
|
+
|
115
|
+
After this, `progress_update` should not be used.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
text: The final text to set on the progress indicator.
|
119
|
+
Ignored in Jupyter notebooks.
|
120
|
+
"""
|
79
121
|
|
80
122
|
@staticmethod
|
81
|
-
def _sanitize_level(name_or_level:
|
123
|
+
def _sanitize_level(name_or_level: str | int | None) -> int:
|
124
|
+
"""Returns the number corresponding to the logging level.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
name_or_level: The logging level passed to `display`.
|
128
|
+
|
129
|
+
Raises:
|
130
|
+
ValueError: if the input is not a valid logging level.
|
131
|
+
"""
|
82
132
|
if isinstance(name_or_level, str):
|
83
133
|
try:
|
84
134
|
return _name_to_level[name_or_level.upper()]
|
@@ -95,65 +145,126 @@ class _Printer:
|
|
95
145
|
|
96
146
|
raise ValueError(f"Unknown status level {name_or_level}")
|
97
147
|
|
98
|
-
@
|
148
|
+
@property
|
149
|
+
@abc.abstractmethod
|
150
|
+
def supports_html(self) -> bool:
|
151
|
+
"""Whether text passed to display may contain HTML styling."""
|
152
|
+
|
153
|
+
@property
|
154
|
+
@abc.abstractmethod
|
155
|
+
def supports_unicode(self) -> bool:
|
156
|
+
"""Whether text passed to display may contain arbitrary Unicode."""
|
157
|
+
|
158
|
+
def sparklines(self, series: list[int | float]) -> str | None:
|
159
|
+
"""Returns a Unicode art representation of the series of numbers.
|
160
|
+
|
161
|
+
Also known as "ASCII art", except this uses non-ASCII
|
162
|
+
Unicode characters.
|
163
|
+
|
164
|
+
Returns None if the output doesn't support Unicode.
|
165
|
+
"""
|
166
|
+
if self.supports_unicode:
|
167
|
+
return sparkline.sparkify(series)
|
168
|
+
else:
|
169
|
+
return None
|
170
|
+
|
171
|
+
@abc.abstractmethod
|
99
172
|
def code(self, text: str) -> str:
|
100
|
-
|
173
|
+
"""Returns the text styled like code."""
|
101
174
|
|
102
|
-
@abstractmethod
|
175
|
+
@abc.abstractmethod
|
103
176
|
def name(self, text: str) -> str:
|
104
|
-
|
177
|
+
"""Returns the text styled like a run name."""
|
105
178
|
|
106
|
-
@abstractmethod
|
107
|
-
def link(self, link: str, text:
|
108
|
-
|
179
|
+
@abc.abstractmethod
|
180
|
+
def link(self, link: str, text: str | None = None) -> str:
|
181
|
+
"""Returns the text styled like a link.
|
109
182
|
|
110
|
-
|
111
|
-
|
112
|
-
|
183
|
+
Args:
|
184
|
+
link: The target link.
|
185
|
+
text: The text to show for the link. If not set, or if we're not
|
186
|
+
in an environment that supports clickable links,
|
187
|
+
this is ignored.
|
188
|
+
"""
|
189
|
+
|
190
|
+
@abc.abstractmethod
|
191
|
+
def secondary_text(self, text: str) -> str:
|
192
|
+
"""Returns the text styled to draw less attention."""
|
193
|
+
|
194
|
+
@abc.abstractmethod
|
195
|
+
def loading_symbol(self, tick: int) -> str:
|
196
|
+
"""Returns a frame of an animated loading symbol.
|
113
197
|
|
114
|
-
|
115
|
-
|
116
|
-
|
198
|
+
May return an empty string.
|
199
|
+
|
200
|
+
Args:
|
201
|
+
tick: An index into the animation.
|
202
|
+
"""
|
203
|
+
|
204
|
+
@abc.abstractmethod
|
205
|
+
def error(self, text: str) -> str:
|
206
|
+
"""Returns the text colored like an error."""
|
207
|
+
|
208
|
+
@abc.abstractmethod
|
209
|
+
def emoji(self, name: str) -> str:
|
210
|
+
"""Returns the string for a named emoji, or an empty string."""
|
117
211
|
|
118
|
-
@abstractmethod
|
212
|
+
@abc.abstractmethod
|
119
213
|
def files(self, text: str) -> str:
|
120
|
-
|
214
|
+
"""Returns the text styled like a file path."""
|
121
215
|
|
122
|
-
@abstractmethod
|
123
|
-
def grid(self, rows:
|
124
|
-
|
216
|
+
@abc.abstractmethod
|
217
|
+
def grid(self, rows: list[list[str]], title: str | None = None) -> str:
|
218
|
+
"""Returns a grid of strings with an optional title."""
|
125
219
|
|
126
|
-
@abstractmethod
|
127
|
-
def panel(self, columns:
|
128
|
-
|
220
|
+
@abc.abstractmethod
|
221
|
+
def panel(self, columns: list[str]) -> str:
|
222
|
+
"""Returns the column text combined in a compact way."""
|
129
223
|
|
130
224
|
|
131
|
-
class
|
225
|
+
class DynamicText(abc.ABC):
|
226
|
+
"""A handle to a block of text that's allowed to change."""
|
227
|
+
|
228
|
+
@abc.abstractmethod
|
229
|
+
def set_text(self, text: str) -> None:
|
230
|
+
r"""Change the text.
|
231
|
+
|
232
|
+
Args:
|
233
|
+
text: The text to put in the block, with lines separated
|
234
|
+
by \n characters. The text should not end in \n unless
|
235
|
+
a blank line at the end of the block is desired.
|
236
|
+
May include styled output from methods on the Printer
|
237
|
+
that created this.
|
238
|
+
"""
|
239
|
+
|
240
|
+
|
241
|
+
class _PrinterTerm(Printer):
|
132
242
|
def __init__(self) -> None:
|
133
243
|
super().__init__()
|
134
|
-
self._html = False
|
135
244
|
self._progress = itertools.cycle(["-", "\\", "|", "/"])
|
136
245
|
|
137
|
-
|
246
|
+
@override
|
247
|
+
@contextlib.contextmanager
|
248
|
+
def dynamic_text(self) -> Iterator[DynamicText | None]:
|
249
|
+
with term.dynamic_text() as handle:
|
250
|
+
if not handle:
|
251
|
+
yield None
|
252
|
+
else:
|
253
|
+
yield _DynamicTermText(handle)
|
254
|
+
|
255
|
+
@override
|
256
|
+
def display(
|
138
257
|
self,
|
139
|
-
text:
|
258
|
+
text: str | list[str] | tuple[str],
|
140
259
|
*,
|
141
|
-
level:
|
142
|
-
default_text: Optional[Union[str, List[str], Tuple[str]]] = None,
|
260
|
+
level: str | int | None = None,
|
143
261
|
) -> None:
|
144
262
|
text = "\n".join(text) if isinstance(text, (list, tuple)) else text
|
145
|
-
if default_text is not None:
|
146
|
-
default_text = (
|
147
|
-
"\n".join(default_text)
|
148
|
-
if isinstance(default_text, (list, tuple))
|
149
|
-
else default_text
|
150
|
-
)
|
151
|
-
text = text or default_text
|
152
263
|
self._display_fn_mapping(level)(text)
|
153
264
|
|
154
265
|
@staticmethod
|
155
|
-
def _display_fn_mapping(level:
|
156
|
-
level =
|
266
|
+
def _display_fn_mapping(level: str | int | None = None) -> Callable[[str], None]:
|
267
|
+
level = Printer._sanitize_level(level)
|
157
268
|
|
158
269
|
if level >= CRITICAL:
|
159
270
|
return wandb.termerror
|
@@ -168,27 +279,43 @@ class PrinterTerm(_Printer):
|
|
168
279
|
else:
|
169
280
|
return wandb.termlog
|
170
281
|
|
171
|
-
|
282
|
+
@override
|
283
|
+
def progress_update(self, text: str, percent_done: float | None = None) -> None:
|
172
284
|
wandb.termlog(f"{next(self._progress)} {text}", newline=False)
|
173
285
|
|
174
|
-
|
286
|
+
@override
|
287
|
+
def progress_close(self, text: str | None = None) -> None:
|
175
288
|
text = text or " " * 79
|
176
289
|
wandb.termlog(text)
|
177
290
|
|
291
|
+
@override
|
292
|
+
@property
|
293
|
+
def supports_html(self) -> bool:
|
294
|
+
return False
|
295
|
+
|
296
|
+
@override
|
297
|
+
@property
|
298
|
+
def supports_unicode(self) -> bool:
|
299
|
+
return wandb.util.is_unicode_safe(sys.stderr)
|
300
|
+
|
301
|
+
@override
|
178
302
|
def code(self, text: str) -> str:
|
179
303
|
ret: str = click.style(text, bold=True)
|
180
304
|
return ret
|
181
305
|
|
306
|
+
@override
|
182
307
|
def name(self, text: str) -> str:
|
183
308
|
ret: str = click.style(text, fg="yellow")
|
184
309
|
return ret
|
185
310
|
|
186
|
-
|
311
|
+
@override
|
312
|
+
def link(self, link: str, text: str | None = None) -> str:
|
187
313
|
ret: str = click.style(link, fg="blue", underline=True)
|
188
314
|
# ret = f"\x1b[m{text or link}\x1b[0m"
|
189
315
|
# ret = f"\x1b]8;;{link}\x1b\\{ret}\x1b]8;;\x1b\\"
|
190
316
|
return ret
|
191
317
|
|
318
|
+
@override
|
192
319
|
def emoji(self, name: str) -> str:
|
193
320
|
emojis = dict()
|
194
321
|
if platform.system() != "Windows" and wandb.util.is_unicode_safe(sys.stdout):
|
@@ -203,16 +330,34 @@ class PrinterTerm(_Printer):
|
|
203
330
|
|
204
331
|
return emojis.get(name, "")
|
205
332
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
333
|
+
@override
|
334
|
+
def secondary_text(self, text: str) -> str:
|
335
|
+
# NOTE: "white" is really a light gray, and is usually distinct
|
336
|
+
# from the terminal's foreground color (i.e. default text color)
|
337
|
+
return click.style(text, fg="white")
|
338
|
+
|
339
|
+
@override
|
340
|
+
def loading_symbol(self, tick: int) -> str:
|
341
|
+
if not self.supports_unicode:
|
342
|
+
return ""
|
343
|
+
|
344
|
+
idx = tick % len(_PROGRESS_SYMBOL_ANIMATION)
|
345
|
+
return click.style(
|
346
|
+
_PROGRESS_SYMBOL_ANIMATION[idx],
|
347
|
+
fg=_PROGRESS_SYMBOL_COLOR,
|
348
|
+
)
|
210
349
|
|
350
|
+
@override
|
351
|
+
def error(self, text: str) -> str:
|
352
|
+
return click.style(text, fg="red")
|
353
|
+
|
354
|
+
@override
|
211
355
|
def files(self, text: str) -> str:
|
212
356
|
ret: str = click.style(text, fg="magenta", bold=True)
|
213
357
|
return ret
|
214
358
|
|
215
|
-
|
359
|
+
@override
|
360
|
+
def grid(self, rows: list[list[str]], title: str | None = None) -> str:
|
216
361
|
max_len = max(len(row[0]) for row in rows)
|
217
362
|
format_row = " ".join(["{:>{max_len}}", "{}" * (len(rows[0]) - 1)])
|
218
363
|
grid = "\n".join([format_row.format(*row, max_len=max_len) for row in rows])
|
@@ -220,36 +365,44 @@ class PrinterTerm(_Printer):
|
|
220
365
|
return f"{title}\n{grid}\n"
|
221
366
|
return f"{grid}\n"
|
222
367
|
|
223
|
-
|
368
|
+
@override
|
369
|
+
def panel(self, columns: list[str]) -> str:
|
224
370
|
return "\n" + "\n".join(columns)
|
225
371
|
|
226
372
|
|
227
|
-
class
|
373
|
+
class _DynamicTermText(DynamicText):
|
374
|
+
def __init__(self, handle: term.DynamicBlock) -> None:
|
375
|
+
self._handle = handle
|
376
|
+
|
377
|
+
@override
|
378
|
+
def set_text(self, text: str) -> None:
|
379
|
+
self._handle.set_text(text)
|
380
|
+
|
381
|
+
|
382
|
+
class _PrinterJupyter(Printer):
|
228
383
|
def __init__(self) -> None:
|
229
384
|
super().__init__()
|
230
|
-
self._html = True
|
231
385
|
self._progress = ipython.jupyter_progress_bar()
|
232
386
|
|
233
|
-
|
387
|
+
@override
|
388
|
+
@contextlib.contextmanager
|
389
|
+
def dynamic_text(self) -> Iterator[DynamicText | None]:
|
390
|
+
# TODO: Support dynamic text in Jupyter notebooks.
|
391
|
+
yield None
|
392
|
+
|
393
|
+
@override
|
394
|
+
def display(
|
234
395
|
self,
|
235
|
-
text:
|
396
|
+
text: str | list[str] | tuple[str],
|
236
397
|
*,
|
237
|
-
level:
|
238
|
-
default_text: Optional[Union[str, List[str], Tuple[str]]] = None,
|
398
|
+
level: str | int | None = None,
|
239
399
|
) -> None:
|
240
400
|
text = "<br/>".join(text) if isinstance(text, (list, tuple)) else text
|
241
|
-
if default_text is not None:
|
242
|
-
default_text = (
|
243
|
-
"<br/>".join(default_text)
|
244
|
-
if isinstance(default_text, (list, tuple))
|
245
|
-
else default_text
|
246
|
-
)
|
247
|
-
text = text or default_text
|
248
401
|
self._display_fn_mapping(level)(text)
|
249
402
|
|
250
403
|
@staticmethod
|
251
|
-
def _display_fn_mapping(level:
|
252
|
-
level =
|
404
|
+
def _display_fn_mapping(level: str | int | None) -> Callable[[str], None]:
|
405
|
+
level = Printer._sanitize_level(level)
|
253
406
|
|
254
407
|
if level >= CRITICAL:
|
255
408
|
return ipython.display_html
|
@@ -264,34 +417,69 @@ class PrinterJupyter(_Printer):
|
|
264
417
|
else:
|
265
418
|
return ipython.display_html
|
266
419
|
|
420
|
+
@override
|
421
|
+
@property
|
422
|
+
def supports_html(self) -> bool:
|
423
|
+
return True
|
424
|
+
|
425
|
+
@override
|
426
|
+
@property
|
427
|
+
def supports_unicode(self) -> bool:
|
428
|
+
return True
|
429
|
+
|
430
|
+
@override
|
267
431
|
def code(self, text: str) -> str:
|
268
432
|
return f"<code>{text}<code>"
|
269
433
|
|
434
|
+
@override
|
270
435
|
def name(self, text: str) -> str:
|
271
436
|
return f'<strong style="color:#cdcd00">{text}</strong>'
|
272
437
|
|
273
|
-
|
438
|
+
@override
|
439
|
+
def link(self, link: str, text: str | None = None) -> str:
|
274
440
|
return f'<a href={link!r} target="_blank">{text or link}</a>'
|
275
441
|
|
442
|
+
@override
|
276
443
|
def emoji(self, name: str) -> str:
|
277
444
|
return ""
|
278
445
|
|
279
|
-
|
280
|
-
|
281
|
-
return
|
446
|
+
@override
|
447
|
+
def secondary_text(self, text: str) -> str:
|
448
|
+
return text
|
449
|
+
|
450
|
+
@override
|
451
|
+
def loading_symbol(self, tick: int) -> str:
|
452
|
+
return ""
|
453
|
+
|
454
|
+
@override
|
455
|
+
def error(self, text: str) -> str:
|
456
|
+
return f'<strong style="color:red">{text}</strong>'
|
282
457
|
|
458
|
+
@override
|
283
459
|
def files(self, text: str) -> str:
|
284
460
|
return f"<code>{text}</code>"
|
285
461
|
|
286
|
-
|
287
|
-
|
288
|
-
|
462
|
+
@override
|
463
|
+
def progress_update(
|
464
|
+
self,
|
465
|
+
text: str,
|
466
|
+
percent_done: float | None = None,
|
467
|
+
) -> None:
|
468
|
+
if not self._progress:
|
469
|
+
return
|
289
470
|
|
290
|
-
|
471
|
+
if percent_done is None:
|
472
|
+
percent_done = 1.0
|
473
|
+
|
474
|
+
self._progress.update(percent_done, text)
|
475
|
+
|
476
|
+
@override
|
477
|
+
def progress_close(self, _: str | None = None) -> None:
|
291
478
|
if self._progress:
|
292
479
|
self._progress.close()
|
293
480
|
|
294
|
-
|
481
|
+
@override
|
482
|
+
def grid(self, rows: list[list[str]], title: str | None = None) -> str:
|
295
483
|
format_row = "".join(["<tr>", "<td>{}</td>" * len(rows[0]), "</tr>"])
|
296
484
|
grid = "".join([format_row.format(*row) for row in rows])
|
297
485
|
grid = f'<table class="wandb">{grid}</table>'
|
@@ -299,15 +487,13 @@ class PrinterJupyter(_Printer):
|
|
299
487
|
return f"<h3>{title}</h3><br/>{grid}<br/>"
|
300
488
|
return f"{grid}<br/>"
|
301
489
|
|
302
|
-
|
490
|
+
@override
|
491
|
+
def panel(self, columns: list[str]) -> str:
|
303
492
|
row = "".join([f'<div class="wandb-col">{col}</div>' for col in columns])
|
304
493
|
return f'{ipython.TABLE_STYLES}<div class="wandb-row">{row}</div>'
|
305
494
|
|
306
495
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
if _jupyter:
|
312
|
-
return PrinterJupyter()
|
313
|
-
return PrinterTerm()
|
496
|
+
def get_printer(jupyter: bool) -> Printer:
|
497
|
+
if jupyter:
|
498
|
+
return _PrinterJupyter()
|
499
|
+
return _PrinterTerm()
|