wandb 0.19.9__py3-none-win32.whl → 0.19.11__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 +1 -1
- wandb/__init__.pyi +6 -3
- wandb/_pydantic/__init__.py +14 -8
- wandb/_pydantic/base.py +51 -36
- wandb/_pydantic/utils.py +73 -0
- wandb/_pydantic/v1_compat.py +79 -57
- wandb/apis/public/__init__.py +2 -2
- wandb/apis/public/api.py +684 -4
- wandb/apis/public/artifacts.py +377 -677
- wandb/apis/public/automations.py +69 -0
- wandb/apis/public/integrations.py +180 -0
- wandb/apis/public/projects.py +29 -0
- wandb/apis/public/registries/__init__.py +0 -0
- wandb/apis/public/registries/_freezable_list.py +179 -0
- wandb/apis/public/{registries.py → registries/registries_search.py} +22 -129
- wandb/apis/public/registries/registry.py +357 -0
- wandb/apis/public/registries/utils.py +140 -0
- wandb/apis/public/runs.py +58 -56
- wandb/apis/public/utils.py +107 -1
- wandb/automations/__init__.py +73 -0
- wandb/automations/_filters/__init__.py +40 -0
- wandb/automations/_filters/expressions.py +181 -0
- wandb/automations/_filters/operators.py +258 -0
- wandb/automations/_filters/run_metrics.py +332 -0
- wandb/automations/_generated/__init__.py +177 -0
- wandb/automations/_generated/create_automation.py +17 -0
- wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
- wandb/automations/_generated/delete_automation.py +17 -0
- wandb/automations/_generated/enums.py +33 -0
- wandb/automations/_generated/fragments.py +358 -0
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
- wandb/automations/_generated/get_automations.py +24 -0
- wandb/automations/_generated/get_automations_by_entity.py +26 -0
- wandb/automations/_generated/input_types.py +104 -0
- wandb/automations/_generated/integrations_by_entity.py +22 -0
- wandb/automations/_generated/operations.py +647 -0
- wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
- wandb/automations/_generated/update_automation.py +17 -0
- wandb/automations/_utils.py +237 -0
- wandb/automations/_validators.py +165 -0
- wandb/automations/actions.py +220 -0
- wandb/automations/automations.py +87 -0
- wandb/automations/events.py +287 -0
- wandb/automations/integrations.py +45 -0
- wandb/automations/scopes.py +78 -0
- wandb/beta/workflows.py +9 -10
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +3 -3
- wandb/env.py +11 -0
- wandb/integration/keras/keras.py +2 -1
- wandb/integration/langchain/wandb_tracer.py +2 -1
- wandb/jupyter.py +137 -118
- wandb/old/settings.py +4 -1
- 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 +8 -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 +289 -0
- wandb/sdk/artifacts/_generated/add_aliases.py +21 -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_aliases.py +21 -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/fetch_linked_artifacts.py +67 -0
- wandb/sdk/artifacts/_generated/fragments.py +221 -0
- wandb/sdk/artifacts/_generated/input_types.py +28 -0
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
- wandb/sdk/artifacts/_generated/operations.py +611 -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.py +26 -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 +57 -79
- wandb/sdk/artifacts/_validators.py +120 -1
- wandb/sdk/artifacts/artifact.py +419 -215
- wandb/sdk/artifacts/artifact_file_cache.py +4 -6
- wandb/sdk/artifacts/artifact_manifest_entry.py +13 -3
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
- wandb/sdk/artifacts/storage_policy.py +3 -0
- wandb/sdk/data_types/base_types/media.py +2 -3
- 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 +12 -12
- 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 +59 -57
- wandb/sdk/interface/interface.py +4 -3
- wandb/sdk/internal/internal_api.py +21 -31
- wandb/sdk/internal/profiler.py +6 -5
- wandb/sdk/internal/run.py +13 -6
- wandb/sdk/internal/sender.py +5 -2
- wandb/sdk/launch/sweeps/utils.py +8 -0
- wandb/sdk/lib/apikey.py +25 -4
- 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/projects/_generated/__init__.py +47 -0
- wandb/sdk/projects/_generated/delete_project.py +22 -0
- wandb/sdk/projects/_generated/enums.py +4 -0
- wandb/sdk/projects/_generated/fetch_registry.py +22 -0
- wandb/sdk/projects/_generated/fragments.py +41 -0
- wandb/sdk/projects/_generated/input_types.py +13 -0
- wandb/sdk/projects/_generated/operations.py +88 -0
- wandb/sdk/projects/_generated/rename_project.py +27 -0
- wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
- wandb/sdk/service/server_sock.py +19 -14
- wandb/sdk/service/service.py +18 -8
- wandb/sdk/service/streams.py +5 -0
- wandb/sdk/verify/verify.py +6 -3
- wandb/sdk/wandb_init.py +217 -70
- wandb/sdk/wandb_login.py +13 -4
- wandb/sdk/wandb_run.py +419 -295
- wandb/sdk/wandb_settings.py +27 -10
- wandb/sdk/wandb_setup.py +61 -0
- wandb/util.py +33 -29
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/METADATA +5 -5
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/RECORD +153 -83
- wandb/_globals.py +0 -19
- wandb/sdk/internal/_generated/base.py +0 -226
- wandb/sdk/internal/_generated/typing_compat.py +0 -14
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_run.py
CHANGED
@@ -14,13 +14,26 @@ import sys
|
|
14
14
|
import threading
|
15
15
|
import time
|
16
16
|
import traceback
|
17
|
-
import warnings
|
18
17
|
from collections.abc import Mapping
|
19
18
|
from dataclasses import dataclass, field
|
20
19
|
from datetime import datetime, timedelta, timezone
|
21
20
|
from enum import IntEnum
|
22
21
|
from types import TracebackType
|
23
|
-
from typing import
|
22
|
+
from typing import (
|
23
|
+
TYPE_CHECKING,
|
24
|
+
Any,
|
25
|
+
Callable,
|
26
|
+
Literal,
|
27
|
+
NamedTuple,
|
28
|
+
Sequence,
|
29
|
+
TextIO,
|
30
|
+
TypeVar,
|
31
|
+
)
|
32
|
+
|
33
|
+
if sys.version_info < (3, 10):
|
34
|
+
from typing_extensions import Concatenate, ParamSpec
|
35
|
+
else:
|
36
|
+
from typing import Concatenate, ParamSpec
|
24
37
|
|
25
38
|
import requests
|
26
39
|
|
@@ -28,13 +41,13 @@ import wandb
|
|
28
41
|
import wandb.env
|
29
42
|
import wandb.util
|
30
43
|
from wandb import trigger
|
31
|
-
from wandb._globals import _datatypes_set_callback
|
32
44
|
from wandb.apis import internal, public
|
33
45
|
from wandb.apis.public import Api as PublicApi
|
34
46
|
from wandb.errors import CommError, UnsupportedError, UsageError
|
35
47
|
from wandb.errors.links import url_registry
|
36
48
|
from wandb.integration.torch import wandb_torch
|
37
49
|
from wandb.plot import CustomChart, Visualize
|
50
|
+
from wandb.proto.wandb_deprecated import Deprecated
|
38
51
|
from wandb.proto.wandb_internal_pb2 import (
|
39
52
|
MetadataRequest,
|
40
53
|
MetricRecord,
|
@@ -361,7 +374,13 @@ class RunStatusChecker:
|
|
361
374
|
self._internal_messages_thread.join()
|
362
375
|
|
363
376
|
|
364
|
-
|
377
|
+
_P = ParamSpec("_P")
|
378
|
+
_T = TypeVar("_T")
|
379
|
+
|
380
|
+
|
381
|
+
def _log_to_run(
|
382
|
+
func: Callable[Concatenate[Run, _P], _T],
|
383
|
+
) -> Callable[Concatenate[Run, _P], _T]:
|
365
384
|
"""Decorate a Run method to set the run ID in the logging context.
|
366
385
|
|
367
386
|
Any logs during the execution of the method go to the run's log file
|
@@ -375,7 +394,7 @@ def _log_to_run(func: Callable) -> Callable:
|
|
375
394
|
"""
|
376
395
|
|
377
396
|
@functools.wraps(func)
|
378
|
-
def wrapper(self: Run, *args, **kwargs) ->
|
397
|
+
def wrapper(self: Run, *args, **kwargs) -> _T:
|
379
398
|
# In "attach" usage, many properties of the Run are not initially
|
380
399
|
# populated.
|
381
400
|
if hasattr(self, "_settings"):
|
@@ -389,100 +408,118 @@ def _log_to_run(func: Callable) -> Callable:
|
|
389
408
|
return wrapper
|
390
409
|
|
391
410
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
class Dummy: ...
|
396
|
-
|
397
|
-
@classmethod
|
398
|
-
def _attach(cls, func: Callable) -> Callable:
|
399
|
-
@functools.wraps(func)
|
400
|
-
def wrapper(self: type[Run], *args: Any, **kwargs: Any) -> Any:
|
401
|
-
# * `_attach_id` is only assigned in service hence for all non-service cases
|
402
|
-
# it will be a passthrough.
|
403
|
-
# * `_attach_pid` is only assigned in _init (using _attach_pid guarantees single attach):
|
404
|
-
# - for non-fork case the object is shared through pickling so will be None.
|
405
|
-
# - for fork case the new process share mem space hence the value would be of parent process.
|
406
|
-
if (
|
407
|
-
getattr(self, "_attach_id", None)
|
408
|
-
and getattr(self, "_attach_pid", None) != os.getpid()
|
409
|
-
):
|
410
|
-
if cls._is_attaching:
|
411
|
-
message = (
|
412
|
-
f"Trying to attach `{func.__name__}` "
|
413
|
-
f"while in the middle of attaching `{cls._is_attaching}`"
|
414
|
-
)
|
415
|
-
raise RuntimeError(message)
|
416
|
-
cls._is_attaching = func.__name__
|
417
|
-
try:
|
418
|
-
wandb._attach(run=self) # type: ignore
|
419
|
-
except Exception as e:
|
420
|
-
# In case the attach fails we will raise the exception that caused the issue.
|
421
|
-
# This exception should be caught and fail the execution of the program.
|
422
|
-
cls._is_attaching = ""
|
423
|
-
raise e
|
424
|
-
cls._is_attaching = ""
|
425
|
-
return func(self, *args, **kwargs)
|
411
|
+
_is_attaching: str = ""
|
412
|
+
|
426
413
|
|
427
|
-
|
414
|
+
def _attach(
|
415
|
+
func: Callable[Concatenate[Run, _P], _T],
|
416
|
+
) -> Callable[Concatenate[Run, _P], _T]:
|
417
|
+
"""Decorate a Run method to auto-attach when in a new process.
|
428
418
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
419
|
+
When in a forked process or using a pickled Run instance, this automatically
|
420
|
+
connects to the service process to "attach" to the existing run.
|
421
|
+
"""
|
422
|
+
|
423
|
+
@functools.wraps(func)
|
424
|
+
def wrapper(self: Run, *args, **kwargs) -> _T:
|
425
|
+
global _is_attaching
|
436
426
|
|
437
|
-
|
438
|
-
|
439
|
-
|
427
|
+
# The _attach_id attribute is only None when running in the "disable
|
428
|
+
# service" mode.
|
429
|
+
#
|
430
|
+
# Since it is set early in `__init__` and included in the run's pickled
|
431
|
+
# state, the attribute always exists.
|
432
|
+
is_using_service = self._attach_id is not None
|
433
|
+
|
434
|
+
# The _attach_pid attribute is not pickled, so it might not exist.
|
435
|
+
# It is set when the run is initialized.
|
436
|
+
attach_pid = getattr(self, "_attach_pid", None)
|
437
|
+
|
438
|
+
if is_using_service and attach_pid != os.getpid():
|
439
|
+
if _is_attaching:
|
440
|
+
raise RuntimeError(
|
441
|
+
f"Trying to attach `{func.__name__}`"
|
442
|
+
f" while in the middle of attaching `{_is_attaching}`"
|
440
443
|
)
|
441
|
-
resolved_message = message or default_message
|
442
|
-
if only_warn:
|
443
|
-
warnings.warn(resolved_message, UserWarning, stacklevel=2)
|
444
|
-
else:
|
445
|
-
raise UsageError(resolved_message)
|
446
|
-
|
447
|
-
return wrapper_fn
|
448
|
-
|
449
|
-
return decorator_fn
|
450
|
-
|
451
|
-
@classmethod
|
452
|
-
def _noop(cls, func: Callable) -> Callable:
|
453
|
-
@functools.wraps(func)
|
454
|
-
def wrapper(self: type[Run], *args: Any, **kwargs: Any) -> Any:
|
455
|
-
# `_attach_id` is only assigned in service hence for all service cases
|
456
|
-
# it will be a passthrough. We don't pickle non-service so again a way
|
457
|
-
# to see that we are in non-service case
|
458
|
-
if getattr(self, "_attach_id", None) is None:
|
459
|
-
# `_init_pid` is only assigned in __init__ (this will be constant check for mp):
|
460
|
-
# - for non-fork case the object is shared through pickling,
|
461
|
-
# and we don't pickle non-service so will be None
|
462
|
-
# - for fork case the new process share mem space hence the value would be of parent process.
|
463
|
-
_init_pid = getattr(self, "_init_pid", None)
|
464
|
-
if _init_pid != os.getpid():
|
465
|
-
message = (
|
466
|
-
f"`{func.__name__}` ignored (called from pid={os.getpid()}, "
|
467
|
-
f"`init` called from pid={_init_pid}). "
|
468
|
-
f"See: {url_registry.url('multiprocess')}"
|
469
|
-
)
|
470
|
-
# - if this process was pickled in non-service case,
|
471
|
-
# we ignore the attributes (since pickle is not supported)
|
472
|
-
# - for fork case will use the settings of the parent process
|
473
|
-
# - only point of inconsistent behavior from forked and non-forked cases
|
474
|
-
settings = getattr(self, "_settings", None)
|
475
|
-
if settings and settings.strict:
|
476
|
-
wandb.termerror(message, repeat=False)
|
477
|
-
raise UnsupportedError(
|
478
|
-
f"`{func.__name__}` does not support multiprocessing"
|
479
|
-
)
|
480
|
-
wandb.termwarn(message, repeat=False)
|
481
|
-
return cls.Dummy()
|
482
444
|
|
445
|
+
_is_attaching = func.__name__
|
446
|
+
try:
|
447
|
+
wandb._attach(run=self) # type: ignore
|
448
|
+
finally:
|
449
|
+
_is_attaching = ""
|
450
|
+
|
451
|
+
return func(self, *args, **kwargs)
|
452
|
+
|
453
|
+
return wrapper
|
454
|
+
|
455
|
+
|
456
|
+
def _raise_if_finished(
|
457
|
+
func: Callable[Concatenate[Run, _P], _T],
|
458
|
+
) -> Callable[Concatenate[Run, _P], _T]:
|
459
|
+
"""Decorate a Run method to raise an error after the run is finished."""
|
460
|
+
|
461
|
+
@functools.wraps(func)
|
462
|
+
def wrapper_fn(self: Run, *args, **kwargs) -> _T:
|
463
|
+
if not getattr(self, "_is_finished", False):
|
464
|
+
return func(self, *args, **kwargs)
|
465
|
+
|
466
|
+
message = (
|
467
|
+
f"Run ({self.id}) is finished. The call to"
|
468
|
+
f" `{func.__name__}` will be ignored."
|
469
|
+
f" Please make sure that you are using an active run."
|
470
|
+
)
|
471
|
+
|
472
|
+
raise UsageError(message)
|
473
|
+
|
474
|
+
return wrapper_fn
|
475
|
+
|
476
|
+
|
477
|
+
def _noop_if_forked_with_no_service(
|
478
|
+
func: Callable[Concatenate[Run, _P], None],
|
479
|
+
) -> Callable[Concatenate[Run, _P], None]:
|
480
|
+
"""Do nothing if called in a forked process and service is disabled.
|
481
|
+
|
482
|
+
Disabling the service is a very old and barely supported setting.
|
483
|
+
"""
|
484
|
+
|
485
|
+
@functools.wraps(func)
|
486
|
+
def wrapper(self: Run, *args, **kwargs) -> None:
|
487
|
+
# The _attach_id attribute is only None when running in the "disable
|
488
|
+
# service" mode.
|
489
|
+
#
|
490
|
+
# Since it is set early in `__init__` and included in the run's pickled
|
491
|
+
# state, the attribute always exists.
|
492
|
+
is_using_service = self._attach_id is not None
|
493
|
+
|
494
|
+
# This is the PID in which the Run object was constructed. The attribute
|
495
|
+
# always exists because it is set early in `__init__` and is included
|
496
|
+
# in the pickled state in `__getstate__` and `__setstate__`.
|
497
|
+
#
|
498
|
+
# It is not equal to the current PID if the process was forked or if
|
499
|
+
# the Run object was pickled and sent to another process.
|
500
|
+
init_pid = self._init_pid
|
501
|
+
|
502
|
+
if is_using_service or init_pid == os.getpid():
|
483
503
|
return func(self, *args, **kwargs)
|
484
504
|
|
485
|
-
|
505
|
+
message = (
|
506
|
+
f"`{func.__name__}` ignored (called from pid={os.getpid()},"
|
507
|
+
f" `init` called from pid={init_pid})."
|
508
|
+
f" See: {url_registry.url('multiprocess')}"
|
509
|
+
)
|
510
|
+
|
511
|
+
# This attribute may not exist because it is not included in the run's
|
512
|
+
# pickled state.
|
513
|
+
settings = getattr(self, "_settings", None)
|
514
|
+
if settings and settings.strict:
|
515
|
+
wandb.termerror(message, repeat=False)
|
516
|
+
raise UnsupportedError(
|
517
|
+
f"`{func.__name__}` does not support multiprocessing"
|
518
|
+
)
|
519
|
+
wandb.termwarn(message, repeat=False)
|
520
|
+
return None
|
521
|
+
|
522
|
+
return wrapper
|
486
523
|
|
487
524
|
|
488
525
|
@dataclass
|
@@ -606,6 +643,7 @@ class Run:
|
|
606
643
|
) -> None:
|
607
644
|
# pid is set, so we know if this run object was initialized by this process
|
608
645
|
self._init_pid = os.getpid()
|
646
|
+
self._attach_id = None
|
609
647
|
|
610
648
|
if settings._noop:
|
611
649
|
# TODO: properly handle setting for disabled mode
|
@@ -647,14 +685,13 @@ class Run:
|
|
647
685
|
|
648
686
|
self._step = 0
|
649
687
|
self._starting_step = 0
|
688
|
+
self._start_runtime = 0
|
650
689
|
# TODO: eventually would be nice to make this configurable using self._settings._start_time
|
651
690
|
# need to test (jhr): if you set start time to 2 days ago and run a test for 15 minutes,
|
652
691
|
# does the total time get calculated right (not as 2 days and 15 minutes)?
|
653
692
|
self._start_time = time.time()
|
654
693
|
|
655
|
-
|
656
|
-
|
657
|
-
self._printer = printer.new_printer()
|
694
|
+
self._printer = printer.new_printer(settings)
|
658
695
|
|
659
696
|
self._torch_history: wandb_torch.TorchHistory | None = None # type: ignore
|
660
697
|
|
@@ -776,8 +813,9 @@ class Run:
|
|
776
813
|
self._unique_launch_artifact_sequence_names[sequence_name] = item
|
777
814
|
|
778
815
|
def _telemetry_callback(self, telem_obj: telemetry.TelemetryRecord) -> None:
|
779
|
-
if not hasattr(self, "_telemetry_obj"):
|
816
|
+
if not hasattr(self, "_telemetry_obj") or self._is_finished:
|
780
817
|
return
|
818
|
+
|
781
819
|
self._telemetry_obj.MergeFrom(telem_obj)
|
782
820
|
self._telemetry_obj_dirty = True
|
783
821
|
self._telemetry_flush()
|
@@ -846,47 +884,45 @@ class Run:
|
|
846
884
|
|
847
885
|
@property
|
848
886
|
@_log_to_run
|
849
|
-
@
|
887
|
+
@_attach
|
850
888
|
def settings(self) -> Settings:
|
851
889
|
"""A frozen copy of run's Settings object."""
|
852
890
|
return self._settings.model_copy(deep=True)
|
853
891
|
|
854
892
|
@property
|
855
893
|
@_log_to_run
|
856
|
-
@
|
894
|
+
@_attach
|
857
895
|
def dir(self) -> str:
|
858
896
|
"""The directory where files associated with the run are saved."""
|
859
897
|
return self._settings.files_dir
|
860
898
|
|
861
899
|
@property
|
862
900
|
@_log_to_run
|
863
|
-
@
|
901
|
+
@_attach
|
864
902
|
def config(self) -> wandb_config.Config:
|
865
903
|
"""Config object associated with this run."""
|
866
904
|
return self._config
|
867
905
|
|
868
906
|
@property
|
869
907
|
@_log_to_run
|
870
|
-
@
|
908
|
+
@_attach
|
871
909
|
def config_static(self) -> wandb_config.ConfigStatic:
|
872
910
|
return wandb_config.ConfigStatic(self._config)
|
873
911
|
|
874
912
|
@property
|
875
913
|
@_log_to_run
|
876
|
-
@
|
914
|
+
@_attach
|
877
915
|
def name(self) -> str | None:
|
878
916
|
"""Display name of the run.
|
879
917
|
|
880
918
|
Display names are not guaranteed to be unique and may be descriptive.
|
881
919
|
By default, they are randomly generated.
|
882
920
|
"""
|
883
|
-
|
884
|
-
return self._settings.run_name
|
885
|
-
return None
|
921
|
+
return self._settings.run_name
|
886
922
|
|
887
923
|
@name.setter
|
888
924
|
@_log_to_run
|
889
|
-
@
|
925
|
+
@_raise_if_finished
|
890
926
|
def name(self, name: str) -> None:
|
891
927
|
with telemetry.context(run=self) as tel:
|
892
928
|
tel.feature.set_run_name = True
|
@@ -896,18 +932,18 @@ class Run:
|
|
896
932
|
|
897
933
|
@property
|
898
934
|
@_log_to_run
|
899
|
-
@
|
935
|
+
@_attach
|
900
936
|
def notes(self) -> str | None:
|
901
937
|
"""Notes associated with the run, if there are any.
|
902
938
|
|
903
|
-
Notes can be a multiline string and can also use markdown and latex
|
904
|
-
inside `$$`, like `$x + 3$`.
|
939
|
+
Notes can be a multiline string and can also use markdown and latex
|
940
|
+
equations inside `$$`, like `$x + 3$`.
|
905
941
|
"""
|
906
942
|
return self._settings.run_notes
|
907
943
|
|
908
944
|
@notes.setter
|
909
945
|
@_log_to_run
|
910
|
-
@
|
946
|
+
@_raise_if_finished
|
911
947
|
def notes(self, notes: str) -> None:
|
912
948
|
self._settings.run_notes = notes
|
913
949
|
if self._backend and self._backend.interface:
|
@@ -915,14 +951,14 @@ class Run:
|
|
915
951
|
|
916
952
|
@property
|
917
953
|
@_log_to_run
|
918
|
-
@
|
954
|
+
@_attach
|
919
955
|
def tags(self) -> tuple | None:
|
920
956
|
"""Tags associated with the run, if there are any."""
|
921
957
|
return self._settings.run_tags or ()
|
922
958
|
|
923
959
|
@tags.setter
|
924
960
|
@_log_to_run
|
925
|
-
@
|
961
|
+
@_raise_if_finished
|
926
962
|
def tags(self, tags: Sequence) -> None:
|
927
963
|
with telemetry.context(run=self) as tel:
|
928
964
|
tel.feature.set_run_tags = True
|
@@ -932,18 +968,17 @@ class Run:
|
|
932
968
|
|
933
969
|
@property
|
934
970
|
@_log_to_run
|
935
|
-
@
|
971
|
+
@_attach
|
936
972
|
def id(self) -> str:
|
937
973
|
"""Identifier for this run."""
|
938
|
-
|
939
|
-
assert self._settings.run_id is not None
|
974
|
+
assert self._settings.run_id is not None
|
940
975
|
return self._settings.run_id
|
941
976
|
|
942
977
|
@property
|
943
978
|
@_log_to_run
|
944
|
-
@
|
979
|
+
@_attach
|
945
980
|
def sweep_id(self) -> str | None:
|
946
|
-
"""
|
981
|
+
"""Identifier for the sweep associated with the run, if there is one."""
|
947
982
|
return self._settings.sweep_id
|
948
983
|
|
949
984
|
def _get_path(self) -> str:
|
@@ -959,7 +994,7 @@ class Run:
|
|
959
994
|
|
960
995
|
@property
|
961
996
|
@_log_to_run
|
962
|
-
@
|
997
|
+
@_attach
|
963
998
|
def path(self) -> str:
|
964
999
|
"""Path to the run.
|
965
1000
|
|
@@ -970,28 +1005,28 @@ class Run:
|
|
970
1005
|
|
971
1006
|
@property
|
972
1007
|
@_log_to_run
|
973
|
-
@
|
1008
|
+
@_attach
|
974
1009
|
def start_time(self) -> float:
|
975
1010
|
"""Unix timestamp (in seconds) of when the run started."""
|
976
1011
|
return self._start_time
|
977
1012
|
|
978
1013
|
@property
|
979
1014
|
@_log_to_run
|
980
|
-
@
|
1015
|
+
@_attach
|
981
1016
|
def starting_step(self) -> int:
|
982
1017
|
"""The first step of the run."""
|
983
1018
|
return self._starting_step
|
984
1019
|
|
985
1020
|
@property
|
986
1021
|
@_log_to_run
|
987
|
-
@
|
1022
|
+
@_attach
|
988
1023
|
def resumed(self) -> bool:
|
989
1024
|
"""True if the run was resumed, False otherwise."""
|
990
1025
|
return self._settings.resumed
|
991
1026
|
|
992
1027
|
@property
|
993
1028
|
@_log_to_run
|
994
|
-
@
|
1029
|
+
@_attach
|
995
1030
|
def step(self) -> int:
|
996
1031
|
"""Current value of the step.
|
997
1032
|
|
@@ -1001,34 +1036,34 @@ class Run:
|
|
1001
1036
|
|
1002
1037
|
@property
|
1003
1038
|
@_log_to_run
|
1004
|
-
@
|
1039
|
+
@_attach
|
1005
1040
|
def mode(self) -> str:
|
1006
1041
|
"""For compatibility with `0.9.x` and earlier, deprecate eventually."""
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1042
|
+
deprecate.deprecate(
|
1043
|
+
field_name=Deprecated.run__mode,
|
1044
|
+
warning_message=(
|
1045
|
+
"The mode property of wandb.run is deprecated "
|
1046
|
+
"and will be removed in a future release."
|
1047
|
+
),
|
1048
|
+
run=self,
|
1049
|
+
)
|
1015
1050
|
return "dryrun" if self._settings._offline else "run"
|
1016
1051
|
|
1017
1052
|
@property
|
1018
1053
|
@_log_to_run
|
1019
|
-
@
|
1054
|
+
@_attach
|
1020
1055
|
def offline(self) -> bool:
|
1021
1056
|
return self._settings._offline
|
1022
1057
|
|
1023
1058
|
@property
|
1024
1059
|
@_log_to_run
|
1025
|
-
@
|
1060
|
+
@_attach
|
1026
1061
|
def disabled(self) -> bool:
|
1027
1062
|
return self._settings._noop
|
1028
1063
|
|
1029
1064
|
@property
|
1030
1065
|
@_log_to_run
|
1031
|
-
@
|
1066
|
+
@_attach
|
1032
1067
|
def group(self) -> str:
|
1033
1068
|
"""Name of the group associated with the run.
|
1034
1069
|
|
@@ -1043,24 +1078,67 @@ class Run:
|
|
1043
1078
|
|
1044
1079
|
@property
|
1045
1080
|
@_log_to_run
|
1046
|
-
@
|
1081
|
+
@_attach
|
1047
1082
|
def job_type(self) -> str:
|
1048
1083
|
return self._settings.run_job_type or ""
|
1049
1084
|
|
1050
1085
|
def project_name(self) -> str:
|
1051
|
-
|
1052
|
-
|
1086
|
+
"""Name of the W&B project associated with the run.
|
1087
|
+
|
1088
|
+
Note: this method is deprecated and will be removed in a future release.
|
1089
|
+
Please use `run.project` instead.
|
1090
|
+
"""
|
1091
|
+
deprecate.deprecate(
|
1092
|
+
field_name=Deprecated.run__project_name,
|
1093
|
+
warning_message=(
|
1094
|
+
"The project_name method is deprecated and will be removed in a"
|
1095
|
+
" future release. Please use `run.project` instead."
|
1096
|
+
),
|
1097
|
+
)
|
1098
|
+
return self.project
|
1053
1099
|
|
1054
1100
|
@property
|
1055
1101
|
@_log_to_run
|
1056
|
-
@
|
1102
|
+
@_attach
|
1057
1103
|
def project(self) -> str:
|
1058
1104
|
"""Name of the W&B project associated with the run."""
|
1059
|
-
|
1105
|
+
assert self._settings.project is not None
|
1106
|
+
return self._settings.project
|
1060
1107
|
|
1061
|
-
@_run_decorator._noop_on_finish()
|
1062
1108
|
@_log_to_run
|
1063
|
-
|
1109
|
+
def get_project_url(self) -> str | None:
|
1110
|
+
"""URL of the W&B project associated with the run, if there is one.
|
1111
|
+
|
1112
|
+
Offline runs do not have a project URL.
|
1113
|
+
|
1114
|
+
Note: this method is deprecated and will be removed in a future release.
|
1115
|
+
Please use `run.project_url` instead.
|
1116
|
+
"""
|
1117
|
+
deprecate.deprecate(
|
1118
|
+
field_name=Deprecated.run__get_project_url,
|
1119
|
+
warning_message=(
|
1120
|
+
"The get_project_url method is deprecated and will be removed in a"
|
1121
|
+
" future release. Please use `run.project_url` instead."
|
1122
|
+
),
|
1123
|
+
)
|
1124
|
+
return self.project_url
|
1125
|
+
|
1126
|
+
@property
|
1127
|
+
@_log_to_run
|
1128
|
+
@_attach
|
1129
|
+
def project_url(self) -> str | None:
|
1130
|
+
"""URL of the W&B project associated with the run, if there is one.
|
1131
|
+
|
1132
|
+
Offline runs do not have a project URL.
|
1133
|
+
"""
|
1134
|
+
if self._settings._offline:
|
1135
|
+
wandb.termwarn("URL not available in offline run")
|
1136
|
+
return None
|
1137
|
+
return self._settings.project_url
|
1138
|
+
|
1139
|
+
@_raise_if_finished
|
1140
|
+
@_log_to_run
|
1141
|
+
@_attach
|
1064
1142
|
def log_code(
|
1065
1143
|
self,
|
1066
1144
|
root: str | None = ".",
|
@@ -1144,22 +1222,40 @@ class Run:
|
|
1144
1222
|
)
|
1145
1223
|
return None
|
1146
1224
|
|
1147
|
-
|
1225
|
+
artifact = self._log_artifact(art)
|
1148
1226
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1227
|
+
self._config.update(
|
1228
|
+
{"_wandb": {"code_path": artifact.name}},
|
1229
|
+
allow_val_change=True,
|
1230
|
+
)
|
1152
1231
|
|
1153
|
-
|
1154
|
-
"""
|
1155
|
-
if self._settings._offline:
|
1156
|
-
wandb.termwarn("URL not available in offline run")
|
1157
|
-
return None
|
1158
|
-
return self._settings.project_url
|
1232
|
+
return artifact
|
1159
1233
|
|
1160
1234
|
@_log_to_run
|
1161
1235
|
def get_sweep_url(self) -> str | None:
|
1162
|
-
"""
|
1236
|
+
"""The URL of the sweep associated with the run, if there is one.
|
1237
|
+
|
1238
|
+
Offline runs do not have a sweep URL.
|
1239
|
+
|
1240
|
+
Note: this method is deprecated and will be removed in a future release.
|
1241
|
+
Please use `run.sweep_url` instead.
|
1242
|
+
"""
|
1243
|
+
deprecate.deprecate(
|
1244
|
+
field_name=Deprecated.run__get_sweep_url,
|
1245
|
+
warning_message=(
|
1246
|
+
"The get_sweep_url method is deprecated and will be removed in a"
|
1247
|
+
" future release. Please use `run.sweep_url` instead."
|
1248
|
+
),
|
1249
|
+
)
|
1250
|
+
return self.sweep_url
|
1251
|
+
|
1252
|
+
@property
|
1253
|
+
@_attach
|
1254
|
+
def sweep_url(self) -> str | None:
|
1255
|
+
"""URL of the sweep associated with the run, if there is one.
|
1256
|
+
|
1257
|
+
Offline runs do not have a sweep URL.
|
1258
|
+
"""
|
1163
1259
|
if self._settings._offline:
|
1164
1260
|
wandb.termwarn("URL not available in offline run")
|
1165
1261
|
return None
|
@@ -1167,7 +1263,27 @@ class Run:
|
|
1167
1263
|
|
1168
1264
|
@_log_to_run
|
1169
1265
|
def get_url(self) -> str | None:
|
1170
|
-
"""
|
1266
|
+
"""URL of the W&B run, if there is one.
|
1267
|
+
|
1268
|
+
Offline runs do not have a URL.
|
1269
|
+
|
1270
|
+
Note: this method is deprecated and will be removed in a future release.
|
1271
|
+
Please use `run.url` instead.
|
1272
|
+
"""
|
1273
|
+
deprecate.deprecate(
|
1274
|
+
field_name=Deprecated.run__get_url,
|
1275
|
+
warning_message=(
|
1276
|
+
"The get_url method is deprecated and will be removed in a"
|
1277
|
+
" future release. Please use `run.url` instead."
|
1278
|
+
),
|
1279
|
+
)
|
1280
|
+
return self.url
|
1281
|
+
|
1282
|
+
@property
|
1283
|
+
@_log_to_run
|
1284
|
+
@_attach
|
1285
|
+
def url(self) -> str | None:
|
1286
|
+
"""The url for the W&B run, if there is one.
|
1171
1287
|
|
1172
1288
|
Offline runs will not have a url.
|
1173
1289
|
"""
|
@@ -1178,14 +1294,7 @@ class Run:
|
|
1178
1294
|
|
1179
1295
|
@property
|
1180
1296
|
@_log_to_run
|
1181
|
-
@
|
1182
|
-
def url(self) -> str | None:
|
1183
|
-
"""The W&B url associated with the run."""
|
1184
|
-
return self.get_url()
|
1185
|
-
|
1186
|
-
@property
|
1187
|
-
@_log_to_run
|
1188
|
-
@_run_decorator._attach
|
1297
|
+
@_attach
|
1189
1298
|
def entity(self) -> str:
|
1190
1299
|
"""The name of the W&B entity associated with the run.
|
1191
1300
|
|
@@ -1280,7 +1389,7 @@ class Run:
|
|
1280
1389
|
self._label_probe_lines(lines)
|
1281
1390
|
|
1282
1391
|
@_log_to_run
|
1283
|
-
@
|
1392
|
+
@_attach
|
1284
1393
|
def display(self, height: int = 420, hidden: bool = False) -> bool:
|
1285
1394
|
"""Display this run in jupyter."""
|
1286
1395
|
if self._settings.silent:
|
@@ -1300,7 +1409,7 @@ class Run:
|
|
1300
1409
|
return False
|
1301
1410
|
|
1302
1411
|
@_log_to_run
|
1303
|
-
@
|
1412
|
+
@_attach
|
1304
1413
|
def to_html(self, height: int = 420, hidden: bool = False) -> str:
|
1305
1414
|
"""Generate HTML containing an iframe displaying the current run."""
|
1306
1415
|
url = self._settings.run_url + "?jupyter=true"
|
@@ -1317,7 +1426,7 @@ class Run:
|
|
1317
1426
|
return {"text/html": self.to_html(hidden=True)}
|
1318
1427
|
|
1319
1428
|
@_log_to_run
|
1320
|
-
@
|
1429
|
+
@_raise_if_finished
|
1321
1430
|
def _config_callback(
|
1322
1431
|
self,
|
1323
1432
|
key: tuple[str, ...] | str | None = None,
|
@@ -1339,6 +1448,8 @@ class Run:
|
|
1339
1448
|
assert isinstance(val, dict)
|
1340
1449
|
public_api = self._public_api()
|
1341
1450
|
artifact = Artifact._from_id(val["id"], public_api.client)
|
1451
|
+
|
1452
|
+
assert artifact
|
1342
1453
|
return self.use_artifact(artifact, use_as=key)
|
1343
1454
|
elif _is_artifact_string(val):
|
1344
1455
|
# this will never fail, but is required to make mypy happy
|
@@ -1357,6 +1468,7 @@ class Run:
|
|
1357
1468
|
# in the future we'll need to support using artifacts from
|
1358
1469
|
# different instances of wandb.
|
1359
1470
|
|
1471
|
+
assert artifact
|
1360
1472
|
return self.use_artifact(artifact, use_as=key)
|
1361
1473
|
elif _is_artifact_object(val):
|
1362
1474
|
return self.use_artifact(val, use_as=key)
|
@@ -1369,7 +1481,7 @@ class Run:
|
|
1369
1481
|
self._config_callback(key=("_wandb", key), val=val)
|
1370
1482
|
|
1371
1483
|
@_log_to_run
|
1372
|
-
@
|
1484
|
+
@_raise_if_finished
|
1373
1485
|
def _summary_update_callback(self, summary_record: SummaryRecord) -> None:
|
1374
1486
|
with telemetry.context(run=self) as tel:
|
1375
1487
|
tel.feature.set_summary = True
|
@@ -1400,7 +1512,16 @@ class Run:
|
|
1400
1512
|
self._backend.interface._publish_metric(metric_record)
|
1401
1513
|
|
1402
1514
|
@_log_to_run
|
1403
|
-
def
|
1515
|
+
def _publish_file(self, fname: str) -> None:
|
1516
|
+
"""Mark a run file to be uploaded with the run.
|
1517
|
+
|
1518
|
+
This is a W&B-internal function: it can be used by other internal
|
1519
|
+
wandb code.
|
1520
|
+
|
1521
|
+
Args:
|
1522
|
+
fname: The path to the file in the run's files directory, relative
|
1523
|
+
to the run's files directory.
|
1524
|
+
"""
|
1404
1525
|
if not self._backend or not self._backend.interface:
|
1405
1526
|
return
|
1406
1527
|
files: FilesDict = dict(files=[(GlobStr(fname), "now")])
|
@@ -1502,7 +1623,7 @@ class Run:
|
|
1502
1623
|
self._backend.interface.publish_output(name, data)
|
1503
1624
|
|
1504
1625
|
@_log_to_run
|
1505
|
-
@
|
1626
|
+
@_raise_if_finished
|
1506
1627
|
def _console_raw_callback(self, name: str, data: str) -> None:
|
1507
1628
|
# logger.info("console callback: %s, %s", name, data)
|
1508
1629
|
|
@@ -1548,6 +1669,9 @@ class Run:
|
|
1548
1669
|
if run_obj.start_time:
|
1549
1670
|
self._start_time = run_obj.start_time.ToMicroseconds() / 1e6
|
1550
1671
|
|
1672
|
+
if run_obj.runtime:
|
1673
|
+
self._start_runtime = run_obj.runtime
|
1674
|
+
|
1551
1675
|
# Grab the config from resuming
|
1552
1676
|
if run_obj.config:
|
1553
1677
|
c_dict = config_util.dict_no_value_from_proto_list(run_obj.config.update)
|
@@ -1683,9 +1807,9 @@ class Run:
|
|
1683
1807
|
self._step += 1
|
1684
1808
|
|
1685
1809
|
@_log_to_run
|
1686
|
-
@
|
1687
|
-
@
|
1688
|
-
@
|
1810
|
+
@_noop_if_forked_with_no_service
|
1811
|
+
@_raise_if_finished
|
1812
|
+
@_attach
|
1689
1813
|
def log(
|
1690
1814
|
self,
|
1691
1815
|
data: dict[str, Any],
|
@@ -1931,10 +2055,11 @@ class Run:
|
|
1931
2055
|
|
1932
2056
|
if sync is not None:
|
1933
2057
|
deprecate.deprecate(
|
1934
|
-
field_name=
|
2058
|
+
field_name=Deprecated.run__log_sync,
|
1935
2059
|
warning_message=(
|
1936
2060
|
"`sync` argument is deprecated and does not affect the behaviour of `wandb.log`"
|
1937
2061
|
),
|
2062
|
+
run=self,
|
1938
2063
|
)
|
1939
2064
|
if self._settings._shared and step is not None:
|
1940
2065
|
wandb.termwarn(
|
@@ -1946,8 +2071,8 @@ class Run:
|
|
1946
2071
|
self._log(data=data, step=step, commit=commit)
|
1947
2072
|
|
1948
2073
|
@_log_to_run
|
1949
|
-
@
|
1950
|
-
@
|
2074
|
+
@_raise_if_finished
|
2075
|
+
@_attach
|
1951
2076
|
def save(
|
1952
2077
|
self,
|
1953
2078
|
glob_str: str | os.PathLike | None = None,
|
@@ -2004,11 +2129,12 @@ class Run:
|
|
2004
2129
|
if glob_str is None:
|
2005
2130
|
# noop for historical reasons, run.save() may be called in legacy code
|
2006
2131
|
deprecate.deprecate(
|
2007
|
-
field_name=
|
2132
|
+
field_name=Deprecated.run__save_no_args,
|
2008
2133
|
warning_message=(
|
2009
2134
|
"Calling wandb.run.save without any arguments is deprecated."
|
2010
2135
|
"Changes to attributes are automatically persisted."
|
2011
2136
|
),
|
2137
|
+
run=self,
|
2012
2138
|
)
|
2013
2139
|
return True
|
2014
2140
|
|
@@ -2132,7 +2258,7 @@ class Run:
|
|
2132
2258
|
return [str(f) for f in globbed_files]
|
2133
2259
|
|
2134
2260
|
@_log_to_run
|
2135
|
-
@
|
2261
|
+
@_attach
|
2136
2262
|
def restore(
|
2137
2263
|
self,
|
2138
2264
|
name: str,
|
@@ -2148,8 +2274,8 @@ class Run:
|
|
2148
2274
|
)
|
2149
2275
|
|
2150
2276
|
@_log_to_run
|
2151
|
-
@
|
2152
|
-
@
|
2277
|
+
@_noop_if_forked_with_no_service
|
2278
|
+
@_attach
|
2153
2279
|
def finish(
|
2154
2280
|
self,
|
2155
2281
|
exit_code: int | None = None,
|
@@ -2173,11 +2299,12 @@ class Run:
|
|
2173
2299
|
"""
|
2174
2300
|
if quiet is not None:
|
2175
2301
|
deprecate.deprecate(
|
2176
|
-
field_name=
|
2302
|
+
field_name=Deprecated.run__finish_quiet,
|
2177
2303
|
warning_message=(
|
2178
2304
|
"The `quiet` argument to `wandb.run.finish()` is deprecated, "
|
2179
2305
|
"use `wandb.Settings(quiet=...)` to set this instead."
|
2180
2306
|
),
|
2307
|
+
run=self,
|
2181
2308
|
)
|
2182
2309
|
return self._finish(exit_code)
|
2183
2310
|
|
@@ -2186,6 +2313,11 @@ class Run:
|
|
2186
2313
|
self,
|
2187
2314
|
exit_code: int | None = None,
|
2188
2315
|
) -> None:
|
2316
|
+
if self._is_finished:
|
2317
|
+
return
|
2318
|
+
|
2319
|
+
assert self._wl
|
2320
|
+
|
2189
2321
|
logger.info(f"finishing run {self._get_path()}")
|
2190
2322
|
with telemetry.context(run=self) as tel:
|
2191
2323
|
tel.feature.finish = True
|
@@ -2199,6 +2331,7 @@ class Run:
|
|
2199
2331
|
# Early-stage hooks may use methods that require _is_finished
|
2200
2332
|
# to be False, so we set this after running those hooks.
|
2201
2333
|
self._is_finished = True
|
2334
|
+
self._wl.remove_active_run(self)
|
2202
2335
|
|
2203
2336
|
try:
|
2204
2337
|
self._atexit_cleanup(exit_code=exit_code)
|
@@ -2214,31 +2347,32 @@ class Run:
|
|
2214
2347
|
#
|
2215
2348
|
# TODO: Why not do this in _atexit_cleanup()?
|
2216
2349
|
if self._settings.run_id:
|
2217
|
-
assert self._wl
|
2218
2350
|
service = self._wl.assert_service()
|
2219
2351
|
service.inform_finish(run_id=self._settings.run_id)
|
2220
2352
|
|
2221
2353
|
finally:
|
2222
|
-
|
2354
|
+
if wandb.run is self:
|
2355
|
+
module.unset_globals()
|
2223
2356
|
wandb._sentry.end_session()
|
2224
2357
|
|
2225
2358
|
@_log_to_run
|
2226
|
-
@
|
2227
|
-
@
|
2359
|
+
@_noop_if_forked_with_no_service
|
2360
|
+
@_attach
|
2228
2361
|
def join(self, exit_code: int | None = None) -> None:
|
2229
2362
|
"""Deprecated alias for `finish()` - use finish instead."""
|
2230
2363
|
if hasattr(self, "_telemetry_obj"):
|
2231
2364
|
deprecate.deprecate(
|
2232
|
-
field_name=
|
2365
|
+
field_name=Deprecated.run__join,
|
2233
2366
|
warning_message=(
|
2234
2367
|
"wandb.run.join() is deprecated, please use wandb.run.finish()."
|
2235
2368
|
),
|
2369
|
+
run=self,
|
2236
2370
|
)
|
2237
2371
|
self._finish(exit_code=exit_code)
|
2238
2372
|
|
2239
2373
|
@_log_to_run
|
2240
|
-
@
|
2241
|
-
@
|
2374
|
+
@_raise_if_finished
|
2375
|
+
@_attach
|
2242
2376
|
def status(
|
2243
2377
|
self,
|
2244
2378
|
) -> RunStatus:
|
@@ -2270,25 +2404,6 @@ class Run:
|
|
2270
2404
|
}
|
2271
2405
|
self._config_callback(val=config, key=("_wandb", "visualize", visualize_key))
|
2272
2406
|
|
2273
|
-
def _set_globals(self) -> None:
|
2274
|
-
module.set_global(
|
2275
|
-
run=self,
|
2276
|
-
config=self.config,
|
2277
|
-
log=self.log,
|
2278
|
-
summary=self.summary,
|
2279
|
-
save=self.save,
|
2280
|
-
use_artifact=self.use_artifact,
|
2281
|
-
log_artifact=self.log_artifact,
|
2282
|
-
define_metric=self.define_metric,
|
2283
|
-
alert=self.alert,
|
2284
|
-
watch=self.watch,
|
2285
|
-
unwatch=self.unwatch,
|
2286
|
-
mark_preempting=self.mark_preempting,
|
2287
|
-
log_model=self.log_model,
|
2288
|
-
use_model=self.use_model,
|
2289
|
-
link_model=self.link_model,
|
2290
|
-
)
|
2291
|
-
|
2292
2407
|
def _redirect(
|
2293
2408
|
self,
|
2294
2409
|
stdout_slave_fd: int | None,
|
@@ -2327,6 +2442,7 @@ class Run:
|
|
2327
2442
|
lambda data: self._console_callback("stdout", data),
|
2328
2443
|
self._output_writer.write, # type: ignore
|
2329
2444
|
],
|
2445
|
+
flush_periodically=(self._settings.mode == "online"),
|
2330
2446
|
)
|
2331
2447
|
err_redir = redirect.Redirect(
|
2332
2448
|
src="stderr",
|
@@ -2334,6 +2450,7 @@ class Run:
|
|
2334
2450
|
lambda data: self._console_callback("stderr", data),
|
2335
2451
|
self._output_writer.write, # type: ignore
|
2336
2452
|
],
|
2453
|
+
flush_periodically=(self._settings.mode == "online"),
|
2337
2454
|
)
|
2338
2455
|
if os.name == "nt":
|
2339
2456
|
|
@@ -2359,6 +2476,7 @@ class Run:
|
|
2359
2476
|
lambda data: self._console_callback("stdout", data),
|
2360
2477
|
self._output_writer.write, # type: ignore
|
2361
2478
|
],
|
2479
|
+
flush_periodically=(self._settings.mode == "online"),
|
2362
2480
|
)
|
2363
2481
|
err_redir = redirect.StreamWrapper(
|
2364
2482
|
src="stderr",
|
@@ -2366,6 +2484,7 @@ class Run:
|
|
2366
2484
|
lambda data: self._console_callback("stderr", data),
|
2367
2485
|
self._output_writer.write, # type: ignore
|
2368
2486
|
],
|
2487
|
+
flush_periodically=(self._settings.mode == "online"),
|
2369
2488
|
)
|
2370
2489
|
elif console == "wrap_raw":
|
2371
2490
|
logger.info("Wrapping output streams.")
|
@@ -2468,13 +2587,7 @@ class Run:
|
|
2468
2587
|
self._output_writer = None
|
2469
2588
|
|
2470
2589
|
def _on_start(self) -> None:
|
2471
|
-
|
2472
|
-
# (we want to do the set globals after attach)
|
2473
|
-
# TODO(console) However _console_start calls Redirect that uses `wandb.run` hence breaks
|
2474
|
-
# TODO(jupyter) However _header calls _header_run_info that uses wandb.jupyter that uses
|
2475
|
-
# `wandb.run` and hence breaks
|
2476
|
-
self._set_globals()
|
2477
|
-
self._header(settings=self._settings, printer=self._printer)
|
2590
|
+
self._header()
|
2478
2591
|
|
2479
2592
|
if self._settings.save_code and self._settings.code_dir is not None:
|
2480
2593
|
self.log_code(self._settings.code_dir)
|
@@ -2504,7 +2617,6 @@ class Run:
|
|
2504
2617
|
with telemetry.context(run=self) as tel:
|
2505
2618
|
tel.feature.attach = True
|
2506
2619
|
|
2507
|
-
self._set_globals()
|
2508
2620
|
self._is_attached = True
|
2509
2621
|
self._on_ready()
|
2510
2622
|
|
@@ -2536,6 +2648,9 @@ class Run:
|
|
2536
2648
|
|
2537
2649
|
def _on_ready(self) -> None:
|
2538
2650
|
"""Event triggered when run is ready for the user."""
|
2651
|
+
assert self._wl
|
2652
|
+
self._wl.add_active_run(self)
|
2653
|
+
|
2539
2654
|
self._register_telemetry_import_hooks()
|
2540
2655
|
|
2541
2656
|
# start reporting any telemetry changes
|
@@ -2627,6 +2742,7 @@ class Run:
|
|
2627
2742
|
docker_image_name=docker_image_name,
|
2628
2743
|
)
|
2629
2744
|
|
2745
|
+
assert job_artifact
|
2630
2746
|
artifact = self.log_artifact(job_artifact)
|
2631
2747
|
|
2632
2748
|
if not artifact:
|
@@ -2739,8 +2855,8 @@ class Run:
|
|
2739
2855
|
unregister_post_import_hook(module_name, run_id)
|
2740
2856
|
|
2741
2857
|
@_log_to_run
|
2742
|
-
@
|
2743
|
-
@
|
2858
|
+
@_raise_if_finished
|
2859
|
+
@_attach
|
2744
2860
|
def define_metric(
|
2745
2861
|
self,
|
2746
2862
|
name: str,
|
@@ -2779,14 +2895,14 @@ class Run:
|
|
2779
2895
|
"""
|
2780
2896
|
if summary and "copy" in summary:
|
2781
2897
|
deprecate.deprecate(
|
2782
|
-
|
2898
|
+
Deprecated.run__define_metric_copy,
|
2783
2899
|
"define_metric(summary='copy') is deprecated and will be removed.",
|
2784
2900
|
self,
|
2785
2901
|
)
|
2786
2902
|
|
2787
2903
|
if (summary and "best" in summary) or goal is not None:
|
2788
2904
|
deprecate.deprecate(
|
2789
|
-
|
2905
|
+
Deprecated.run__define_metric_best_goal,
|
2790
2906
|
"define_metric(summary='best', goal=...) is deprecated and will be removed. "
|
2791
2907
|
"Use define_metric(summary='min') or define_metric(summary='max') instead.",
|
2792
2908
|
self,
|
@@ -2881,7 +2997,7 @@ class Run:
|
|
2881
2997
|
return m
|
2882
2998
|
|
2883
2999
|
@_log_to_run
|
2884
|
-
@
|
3000
|
+
@_attach
|
2885
3001
|
def watch(
|
2886
3002
|
self,
|
2887
3003
|
models: torch.nn.Module | Sequence[torch.nn.Module],
|
@@ -2919,7 +3035,7 @@ class Run:
|
|
2919
3035
|
wandb.sdk._watch(self, models, criterion, log, log_freq, idx, log_graph)
|
2920
3036
|
|
2921
3037
|
@_log_to_run
|
2922
|
-
@
|
3038
|
+
@_attach
|
2923
3039
|
def unwatch(
|
2924
3040
|
self, models: torch.nn.Module | Sequence[torch.nn.Module] | None = None
|
2925
3041
|
) -> None:
|
@@ -2970,14 +3086,14 @@ class Run:
|
|
2970
3086
|
pass
|
2971
3087
|
|
2972
3088
|
@_log_to_run
|
2973
|
-
@
|
2974
|
-
@
|
3089
|
+
@_raise_if_finished
|
3090
|
+
@_attach
|
2975
3091
|
def link_artifact(
|
2976
3092
|
self,
|
2977
3093
|
artifact: Artifact,
|
2978
3094
|
target_path: str,
|
2979
3095
|
aliases: list[str] | None = None,
|
2980
|
-
) -> None:
|
3096
|
+
) -> Artifact | None:
|
2981
3097
|
"""Link the given artifact to a portfolio (a promoted collection of artifacts).
|
2982
3098
|
|
2983
3099
|
The linked artifact will be visible in the UI for the specified portfolio.
|
@@ -2991,7 +3107,7 @@ class Run:
|
|
2991
3107
|
The alias "latest" will always be applied to the latest version of an artifact that is linked.
|
2992
3108
|
|
2993
3109
|
Returns:
|
2994
|
-
None
|
3110
|
+
The linked artifact if linking was successful, otherwise None.
|
2995
3111
|
|
2996
3112
|
"""
|
2997
3113
|
portfolio, project, entity = wandb.util._parse_entity_project_item(target_path)
|
@@ -2999,7 +3115,7 @@ class Run:
|
|
2999
3115
|
aliases = []
|
3000
3116
|
|
3001
3117
|
if not self._backend or not self._backend.interface:
|
3002
|
-
return
|
3118
|
+
return None
|
3003
3119
|
|
3004
3120
|
if artifact.is_draft() and not artifact._is_draft_save_started():
|
3005
3121
|
artifact = self._log_artifact(artifact)
|
@@ -3013,12 +3129,13 @@ class Run:
|
|
3013
3129
|
|
3014
3130
|
organization = ""
|
3015
3131
|
if is_artifact_registry_project(project):
|
3016
|
-
organization = entity
|
3132
|
+
organization = entity or self.settings.organization or ""
|
3017
3133
|
# In a Registry linking, the entity is used to fetch the organization of the artifact
|
3018
3134
|
# therefore the source artifact's entity is passed to the backend
|
3019
3135
|
entity = artifact._source_entity
|
3136
|
+
project = project or self.project
|
3137
|
+
entity = entity or self.entity
|
3020
3138
|
handle = self._backend.interface.deliver_link_artifact(
|
3021
|
-
self,
|
3022
3139
|
artifact,
|
3023
3140
|
portfolio,
|
3024
3141
|
aliases,
|
@@ -3034,10 +3151,28 @@ class Run:
|
|
3034
3151
|
response = result.response.link_artifact_response
|
3035
3152
|
if response.error_message:
|
3036
3153
|
wandb.termerror(response.error_message)
|
3154
|
+
if response.version_index is None:
|
3155
|
+
wandb.termerror(
|
3156
|
+
"Error fetching the linked artifact's version index after linking"
|
3157
|
+
)
|
3158
|
+
return None
|
3159
|
+
|
3160
|
+
try:
|
3161
|
+
artifact_name = f"{entity}/{project}/{portfolio}:v{response.version_index}"
|
3162
|
+
if is_artifact_registry_project(project):
|
3163
|
+
if organization:
|
3164
|
+
artifact_name = f"{organization}/{project}/{portfolio}:v{response.version_index}"
|
3165
|
+
else:
|
3166
|
+
artifact_name = f"{project}/{portfolio}:v{response.version_index}"
|
3167
|
+
linked_artifact = self._public_api()._artifact(artifact_name)
|
3168
|
+
except Exception as e:
|
3169
|
+
wandb.termerror(f"Error fetching link artifact after linking: {e}")
|
3170
|
+
return None
|
3171
|
+
return linked_artifact
|
3037
3172
|
|
3038
3173
|
@_log_to_run
|
3039
|
-
@
|
3040
|
-
@
|
3174
|
+
@_raise_if_finished
|
3175
|
+
@_attach
|
3041
3176
|
def use_artifact(
|
3042
3177
|
self,
|
3043
3178
|
artifact_or_name: str | Artifact,
|
@@ -3156,8 +3291,8 @@ class Run:
|
|
3156
3291
|
return artifact
|
3157
3292
|
|
3158
3293
|
@_log_to_run
|
3159
|
-
@
|
3160
|
-
@
|
3294
|
+
@_raise_if_finished
|
3295
|
+
@_attach
|
3161
3296
|
def log_artifact(
|
3162
3297
|
self,
|
3163
3298
|
artifact_or_path: Artifact | StrPath,
|
@@ -3199,8 +3334,8 @@ class Run:
|
|
3199
3334
|
)
|
3200
3335
|
|
3201
3336
|
@_log_to_run
|
3202
|
-
@
|
3203
|
-
@
|
3337
|
+
@_raise_if_finished
|
3338
|
+
@_attach
|
3204
3339
|
def upsert_artifact(
|
3205
3340
|
self,
|
3206
3341
|
artifact_or_path: Artifact | str,
|
@@ -3254,8 +3389,8 @@ class Run:
|
|
3254
3389
|
)
|
3255
3390
|
|
3256
3391
|
@_log_to_run
|
3257
|
-
@
|
3258
|
-
@
|
3392
|
+
@_raise_if_finished
|
3393
|
+
@_attach
|
3259
3394
|
def finish_artifact(
|
3260
3395
|
self,
|
3261
3396
|
artifact_or_path: Artifact | str,
|
@@ -3456,8 +3591,8 @@ class Run:
|
|
3456
3591
|
return artifact, _resolve_aliases(aliases)
|
3457
3592
|
|
3458
3593
|
@_log_to_run
|
3459
|
-
@
|
3460
|
-
@
|
3594
|
+
@_raise_if_finished
|
3595
|
+
@_attach
|
3461
3596
|
def log_model(
|
3462
3597
|
self,
|
3463
3598
|
path: StrPath,
|
@@ -3508,8 +3643,8 @@ class Run:
|
|
3508
3643
|
)
|
3509
3644
|
|
3510
3645
|
@_log_to_run
|
3511
|
-
@
|
3512
|
-
@
|
3646
|
+
@_raise_if_finished
|
3647
|
+
@_attach
|
3513
3648
|
def use_model(self, name: str) -> FilePathStr:
|
3514
3649
|
"""Download the files logged in a model artifact 'name'.
|
3515
3650
|
|
@@ -3549,6 +3684,10 @@ class Run:
|
|
3549
3684
|
Returns:
|
3550
3685
|
path: (str) path to downloaded model artifact file(s).
|
3551
3686
|
"""
|
3687
|
+
if self._settings._offline:
|
3688
|
+
# Downloading artifacts is not supported when offline.
|
3689
|
+
raise RuntimeError("`use_model` not supported in offline mode.")
|
3690
|
+
|
3552
3691
|
artifact = self.use_artifact(artifact_or_name=name)
|
3553
3692
|
if "model" not in str(artifact.type.lower()):
|
3554
3693
|
raise AssertionError(
|
@@ -3566,15 +3705,15 @@ class Run:
|
|
3566
3705
|
return path
|
3567
3706
|
|
3568
3707
|
@_log_to_run
|
3569
|
-
@
|
3570
|
-
@
|
3708
|
+
@_raise_if_finished
|
3709
|
+
@_attach
|
3571
3710
|
def link_model(
|
3572
3711
|
self,
|
3573
3712
|
path: StrPath,
|
3574
3713
|
registered_model_name: str,
|
3575
3714
|
name: str | None = None,
|
3576
3715
|
aliases: list[str] | None = None,
|
3577
|
-
) -> None:
|
3716
|
+
) -> Artifact | None:
|
3578
3717
|
"""Log a model artifact version and link it to a registered model in the model registry.
|
3579
3718
|
|
3580
3719
|
The linked model version will be visible in the UI for the specified registered model.
|
@@ -3636,7 +3775,7 @@ class Run:
|
|
3636
3775
|
ValueError: if name has invalid special characters
|
3637
3776
|
|
3638
3777
|
Returns:
|
3639
|
-
None
|
3778
|
+
The linked artifact if linking was successful, otherwise None.
|
3640
3779
|
"""
|
3641
3780
|
name_parts = registered_model_name.split("/")
|
3642
3781
|
if len(name_parts) != 1:
|
@@ -3665,11 +3804,13 @@ class Run:
|
|
3665
3804
|
artifact = self._log_artifact(
|
3666
3805
|
artifact_or_path=path, name=name, type="model"
|
3667
3806
|
)
|
3668
|
-
self.link_artifact(
|
3807
|
+
return self.link_artifact(
|
3808
|
+
artifact=artifact, target_path=target_path, aliases=aliases
|
3809
|
+
)
|
3669
3810
|
|
3670
3811
|
@_log_to_run
|
3671
|
-
@
|
3672
|
-
@
|
3812
|
+
@_raise_if_finished
|
3813
|
+
@_attach
|
3673
3814
|
def alert(
|
3674
3815
|
self,
|
3675
3816
|
title: str,
|
@@ -3720,8 +3861,8 @@ class Run:
|
|
3720
3861
|
return not exception_raised
|
3721
3862
|
|
3722
3863
|
@_log_to_run
|
3723
|
-
@
|
3724
|
-
@
|
3864
|
+
@_raise_if_finished
|
3865
|
+
@_attach
|
3725
3866
|
def mark_preempting(self) -> None:
|
3726
3867
|
"""Mark this run as preempting.
|
3727
3868
|
|
@@ -3732,8 +3873,8 @@ class Run:
|
|
3732
3873
|
|
3733
3874
|
@property
|
3734
3875
|
@_log_to_run
|
3735
|
-
@
|
3736
|
-
@
|
3876
|
+
@_raise_if_finished
|
3877
|
+
@_attach
|
3737
3878
|
def _system_metrics(self) -> dict[str, list[tuple[datetime, float]]]:
|
3738
3879
|
"""Returns a dictionary of system metrics.
|
3739
3880
|
|
@@ -3781,8 +3922,8 @@ class Run:
|
|
3781
3922
|
|
3782
3923
|
@property
|
3783
3924
|
@_log_to_run
|
3784
|
-
@
|
3785
|
-
@
|
3925
|
+
@_attach
|
3926
|
+
@_raise_if_finished
|
3786
3927
|
def _metadata(self) -> Metadata | None:
|
3787
3928
|
"""The metadata associated with this run.
|
3788
3929
|
|
@@ -3820,8 +3961,8 @@ class Run:
|
|
3820
3961
|
return None
|
3821
3962
|
|
3822
3963
|
@_log_to_run
|
3823
|
-
@
|
3824
|
-
@
|
3964
|
+
@_raise_if_finished
|
3965
|
+
@_attach
|
3825
3966
|
def _metadata_callback(
|
3826
3967
|
self,
|
3827
3968
|
metadata: MetadataRequest,
|
@@ -3841,59 +3982,41 @@ class Run:
|
|
3841
3982
|
# ------------------------------------------------------------------------------
|
3842
3983
|
# HEADER
|
3843
3984
|
# ------------------------------------------------------------------------------
|
3844
|
-
|
3845
|
-
|
3846
|
-
|
3847
|
-
|
3848
|
-
*,
|
3849
|
-
settings: Settings,
|
3850
|
-
printer: printer.Printer,
|
3851
|
-
) -> None:
|
3852
|
-
Run._header_wandb_version_info(settings=settings, printer=printer)
|
3853
|
-
Run._header_sync_info(settings=settings, printer=printer)
|
3854
|
-
Run._header_run_info(settings=settings, printer=printer)
|
3985
|
+
def _header(self) -> None:
|
3986
|
+
self._header_wandb_version_info()
|
3987
|
+
self._header_sync_info()
|
3988
|
+
self._header_run_info()
|
3855
3989
|
|
3856
|
-
|
3857
|
-
|
3858
|
-
*,
|
3859
|
-
settings: Settings,
|
3860
|
-
printer: printer.Printer,
|
3861
|
-
) -> None:
|
3862
|
-
if settings.quiet or settings.silent:
|
3990
|
+
def _header_wandb_version_info(self) -> None:
|
3991
|
+
if self._settings.quiet or self._settings.silent:
|
3863
3992
|
return
|
3864
3993
|
|
3865
3994
|
# TODO: add this to a higher verbosity level
|
3866
|
-
|
3995
|
+
self._printer.display(f"Tracking run with wandb version {wandb.__version__}")
|
3867
3996
|
|
3868
|
-
|
3869
|
-
|
3870
|
-
|
3871
|
-
settings: Settings,
|
3872
|
-
printer: printer.Printer,
|
3873
|
-
) -> None:
|
3874
|
-
if settings._offline:
|
3875
|
-
printer.display(
|
3997
|
+
def _header_sync_info(self) -> None:
|
3998
|
+
if self._settings._offline:
|
3999
|
+
self._printer.display(
|
3876
4000
|
[
|
3877
|
-
f"W&B syncing is set to {
|
3878
|
-
f"Run {
|
3879
|
-
"
|
4001
|
+
f"W&B syncing is set to {self._printer.code('`offline`')}"
|
4002
|
+
f" in this directory. Run {self._printer.code('`wandb online`')}"
|
4003
|
+
f" or set {self._printer.code('WANDB_MODE=online')}"
|
4004
|
+
" to enable cloud syncing.",
|
3880
4005
|
]
|
3881
4006
|
)
|
3882
4007
|
else:
|
3883
|
-
|
3884
|
-
|
4008
|
+
sync_dir = self._settings.sync_dir
|
4009
|
+
info = [f"Run data is saved locally in {self._printer.files(sync_dir)}"]
|
4010
|
+
if not self._printer.supports_html:
|
3885
4011
|
info.append(
|
3886
|
-
f"Run {
|
4012
|
+
f"Run {self._printer.code('`wandb offline`')} to turn off syncing."
|
3887
4013
|
)
|
3888
|
-
if not
|
3889
|
-
|
4014
|
+
if not self._settings.quiet and not self._settings.silent:
|
4015
|
+
self._printer.display(info)
|
4016
|
+
|
4017
|
+
def _header_run_info(self) -> None:
|
4018
|
+
settings, printer = self._settings, self._printer
|
3890
4019
|
|
3891
|
-
@staticmethod
|
3892
|
-
def _header_run_info(
|
3893
|
-
*,
|
3894
|
-
settings: Settings,
|
3895
|
-
printer: printer.Printer,
|
3896
|
-
) -> None:
|
3897
4020
|
if settings._offline or settings.silent:
|
3898
4021
|
return
|
3899
4022
|
|
@@ -3911,12 +4034,13 @@ class Run:
|
|
3911
4034
|
return
|
3912
4035
|
|
3913
4036
|
if printer.supports_html:
|
3914
|
-
|
4037
|
+
import wandb.jupyter
|
4038
|
+
|
4039
|
+
if not wandb.jupyter.display_if_magic_is_used(self):
|
3915
4040
|
run_line = f"<strong>{printer.link(run_url, run_name)}</strong>"
|
3916
4041
|
project_line, sweep_line = "", ""
|
3917
4042
|
|
3918
|
-
|
3919
|
-
if not wandb.jupyter.quiet(): # type: ignore
|
4043
|
+
if not settings.quiet:
|
3920
4044
|
doc_html = printer.link(url_registry.url("developer-guide"), "docs")
|
3921
4045
|
|
3922
4046
|
project_html = printer.link(project_url, "Weights & Biases")
|