wandb 0.19.8__py3-none-any.whl → 0.19.10__py3-none-any.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 +5 -1
- wandb/__init__.pyi +15 -8
- wandb/_pydantic/__init__.py +30 -0
- wandb/_pydantic/base.py +148 -0
- wandb/_pydantic/utils.py +66 -0
- wandb/_pydantic/v1_compat.py +284 -0
- wandb/apis/paginator.py +82 -38
- wandb/apis/public/__init__.py +2 -2
- wandb/apis/public/api.py +111 -53
- wandb/apis/public/artifacts.py +387 -639
- wandb/apis/public/automations.py +69 -0
- wandb/apis/public/files.py +2 -2
- wandb/apis/public/integrations.py +168 -0
- wandb/apis/public/projects.py +32 -2
- wandb/apis/public/reports.py +2 -2
- wandb/apis/public/runs.py +19 -11
- wandb/apis/public/utils.py +107 -1
- wandb/automations/__init__.py +81 -0
- wandb/automations/_filters/__init__.py +40 -0
- wandb/automations/_filters/expressions.py +179 -0
- wandb/automations/_filters/operators.py +267 -0
- wandb/automations/_filters/run_metrics.py +183 -0
- wandb/automations/_generated/__init__.py +184 -0
- wandb/automations/_generated/create_filter_trigger.py +21 -0
- wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
- wandb/automations/_generated/delete_trigger.py +19 -0
- wandb/automations/_generated/enums.py +33 -0
- wandb/automations/_generated/fragments.py +343 -0
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
- wandb/automations/_generated/get_triggers.py +24 -0
- wandb/automations/_generated/get_triggers_by_entity.py +24 -0
- wandb/automations/_generated/input_types.py +104 -0
- wandb/automations/_generated/integrations_by_entity.py +22 -0
- wandb/automations/_generated/operations.py +710 -0
- wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
- wandb/automations/_generated/update_filter_trigger.py +21 -0
- wandb/automations/_utils.py +123 -0
- wandb/automations/_validators.py +73 -0
- wandb/automations/actions.py +205 -0
- wandb/automations/automations.py +109 -0
- wandb/automations/events.py +235 -0
- wandb/automations/integrations.py +26 -0
- wandb/automations/scopes.py +76 -0
- wandb/beta/workflows.py +9 -10
- wandb/bin/gpu_stats +0 -0
- wandb/cli/cli.py +3 -3
- wandb/integration/keras/keras.py +2 -1
- wandb/integration/langchain/wandb_tracer.py +2 -1
- wandb/integration/metaflow/metaflow.py +19 -17
- wandb/integration/sacred/__init__.py +1 -1
- wandb/jupyter.py +155 -133
- wandb/old/summary.py +0 -2
- wandb/proto/v3/wandb_internal_pb2.py +297 -292
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_internal_pb2.py +292 -292
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v5/wandb_internal_pb2.py +292 -292
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v6/wandb_base_pb2.py +41 -0
- wandb/proto/v6/wandb_internal_pb2.py +393 -0
- wandb/proto/v6/wandb_server_pb2.py +78 -0
- wandb/proto/v6/wandb_settings_pb2.py +58 -0
- wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
- wandb/proto/wandb_base_pb2.py +2 -0
- wandb/proto/wandb_deprecated.py +10 -0
- wandb/proto/wandb_internal_pb2.py +3 -1
- wandb/proto/wandb_server_pb2.py +2 -0
- wandb/proto/wandb_settings_pb2.py +2 -0
- wandb/proto/wandb_telemetry_pb2.py +2 -0
- wandb/sdk/artifacts/_generated/__init__.py +248 -0
- wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
- wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
- wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_generated/enums.py +17 -0
- wandb/sdk/artifacts/_generated/fragments.py +186 -0
- wandb/sdk/artifacts/_generated/input_types.py +16 -0
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
- wandb/sdk/artifacts/_generated/operations.py +510 -0
- wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
- wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
- wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_graphql_fragments.py +56 -81
- wandb/sdk/artifacts/_validators.py +1 -0
- wandb/sdk/artifacts/artifact.py +110 -49
- wandb/sdk/artifacts/artifact_manifest_entry.py +2 -1
- wandb/sdk/artifacts/artifact_saver.py +16 -2
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +23 -2
- wandb/sdk/data_types/audio.py +1 -3
- wandb/sdk/data_types/base_types/media.py +13 -7
- wandb/sdk/data_types/base_types/wb_value.py +34 -11
- wandb/sdk/data_types/html.py +36 -9
- wandb/sdk/data_types/image.py +56 -37
- wandb/sdk/data_types/molecule.py +1 -5
- wandb/sdk/data_types/object_3d.py +2 -1
- wandb/sdk/data_types/saved_model.py +7 -9
- wandb/sdk/data_types/table.py +5 -0
- wandb/sdk/data_types/trace_tree.py +2 -0
- wandb/sdk/data_types/utils.py +1 -1
- wandb/sdk/data_types/video.py +15 -30
- wandb/sdk/interface/interface.py +2 -0
- wandb/{apis/public → sdk/internal}/_generated/__init__.py +0 -6
- wandb/{apis/public → sdk/internal}/_generated/server_features_query.py +3 -3
- wandb/sdk/internal/internal_api.py +138 -47
- wandb/sdk/internal/profiler.py +6 -5
- wandb/sdk/internal/run.py +13 -6
- wandb/sdk/internal/sender.py +2 -0
- wandb/sdk/internal/sender_config.py +8 -11
- wandb/sdk/internal/settings_static.py +24 -2
- wandb/sdk/lib/apikey.py +40 -20
- wandb/sdk/lib/asyncio_compat.py +1 -1
- wandb/sdk/lib/deprecate.py +13 -22
- wandb/sdk/lib/disabled.py +2 -1
- wandb/sdk/lib/printer.py +37 -8
- wandb/sdk/lib/printer_asyncio.py +46 -0
- wandb/sdk/lib/redirect.py +10 -5
- wandb/sdk/lib/run_moment.py +4 -6
- wandb/sdk/lib/wb_logging.py +161 -0
- wandb/sdk/service/server_sock.py +19 -14
- wandb/sdk/service/service.py +9 -7
- wandb/sdk/service/streams.py +5 -0
- wandb/sdk/verify/verify.py +6 -3
- wandb/sdk/wandb_config.py +44 -43
- wandb/sdk/wandb_init.py +323 -141
- wandb/sdk/wandb_login.py +13 -4
- wandb/sdk/wandb_metadata.py +107 -91
- wandb/sdk/wandb_run.py +529 -325
- wandb/sdk/wandb_settings.py +422 -202
- wandb/sdk/wandb_setup.py +52 -1
- wandb/util.py +29 -29
- {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/METADATA +7 -7
- {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/RECORD +150 -93
- wandb/_globals.py +0 -19
- wandb/apis/public/_generated/base.py +0 -128
- wandb/apis/public/_generated/typing_compat.py +0 -14
- /wandb/{apis/public → sdk/internal}/_generated/enums.py +0 -0
- /wandb/{apis/public → sdk/internal}/_generated/input_types.py +0 -0
- /wandb/{apis/public → sdk/internal}/_generated/operations.py +0 -0
- {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/WHEEL +0 -0
- {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/lib/deprecate.py
CHANGED
@@ -1,42 +1,33 @@
|
|
1
|
-
|
1
|
+
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import TYPE_CHECKING
|
3
|
+
from typing import TYPE_CHECKING
|
4
4
|
|
5
5
|
import wandb
|
6
|
-
from wandb.proto.wandb_deprecated import DEPRECATED_FEATURES
|
7
|
-
from wandb.
|
6
|
+
from wandb.proto.wandb_deprecated import DEPRECATED_FEATURES
|
7
|
+
from wandb.sdk.lib import telemetry
|
8
8
|
|
9
|
-
#
|
9
|
+
# Necessary to break import cycle.
|
10
10
|
if TYPE_CHECKING:
|
11
|
-
from
|
12
|
-
|
13
|
-
|
14
|
-
deprecated_field_names: Tuple[str, ...] = tuple(
|
15
|
-
str(v) for k, v in Deprecated.__dict__.items() if not k.startswith("_")
|
16
|
-
)
|
11
|
+
from wandb import wandb_run
|
17
12
|
|
18
13
|
|
19
14
|
def deprecate(
|
20
15
|
field_name: DEPRECATED_FEATURES,
|
21
16
|
warning_message: str,
|
22
|
-
run:
|
17
|
+
run: wandb_run.Run | None = None,
|
23
18
|
) -> None:
|
24
19
|
"""Warn the user that a feature has been deprecated.
|
25
20
|
|
26
|
-
|
21
|
+
If a run is provided, the given field on its telemetry is updated.
|
22
|
+
Otherwise, the global run is used.
|
27
23
|
|
28
24
|
Args:
|
29
|
-
field_name: The
|
30
|
-
Defined in wandb/proto/wandb_telemetry.proto::Deprecated
|
25
|
+
field_name: The field on the Deprecated proto for this deprecation.
|
31
26
|
warning_message: The message to display to the user.
|
32
|
-
run: The run
|
27
|
+
run: The run whose telemetry to update.
|
33
28
|
"""
|
34
|
-
known_fields = TelemetryDeprecated.DESCRIPTOR.fields_by_name.keys()
|
35
|
-
if field_name not in known_fields:
|
36
|
-
raise ValueError(
|
37
|
-
f"Unknown field name: {field_name}. Known fields: {known_fields}"
|
38
|
-
)
|
39
29
|
_run = run or wandb.run
|
40
|
-
with
|
30
|
+
with telemetry.context(run=_run) as tel:
|
41
31
|
setattr(tel.deprecated, field_name, True)
|
32
|
+
|
42
33
|
wandb.termwarn(warning_message, repeat=False)
|
wandb/sdk/lib/disabled.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
|
+
from wandb.proto.wandb_deprecated import Deprecated
|
3
4
|
from wandb.sdk.lib import deprecate
|
4
5
|
|
5
6
|
|
@@ -23,7 +24,7 @@ class RunDisabled:
|
|
23
24
|
|
24
25
|
def __getattr__(self, name: str) -> Any:
|
25
26
|
deprecate.deprecate(
|
26
|
-
field_name=
|
27
|
+
field_name=Deprecated.run_disabled,
|
27
28
|
warning_message="RunDisabled is deprecated and is a no-op. "
|
28
29
|
'`wandb.init(mode="disabled")` now returns and instance of `wandb.sdk.wandb_run.Run`.',
|
29
30
|
)
|
wandb/sdk/lib/printer.py
CHANGED
@@ -20,6 +20,7 @@ import click
|
|
20
20
|
|
21
21
|
import wandb
|
22
22
|
from wandb.errors import term
|
23
|
+
from wandb.sdk import wandb_setup
|
23
24
|
|
24
25
|
from . import ipython, sparkline
|
25
26
|
|
@@ -98,12 +99,21 @@ _JUPYTER_PANEL_STYLES = """
|
|
98
99
|
"""
|
99
100
|
|
100
101
|
|
101
|
-
def new_printer() -> Printer:
|
102
|
-
"""Returns a
|
102
|
+
def new_printer(settings: wandb.Settings | None = None) -> Printer:
|
103
|
+
"""Returns a printer appropriate for the environment we're in.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
settings: The settings of a run. If not provided and `wandb.setup()`
|
107
|
+
has been called, then global settings are used. Otherwise,
|
108
|
+
settings (such as silent mode) are ignored.
|
109
|
+
"""
|
110
|
+
if not settings and (singleton := wandb_setup.singleton()):
|
111
|
+
settings = singleton.settings
|
112
|
+
|
103
113
|
if ipython.in_jupyter():
|
104
|
-
return _PrinterJupyter()
|
114
|
+
return _PrinterJupyter(settings=settings)
|
105
115
|
else:
|
106
|
-
return _PrinterTerm()
|
116
|
+
return _PrinterTerm(settings=settings)
|
107
117
|
|
108
118
|
|
109
119
|
class Printer(abc.ABC):
|
@@ -281,13 +291,18 @@ class DynamicText(abc.ABC):
|
|
281
291
|
|
282
292
|
|
283
293
|
class _PrinterTerm(Printer):
|
284
|
-
def __init__(self) -> None:
|
294
|
+
def __init__(self, *, settings: wandb.Settings | None) -> None:
|
285
295
|
super().__init__()
|
296
|
+
self._settings = settings
|
286
297
|
self._progress = itertools.cycle(["-", "\\", "|", "/"])
|
287
298
|
|
288
299
|
@override
|
289
300
|
@contextlib.contextmanager
|
290
301
|
def dynamic_text(self) -> Iterator[DynamicText | None]:
|
302
|
+
if self._settings and self._settings.silent:
|
303
|
+
yield None
|
304
|
+
return
|
305
|
+
|
291
306
|
with term.dynamic_text() as handle:
|
292
307
|
if not handle:
|
293
308
|
yield None
|
@@ -301,6 +316,9 @@ class _PrinterTerm(Printer):
|
|
301
316
|
*,
|
302
317
|
level: str | int | None = None,
|
303
318
|
) -> None:
|
319
|
+
if self._settings and self._settings.silent:
|
320
|
+
return
|
321
|
+
|
304
322
|
text = "\n".join(text) if isinstance(text, (list, tuple)) else text
|
305
323
|
self._display_fn_mapping(level)(text)
|
306
324
|
|
@@ -323,10 +341,16 @@ class _PrinterTerm(Printer):
|
|
323
341
|
|
324
342
|
@override
|
325
343
|
def progress_update(self, text: str, percent_done: float | None = None) -> None:
|
344
|
+
if self._settings and self._settings.silent:
|
345
|
+
return
|
346
|
+
|
326
347
|
wandb.termlog(f"{next(self._progress)} {text}", newline=False)
|
327
348
|
|
328
349
|
@override
|
329
350
|
def progress_close(self, text: str | None = None) -> None:
|
351
|
+
if self._settings and self._settings.silent:
|
352
|
+
return
|
353
|
+
|
330
354
|
text = text or " " * 79
|
331
355
|
wandb.termlog(text)
|
332
356
|
|
@@ -422,8 +446,9 @@ class _DynamicTermText(DynamicText):
|
|
422
446
|
|
423
447
|
|
424
448
|
class _PrinterJupyter(Printer):
|
425
|
-
def __init__(self) -> None:
|
449
|
+
def __init__(self, *, settings: wandb.Settings | None) -> None:
|
426
450
|
super().__init__()
|
451
|
+
self._settings = settings
|
427
452
|
self._progress = ipython.jupyter_progress_bar()
|
428
453
|
|
429
454
|
from IPython import display
|
@@ -433,6 +458,10 @@ class _PrinterJupyter(Printer):
|
|
433
458
|
@override
|
434
459
|
@contextlib.contextmanager
|
435
460
|
def dynamic_text(self) -> Iterator[DynamicText | None]:
|
461
|
+
if self._settings and self._settings.silent:
|
462
|
+
yield None
|
463
|
+
return
|
464
|
+
|
436
465
|
handle = self._ipython_display.display(
|
437
466
|
self._ipython_display.HTML(""),
|
438
467
|
display_id=True,
|
@@ -452,7 +481,7 @@ class _PrinterJupyter(Printer):
|
|
452
481
|
*,
|
453
482
|
level: str | int | None = None,
|
454
483
|
) -> None:
|
455
|
-
if
|
484
|
+
if self._settings and self._settings.silent:
|
456
485
|
return
|
457
486
|
|
458
487
|
text = "<br>".join(text) if isinstance(text, (list, tuple)) else text
|
@@ -507,7 +536,7 @@ class _PrinterJupyter(Printer):
|
|
507
536
|
text: str,
|
508
537
|
percent_done: float | None = None,
|
509
538
|
) -> None:
|
510
|
-
if not self._progress:
|
539
|
+
if (self._settings and self._settings.silent) or not self._progress:
|
511
540
|
return
|
512
541
|
|
513
542
|
if percent_done is None:
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import asyncio
|
2
|
+
from typing import Callable, TypeVar
|
3
|
+
|
4
|
+
from wandb.sdk.lib import asyncio_compat, printer
|
5
|
+
|
6
|
+
_T = TypeVar("_T")
|
7
|
+
|
8
|
+
|
9
|
+
def run_async_with_spinner(
|
10
|
+
spinner_printer: printer.Printer,
|
11
|
+
text: str,
|
12
|
+
func: Callable[[], _T],
|
13
|
+
) -> _T:
|
14
|
+
"""Run a slow function while displaying a loading icon.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
spinner_printer: The printer to use to display text.
|
18
|
+
text: The text to display next to the spinner while the function runs.
|
19
|
+
func: The function to run.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
The result of func.
|
23
|
+
"""
|
24
|
+
|
25
|
+
async def _loop_run_with_spinner() -> _T:
|
26
|
+
func_running = asyncio.Event()
|
27
|
+
|
28
|
+
async def update_spinner() -> None:
|
29
|
+
tick = 0
|
30
|
+
with spinner_printer.dynamic_text() as text_area:
|
31
|
+
if text_area:
|
32
|
+
while not func_running.is_set():
|
33
|
+
spinner = spinner_printer.loading_symbol(tick)
|
34
|
+
text_area.set_text(f"{spinner} {text}")
|
35
|
+
tick += 1
|
36
|
+
await asyncio.sleep(0.1)
|
37
|
+
else:
|
38
|
+
spinner_printer.display(text)
|
39
|
+
|
40
|
+
async with asyncio_compat.open_task_group() as group:
|
41
|
+
group.start_soon(update_spinner())
|
42
|
+
res = await asyncio.get_running_loop().run_in_executor(None, func)
|
43
|
+
func_running.set()
|
44
|
+
return res
|
45
|
+
|
46
|
+
return asyncio_compat.run(_loop_run_with_spinner)
|
wandb/sdk/lib/redirect.py
CHANGED
@@ -534,12 +534,15 @@ class StreamWrapper(RedirectBase):
|
|
534
534
|
self,
|
535
535
|
src: Literal["stdout", "stderr"],
|
536
536
|
cbs: Iterable[Callable[[str], None]] = (),
|
537
|
+
*,
|
538
|
+
flush_periodically: bool,
|
537
539
|
) -> None:
|
538
540
|
super().__init__(src=src, cbs=cbs)
|
539
541
|
self._uninstall: Callable[[], None] | None = None
|
540
542
|
self._emulator = TerminalEmulator()
|
541
543
|
self._queue: queue.Queue[str] = queue.Queue()
|
542
544
|
self._stopped = threading.Event()
|
545
|
+
self._flush_periodically = flush_periodically
|
543
546
|
|
544
547
|
def _emulator_write(self) -> None:
|
545
548
|
while True:
|
@@ -600,7 +603,7 @@ class StreamWrapper(RedirectBase):
|
|
600
603
|
self._emulator_write_thread.daemon = True
|
601
604
|
self._emulator_write_thread.start()
|
602
605
|
|
603
|
-
if
|
606
|
+
if self._flush_periodically:
|
604
607
|
self._callback_thread = threading.Thread(target=self._callback)
|
605
608
|
self._callback_thread.daemon = True
|
606
609
|
self._callback_thread.start()
|
@@ -732,10 +735,11 @@ _redirects: dict[str, Redirect | None] = {"stdout": None, "stderr": None}
|
|
732
735
|
class Redirect(RedirectBase):
|
733
736
|
"""Redirect low level file descriptors."""
|
734
737
|
|
735
|
-
def __init__(self, src, cbs=()):
|
738
|
+
def __init__(self, src, cbs=(), *, flush_periodically: bool):
|
736
739
|
super().__init__(src=src, cbs=cbs)
|
737
740
|
self._installed = False
|
738
741
|
self._emulator = TerminalEmulator()
|
742
|
+
self._flush_periodically = flush_periodically
|
739
743
|
|
740
744
|
def _pipe(self):
|
741
745
|
if pty:
|
@@ -767,7 +771,7 @@ class Redirect(RedirectBase):
|
|
767
771
|
self._emulator_write_thread = threading.Thread(target=self._emulator_write)
|
768
772
|
self._emulator_write_thread.daemon = True
|
769
773
|
self._emulator_write_thread.start()
|
770
|
-
if
|
774
|
+
if self._flush_periodically:
|
771
775
|
self._callback_thread = threading.Thread(target=self._callback)
|
772
776
|
self._callback_thread.daemon = True
|
773
777
|
self._callback_thread.start()
|
@@ -776,8 +780,9 @@ class Redirect(RedirectBase):
|
|
776
780
|
if not self._installed:
|
777
781
|
return
|
778
782
|
self._installed = False
|
779
|
-
# If the user printed a very long string (millions of chars) right
|
780
|
-
# it will take a while for it to reach pipe relay.
|
783
|
+
# If the user printed a very long string (millions of chars) right
|
784
|
+
# before run.finish(), it will take a while for it to reach pipe relay.
|
785
|
+
# 1 second is enough time for ~5 million chars.
|
781
786
|
time.sleep(1)
|
782
787
|
self._stopped.set()
|
783
788
|
os.dup2(self._orig_src_fd, self.src_fd)
|
wandb/sdk/lib/run_moment.py
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
4
|
+
from typing import Union
|
5
5
|
from urllib import parse
|
6
6
|
|
7
|
-
_STEP = Literal["_step"]
|
8
|
-
|
9
7
|
|
10
8
|
@dataclass
|
11
9
|
class RunMoment:
|
@@ -19,10 +17,10 @@ class RunMoment:
|
|
19
17
|
run: str
|
20
18
|
"""run ID"""
|
21
19
|
|
22
|
-
value: int
|
20
|
+
value: Union[int, float]
|
23
21
|
"""Value of the metric."""
|
24
22
|
|
25
|
-
metric:
|
23
|
+
metric: str = "_step"
|
26
24
|
"""Metric to use to determine the moment in the run.
|
27
25
|
|
28
26
|
Currently, only the metric '_step' is supported.
|
@@ -81,4 +79,4 @@ class RunMoment:
|
|
81
79
|
except ValueError as e:
|
82
80
|
raise parse_err from e
|
83
81
|
|
84
|
-
return cls(run=run, metric=
|
82
|
+
return cls(run=run, metric=metric, value=num_value)
|
@@ -0,0 +1,161 @@
|
|
1
|
+
"""Logging configuration for the "wandb" logger.
|
2
|
+
|
3
|
+
Most log statements in wandb are made in the context of a run and should be
|
4
|
+
redirected to that run's log file (usually named 'debug.log'). This module
|
5
|
+
provides a context manager to temporarily set the current run ID and registers
|
6
|
+
a global handler for the 'wandb' logger that sends log statements to the right
|
7
|
+
place.
|
8
|
+
|
9
|
+
All functions in this module are threadsafe.
|
10
|
+
|
11
|
+
NOTE: The pytest caplog fixture will fail to capture logs from the wandb logger
|
12
|
+
because they are not propagated to the root logger.
|
13
|
+
"""
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import contextlib
|
18
|
+
import contextvars
|
19
|
+
import logging
|
20
|
+
import pathlib
|
21
|
+
from typing import Iterator
|
22
|
+
|
23
|
+
|
24
|
+
class _NotRunSpecific:
|
25
|
+
"""Sentinel for `not_run_specific()`."""
|
26
|
+
|
27
|
+
|
28
|
+
_NOT_RUN_SPECIFIC = _NotRunSpecific()
|
29
|
+
|
30
|
+
|
31
|
+
_run_id: contextvars.ContextVar[str | _NotRunSpecific | None] = contextvars.ContextVar(
|
32
|
+
"_run_id",
|
33
|
+
default=None,
|
34
|
+
)
|
35
|
+
|
36
|
+
_logger = logging.getLogger("wandb")
|
37
|
+
|
38
|
+
|
39
|
+
def configure_wandb_logger() -> None:
|
40
|
+
"""Configures the global 'wandb' logger.
|
41
|
+
|
42
|
+
The wandb logger is not intended to be customized by users. Instead, it is
|
43
|
+
used as a mechanism to redirect log messages into wandb run-specific log
|
44
|
+
files.
|
45
|
+
|
46
|
+
This function is idempotent: calling it multiple times has the same effect.
|
47
|
+
"""
|
48
|
+
# Send all DEBUG and above messages to registered handlers.
|
49
|
+
#
|
50
|
+
# Per-run handlers can set different levels.
|
51
|
+
_logger.setLevel(logging.DEBUG)
|
52
|
+
|
53
|
+
# Do not propagate wandb logs to the root logger, which the user may have
|
54
|
+
# configured to point elsewhere. All wandb log messages should go to a run's
|
55
|
+
# log file.
|
56
|
+
_logger.propagate = False
|
57
|
+
|
58
|
+
# If no handlers are configured for the 'wandb' logger, don't activate the
|
59
|
+
# "lastResort" handler which sends messages to stderr with a level of
|
60
|
+
# WARNING by default.
|
61
|
+
#
|
62
|
+
# This occurs in wandb code that runs outside the context of a Run and
|
63
|
+
# not as part of the CLI.
|
64
|
+
#
|
65
|
+
# Most such code uses the `termlog` / `termwarn` / `termerror` methods
|
66
|
+
# to communicate with the user. When that code executes while a run is
|
67
|
+
# active, its logger messages go to that run's log file.
|
68
|
+
if not _logger.handlers:
|
69
|
+
_logger.addHandler(logging.NullHandler())
|
70
|
+
|
71
|
+
|
72
|
+
@contextlib.contextmanager
|
73
|
+
def log_to_run(run_id: str | None) -> Iterator[None]:
|
74
|
+
"""Direct all wandb log messages to the given run.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
id: The current run ID, or None if actions in the context manager are
|
78
|
+
not associated to a specific run. In the latter case, log messages
|
79
|
+
will go to all runs.
|
80
|
+
|
81
|
+
Usage:
|
82
|
+
|
83
|
+
with wb_logging.run_id(...):
|
84
|
+
... # Log messages here go to the specified run's logger.
|
85
|
+
"""
|
86
|
+
token = _run_id.set(run_id)
|
87
|
+
try:
|
88
|
+
yield
|
89
|
+
finally:
|
90
|
+
_run_id.reset(token)
|
91
|
+
|
92
|
+
|
93
|
+
@contextlib.contextmanager
|
94
|
+
def log_to_all_runs() -> Iterator[None]:
|
95
|
+
"""Direct wandb log messages to all runs.
|
96
|
+
|
97
|
+
Unlike `log_to_run(None)`, this indicates an intentional choice.
|
98
|
+
This is often convenient to use as a decorator:
|
99
|
+
|
100
|
+
@wb_logging.log_to_all_runs()
|
101
|
+
def my_func():
|
102
|
+
... # Log messages here go to the specified run's logger.
|
103
|
+
"""
|
104
|
+
token = _run_id.set(_NOT_RUN_SPECIFIC)
|
105
|
+
try:
|
106
|
+
yield
|
107
|
+
finally:
|
108
|
+
_run_id.reset(token)
|
109
|
+
|
110
|
+
|
111
|
+
def add_file_handler(run_id: str, filepath: pathlib.Path) -> logging.Handler:
|
112
|
+
"""Direct log messages for a run to a file.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
run_id: The run for which to create a log file.
|
116
|
+
filepath: The file to write log messages to.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
The added handler which can then be configured further or removed
|
120
|
+
from the 'wandb' logger directly.
|
121
|
+
|
122
|
+
The default logging level is INFO.
|
123
|
+
"""
|
124
|
+
handler = logging.FileHandler(filepath)
|
125
|
+
handler.setLevel(logging.INFO)
|
126
|
+
handler.addFilter(_RunIDFilter(run_id))
|
127
|
+
handler.setFormatter(
|
128
|
+
logging.Formatter(
|
129
|
+
"%(asctime)s %(levelname)-7s %(threadName)-10s:%(process)d"
|
130
|
+
" [%(filename)s:%(funcName)s():%(lineno)s]%(run_id_tag)s"
|
131
|
+
" %(message)s"
|
132
|
+
)
|
133
|
+
)
|
134
|
+
|
135
|
+
_logger.addHandler(handler)
|
136
|
+
return handler
|
137
|
+
|
138
|
+
|
139
|
+
class _RunIDFilter(logging.Filter):
|
140
|
+
"""Filters out messages logged for a different run."""
|
141
|
+
|
142
|
+
def __init__(self, run_id: str) -> None:
|
143
|
+
"""Create a _RunIDFilter.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
run_id: Allows messages when the run ID is this or None.
|
147
|
+
"""
|
148
|
+
self._run_id = run_id
|
149
|
+
|
150
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
151
|
+
run_id = _run_id.get()
|
152
|
+
|
153
|
+
if run_id is None:
|
154
|
+
record.run_id_tag = " [no run ID]"
|
155
|
+
return True
|
156
|
+
elif isinstance(run_id, _NotRunSpecific):
|
157
|
+
record.run_id_tag = " [all runs]"
|
158
|
+
return True
|
159
|
+
else:
|
160
|
+
record.run_id_tag = ""
|
161
|
+
return run_id == self._run_id
|
wandb/sdk/service/server_sock.py
CHANGED
@@ -5,6 +5,7 @@ import time
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
|
6
6
|
|
7
7
|
import wandb
|
8
|
+
from wandb.proto import wandb_internal_pb2 as pb
|
8
9
|
from wandb.proto import wandb_server_pb2 as spb
|
9
10
|
from wandb.sdk.internal.settings_static import SettingsStatic
|
10
11
|
|
@@ -156,27 +157,31 @@ class SockServerReadThread(threading.Thread):
|
|
156
157
|
inform_attach_response=inform_attach_response,
|
157
158
|
)
|
158
159
|
self._sock_client.send_server_response(response)
|
159
|
-
iface = self._mux.get_stream(stream_id).interface
|
160
|
-
|
161
|
-
assert iface
|
162
160
|
|
163
161
|
def server_record_communicate(self, sreq: "spb.ServerRequest") -> None:
|
164
|
-
|
165
|
-
# encode relay information so the right socket picks up the data
|
166
|
-
record.control.relay_id = self._sock_client._sockid
|
167
|
-
stream_id = record._info.stream_id
|
168
|
-
iface = self._mux.get_stream(stream_id).interface
|
169
|
-
assert iface.record_q
|
170
|
-
iface.record_q.put(record)
|
162
|
+
self._put_record(sreq.record_communicate)
|
171
163
|
|
172
164
|
def server_record_publish(self, sreq: "spb.ServerRequest") -> None:
|
173
|
-
|
165
|
+
self._put_record(sreq.record_publish)
|
166
|
+
|
167
|
+
def _put_record(self, record: "pb.Record") -> None:
|
174
168
|
# encode relay information so the right socket picks up the data
|
175
169
|
record.control.relay_id = self._sock_client._sockid
|
176
170
|
stream_id = record._info.stream_id
|
177
|
-
|
178
|
-
|
179
|
-
|
171
|
+
|
172
|
+
try:
|
173
|
+
iface = self._mux.get_stream(stream_id).interface
|
174
|
+
|
175
|
+
except KeyError:
|
176
|
+
# We should log the error but cannot because it may print to console
|
177
|
+
# due to how logging is set up. This error usually happens if
|
178
|
+
# a record is sent when no run is active, but during this time the
|
179
|
+
# logger prints to the console.
|
180
|
+
pass
|
181
|
+
|
182
|
+
else:
|
183
|
+
assert iface.record_q
|
184
|
+
iface.record_q.put(record)
|
180
185
|
|
181
186
|
def server_inform_finish(self, sreq: "spb.ServerRequest") -> None:
|
182
187
|
request = sreq.inform_finish
|
wandb/sdk/service/service.py
CHANGED
@@ -14,10 +14,10 @@ import tempfile
|
|
14
14
|
import time
|
15
15
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
16
16
|
|
17
|
-
from wandb import _sentry
|
17
|
+
from wandb import _sentry
|
18
18
|
from wandb.env import core_debug, error_reporting_enabled, is_require_legacy_service
|
19
19
|
from wandb.errors import Error, WandbCoreNotAvailableError
|
20
|
-
from wandb.errors.
|
20
|
+
from wandb.errors.term import termlog, termwarn
|
21
21
|
from wandb.util import get_core_path, get_module
|
22
22
|
|
23
23
|
from . import _startup_debug, port_file
|
@@ -163,13 +163,15 @@ class _Service:
|
|
163
163
|
service_args.extend(["--log-level", "-4"])
|
164
164
|
|
165
165
|
exec_cmd_list = []
|
166
|
-
termlog(
|
167
|
-
"Using wandb-core as the SDK backend. Please refer to "
|
168
|
-
f"{url_registry.url('wandb-core')} for more information.",
|
169
|
-
repeat=False,
|
170
|
-
)
|
171
166
|
else:
|
172
167
|
service_args.extend(["wandb", "service", "--debug"])
|
168
|
+
termwarn(
|
169
|
+
"Using legacy-service, which is deprecated. If this is"
|
170
|
+
" unintentional, you can fix it by ensuring you do not call"
|
171
|
+
" `wandb.require('legacy-service')` and do not set the"
|
172
|
+
" WANDB_X_REQUIRE_LEGACY_SERVICE environment"
|
173
|
+
" variable."
|
174
|
+
)
|
173
175
|
|
174
176
|
service_args += [
|
175
177
|
"--port-filename",
|
wandb/sdk/service/streams.py
CHANGED
@@ -198,6 +198,11 @@ class StreamMux:
|
|
198
198
|
return stream_id in self._streams
|
199
199
|
|
200
200
|
def get_stream(self, stream_id: str) -> StreamRecord:
|
201
|
+
"""Returns the StreamRecord for the ID.
|
202
|
+
|
203
|
+
Raises:
|
204
|
+
KeyError: If a corresponding StreamRecord does not exist.
|
205
|
+
"""
|
201
206
|
with self._streams_lock:
|
202
207
|
stream = self._streams[stream_id]
|
203
208
|
return stream
|
wandb/sdk/verify/verify.py
CHANGED
@@ -132,7 +132,10 @@ def check_run(api: Api) -> bool:
|
|
132
132
|
f.close()
|
133
133
|
|
134
134
|
with wandb.init(
|
135
|
-
id=nice_id("check_run"),
|
135
|
+
id=nice_id("check_run"),
|
136
|
+
reinit=True,
|
137
|
+
config=config,
|
138
|
+
project=PROJECT_NAME,
|
136
139
|
) as run:
|
137
140
|
run_id = run.id
|
138
141
|
entity = run.entity
|
@@ -155,7 +158,7 @@ def check_run(api: Api) -> bool:
|
|
155
158
|
"Failed to log to media. Contact W&B for support."
|
156
159
|
)
|
157
160
|
|
158
|
-
|
161
|
+
run.save(filepath)
|
159
162
|
public_api = wandb.Api()
|
160
163
|
prev_run = public_api.run(f"{entity}/{PROJECT_NAME}/{run_id}")
|
161
164
|
# raise Exception(prev_run.__dict__)
|
@@ -379,7 +382,7 @@ def check_graphql_put(api: Api, host: str) -> Tuple[bool, Optional[str]]:
|
|
379
382
|
project=PROJECT_NAME,
|
380
383
|
config={"test": "put to graphql"},
|
381
384
|
) as run:
|
382
|
-
|
385
|
+
run.save(gql_fp)
|
383
386
|
public_api = wandb.Api()
|
384
387
|
prev_run = public_api.run(f"{run.entity}/{PROJECT_NAME}/{run.id}")
|
385
388
|
if prev_run is None:
|