wandb 0.19.8__py3-none-any.whl → 0.19.9__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.
Files changed (64) hide show
  1. wandb/__init__.py +5 -1
  2. wandb/__init__.pyi +12 -8
  3. wandb/_pydantic/__init__.py +23 -0
  4. wandb/_pydantic/base.py +113 -0
  5. wandb/_pydantic/v1_compat.py +262 -0
  6. wandb/apis/paginator.py +82 -38
  7. wandb/apis/public/api.py +10 -64
  8. wandb/apis/public/artifacts.py +73 -17
  9. wandb/apis/public/files.py +2 -2
  10. wandb/apis/public/projects.py +3 -2
  11. wandb/apis/public/reports.py +2 -2
  12. wandb/apis/public/runs.py +19 -11
  13. wandb/bin/gpu_stats +0 -0
  14. wandb/integration/metaflow/metaflow.py +19 -17
  15. wandb/integration/sacred/__init__.py +1 -1
  16. wandb/jupyter.py +18 -15
  17. wandb/proto/v3/wandb_internal_pb2.py +7 -3
  18. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  19. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  20. wandb/proto/v4/wandb_internal_pb2.py +3 -3
  21. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  22. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  23. wandb/proto/v5/wandb_internal_pb2.py +3 -3
  24. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  25. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  26. wandb/proto/wandb_deprecated.py +2 -0
  27. wandb/sdk/artifacts/_graphql_fragments.py +18 -20
  28. wandb/sdk/artifacts/_validators.py +1 -0
  29. wandb/sdk/artifacts/artifact.py +70 -36
  30. wandb/sdk/artifacts/artifact_saver.py +16 -2
  31. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +23 -2
  32. wandb/sdk/data_types/audio.py +1 -3
  33. wandb/sdk/data_types/base_types/media.py +11 -4
  34. wandb/sdk/data_types/image.py +44 -25
  35. wandb/sdk/data_types/molecule.py +1 -5
  36. wandb/sdk/data_types/object_3d.py +2 -1
  37. wandb/sdk/data_types/saved_model.py +7 -9
  38. wandb/sdk/data_types/video.py +1 -4
  39. wandb/{apis/public → sdk/internal}/_generated/__init__.py +0 -6
  40. wandb/sdk/internal/_generated/base.py +226 -0
  41. wandb/{apis/public → sdk/internal}/_generated/server_features_query.py +3 -3
  42. wandb/{apis/public → sdk/internal}/_generated/typing_compat.py +1 -1
  43. wandb/sdk/internal/internal_api.py +138 -47
  44. wandb/sdk/internal/sender.py +2 -0
  45. wandb/sdk/internal/sender_config.py +8 -11
  46. wandb/sdk/internal/settings_static.py +24 -2
  47. wandb/sdk/lib/apikey.py +15 -16
  48. wandb/sdk/lib/run_moment.py +4 -6
  49. wandb/sdk/lib/wb_logging.py +161 -0
  50. wandb/sdk/wandb_config.py +44 -43
  51. wandb/sdk/wandb_init.py +141 -79
  52. wandb/sdk/wandb_metadata.py +107 -91
  53. wandb/sdk/wandb_run.py +152 -44
  54. wandb/sdk/wandb_settings.py +403 -201
  55. wandb/sdk/wandb_setup.py +3 -1
  56. {wandb-0.19.8.dist-info → wandb-0.19.9.dist-info}/METADATA +3 -3
  57. {wandb-0.19.8.dist-info → wandb-0.19.9.dist-info}/RECORD +63 -59
  58. wandb/apis/public/_generated/base.py +0 -128
  59. /wandb/{apis/public → sdk/internal}/_generated/enums.py +0 -0
  60. /wandb/{apis/public → sdk/internal}/_generated/input_types.py +0 -0
  61. /wandb/{apis/public → sdk/internal}/_generated/operations.py +0 -0
  62. {wandb-0.19.8.dist-info → wandb-0.19.9.dist-info}/WHEEL +0 -0
  63. {wandb-0.19.8.dist-info → wandb-0.19.9.dist-info}/entry_points.txt +0 -0
  64. {wandb-0.19.8.dist-info → wandb-0.19.9.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_init.py CHANGED
@@ -10,6 +10,7 @@ For more on using `wandb.init()`, including code snippets, check out our
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import contextlib
13
14
  import copy
14
15
  import dataclasses
15
16
  import json
@@ -20,7 +21,7 @@ import platform
20
21
  import sys
21
22
  import tempfile
22
23
  import time
23
- from typing import Any, Literal, Sequence
24
+ from typing import Any, Iterator, Literal, Sequence
24
25
 
25
26
  if sys.version_info >= (3, 11):
26
27
  from typing import Self
@@ -29,12 +30,13 @@ else:
29
30
 
30
31
  import wandb
31
32
  import wandb.env
32
- from wandb import trigger
33
+ from wandb import env, trigger
33
34
  from wandb.errors import CommError, Error, UsageError
34
35
  from wandb.errors.links import url_registry
35
36
  from wandb.errors.util import ProtobufErrorHandler
36
37
  from wandb.integration import sagemaker
37
- from wandb.sdk.lib import progress, runid
38
+ from wandb.sdk.lib import ipython as wb_ipython
39
+ from wandb.sdk.lib import progress, runid, wb_logging
38
40
  from wandb.sdk.lib.paths import StrPath
39
41
  from wandb.util import _is_artifact_representation
40
42
 
@@ -349,6 +351,9 @@ class _WandbInit:
349
351
 
350
352
  After this, `settings.run_id` is guaranteed to be set.
351
353
 
354
+ If a `resume_from` is provided and `run_id` is not set, initialize
355
+ `run_id` with the `resume_from` run's `run_id`.
356
+
352
357
  Args:
353
358
  settings: The run's settings derived from the environment
354
359
  and explicit values passed to `wandb.init()`.
@@ -375,7 +380,12 @@ class _WandbInit:
375
380
  # If no run ID was inferred, explicitly set, or loaded from an
376
381
  # auto-resume file, then we generate a new ID.
377
382
  if settings.run_id is None:
378
- settings.run_id = runid.generate_id()
383
+ # If resume_from is provided and run_id is not already set,
384
+ # initialize run_id with the value from resume_from.
385
+ if settings.resume_from:
386
+ settings.run_id = settings.resume_from.run
387
+ else:
388
+ settings.run_id = runid.generate_id()
379
389
 
380
390
  if resume_path:
381
391
  self._save_autoresume_run_id(
@@ -401,14 +411,17 @@ class _WandbInit:
401
411
  Returns:
402
412
  Initial values for the run's config.
403
413
  """
404
- # TODO: remove this once officially deprecated
405
414
  if config_exclude_keys:
406
- self.deprecated_features_used["config_exclude_keys"] = (
407
- "Use `config=wandb.helper.parse_config(config_object, exclude=('key',))` instead."
415
+ self.deprecated_features_used["init__config_exclude_keys"] = (
416
+ "config_exclude_keys is deprecated. Use"
417
+ " `config=wandb.helper.parse_config(config_object,"
418
+ " exclude=('key',))` instead."
408
419
  )
409
420
  if config_include_keys:
410
- self.deprecated_features_used["config_include_keys"] = (
411
- "Use `config=wandb.helper.parse_config(config_object, include=('key',))` instead."
421
+ self.deprecated_features_used["init__config_include_keys"] = (
422
+ "config_include_keys is deprecated. Use"
423
+ " `config=wandb.helper.parse_config(config_object,"
424
+ " include=('key',))` instead."
412
425
  )
413
426
  config = parse_config(
414
427
  config or dict(),
@@ -498,37 +511,6 @@ class _WandbInit:
498
511
  else:
499
512
  config_target.setdefault(k, v)
500
513
 
501
- def _create_logger(self, log_fname: str) -> logging.Logger:
502
- """Returns a logger configured to write to a file.
503
-
504
- This adds a run_id to the log, in case of multiple processes on the same
505
- machine. Currently, there is no way to disable logging after it's
506
- enabled.
507
- """
508
- handler = logging.FileHandler(log_fname)
509
- handler.setLevel(logging.INFO)
510
-
511
- formatter = logging.Formatter(
512
- "%(asctime)s %(levelname)-7s %(threadName)-10s:%(process)d "
513
- "[%(filename)s:%(funcName)s():%(lineno)s] %(message)s"
514
- )
515
-
516
- handler.setFormatter(formatter)
517
-
518
- logger = logging.getLogger("wandb")
519
- logger.propagate = False
520
- logger.addHandler(handler)
521
- # TODO: make me configurable
522
- logger.setLevel(logging.DEBUG)
523
- self._teardown_hooks.append(
524
- TeardownHook(
525
- lambda: (handler.close(), logger.removeHandler(handler)), # type: ignore
526
- TeardownStage.LATE,
527
- )
528
- )
529
-
530
- return logger
531
-
532
514
  def _safe_symlink(
533
515
  self, base: str, target: str, name: str, delete: bool = False
534
516
  ) -> None:
@@ -615,8 +597,14 @@ class _WandbInit:
615
597
 
616
598
  ipython.display_pub.publish = publish
617
599
 
618
- def setup_run_log_directory(self, settings: Settings) -> None:
619
- """Set up logging from settings."""
600
+ @contextlib.contextmanager
601
+ def setup_run_log_directory(self, settings: Settings) -> Iterator[None]:
602
+ """Set up the run's log directory.
603
+
604
+ This is a context manager that closes and unregisters the log handler
605
+ in case of an uncaught exception, so that future logged messages do not
606
+ modify this run's log file.
607
+ """
620
608
  filesystem.mkdir_exists_ok(os.path.dirname(settings.log_user))
621
609
  filesystem.mkdir_exists_ok(os.path.dirname(settings.log_internal))
622
610
  filesystem.mkdir_exists_ok(os.path.dirname(settings.sync_file))
@@ -643,9 +631,41 @@ class _WandbInit:
643
631
  delete=True,
644
632
  )
645
633
 
646
- self._wl._early_logger_flush(self._create_logger(settings.log_user))
647
- self._logger.info(f"Logging user logs to {settings.log_user}")
648
- self._logger.info(f"Logging internal logs to {settings.log_internal}")
634
+ assert settings.run_id
635
+ handler = wb_logging.add_file_handler(
636
+ settings.run_id,
637
+ pathlib.Path(settings.log_user),
638
+ )
639
+
640
+ if env.is_debug():
641
+ handler.setLevel(logging.DEBUG)
642
+
643
+ disposed = False
644
+
645
+ def dispose_handler() -> None:
646
+ nonlocal disposed
647
+
648
+ if not disposed:
649
+ disposed = True
650
+ logging.getLogger("wandb").removeHandler(handler)
651
+ handler.close()
652
+
653
+ try:
654
+ self._teardown_hooks.append(
655
+ TeardownHook(
656
+ call=dispose_handler,
657
+ stage=TeardownStage.LATE,
658
+ )
659
+ )
660
+
661
+ self._wl._early_logger_flush(logging.getLogger("wandb"))
662
+ self._logger.info(f"Logging user logs to {settings.log_user}")
663
+ self._logger.info(f"Logging internal logs to {settings.log_internal}")
664
+
665
+ yield
666
+ except Exception:
667
+ dispose_handler()
668
+ raise
649
669
 
650
670
  def make_disabled_run(self, config: _ConfigParts) -> Run:
651
671
  """Returns a Run-like object where all methods are no-ops.
@@ -769,8 +789,13 @@ class _WandbInit:
769
789
  )
770
790
 
771
791
  if wandb.run is not None and os.getpid() == wandb.run._init_pid:
772
- if settings.reinit:
773
- self._logger.info(f"finishing previous run: {wandb.run.id}")
792
+ if (
793
+ settings.reinit in (True, "finish_previous")
794
+ # calling wandb.init() in notebooks finishes previous runs
795
+ # by default for user convenience.
796
+ or (settings.reinit == "default" and wb_ipython.in_notebook())
797
+ ):
798
+ self._logger.info("finishing previous run: %s", wandb.run.id)
774
799
  wandb.run.finish()
775
800
  else:
776
801
  self._logger.info("wandb.init() called while a run is active")
@@ -881,10 +906,9 @@ class _WandbInit:
881
906
  run._label_probe_main()
882
907
 
883
908
  for deprecated_feature, msg in self.deprecated_features_used.items():
884
- warning_message = f"`{deprecated_feature}` is deprecated. {msg}"
885
909
  deprecate(
886
- field_name=getattr(Deprecated, "init__" + deprecated_feature),
887
- warning_message=warning_message,
910
+ field_name=getattr(Deprecated, deprecated_feature),
911
+ warning_message=msg,
888
912
  run=run,
889
913
  )
890
914
 
@@ -1128,6 +1152,29 @@ def _monkeypatch_tensorboard() -> None:
1128
1152
  tb_module.patch()
1129
1153
 
1130
1154
 
1155
+ def try_create_root_dir(settings: Settings) -> None:
1156
+ """Try to create the root directory specified in settings.
1157
+
1158
+ If creation fails due to permissions or other errors,
1159
+ falls back to using the system temp directory.
1160
+
1161
+ Args:
1162
+ settings: The runs settings containing root_dir configuration.
1163
+ This function may update the root_dir to a temporary directory
1164
+ if the parent directory is not writable.
1165
+ """
1166
+ try:
1167
+ if not os.path.exists(settings.root_dir):
1168
+ os.makedirs(settings.root_dir, exist_ok=True)
1169
+ except OSError:
1170
+ temp_dir = tempfile.gettempdir()
1171
+ wandb.termwarn(
1172
+ f"Path {settings.root_dir} wasn't writable, using system temp directory {temp_dir}.",
1173
+ repeat=False,
1174
+ )
1175
+ settings.root_dir = temp_dir
1176
+
1177
+
1131
1178
  def init( # noqa: C901
1132
1179
  entity: str | None = None,
1133
1180
  project: str | None = None,
@@ -1145,7 +1192,15 @@ def init( # noqa: C901
1145
1192
  mode: Literal["online", "offline", "disabled"] | None = None,
1146
1193
  force: bool | None = None,
1147
1194
  anonymous: Literal["never", "allow", "must"] | None = None,
1148
- reinit: bool | None = None,
1195
+ reinit: (
1196
+ bool
1197
+ | Literal[
1198
+ None,
1199
+ "default",
1200
+ "return_previous",
1201
+ "finish_previous",
1202
+ ]
1203
+ ) = None,
1149
1204
  resume: bool | Literal["allow", "never", "must", "auto"] | None = None,
1150
1205
  resume_from: str | None = None,
1151
1206
  fork_from: str | None = None,
@@ -1293,12 +1348,8 @@ def init( # noqa: C901
1293
1348
  to view the charts and data in the UI.
1294
1349
  - `"must"`: Forces the run to be logged to an anonymous account, even
1295
1350
  if the user is logged in.
1296
- reinit: Determines if multiple `wandb.init()` calls can start new runs
1297
- within the same process. By default (`False`), if an active run
1298
- exists, calling `wandb.init()` returns the existing run instead of
1299
- creating a new one. When `reinit=True`, the active run is finished
1300
- before a new run is initialized. In notebook environments, runs are
1301
- reinitialized by default unless `reinit` is explicitly set to `False`.
1351
+ reinit: Shorthand for the "reinit" setting. Determines the behavior of
1352
+ `wandb.init()` when a run is active.
1302
1353
  resume: Controls the behavior when resuming a run with the specified `id`.
1303
1354
  Available options are:
1304
1355
  - `"allow"`: If a run with the specified `id` exists, it will resume
@@ -1427,6 +1478,12 @@ def init( # noqa: C901
1427
1478
  wi.maybe_login(init_settings)
1428
1479
  run_settings = wi.make_run_settings(init_settings)
1429
1480
 
1481
+ if isinstance(run_settings.reinit, bool):
1482
+ wi.deprecated_features_used["run__reinit_bool"] = (
1483
+ "Using a boolean value for 'reinit' is deprecated."
1484
+ " Use 'return_previous' or 'finish_previous' instead."
1485
+ )
1486
+
1430
1487
  if run_settings.run_id is not None:
1431
1488
  init_telemetry.feature.set_init_id = True
1432
1489
  if run_settings.run_name is not None:
@@ -1438,34 +1495,39 @@ def init( # noqa: C901
1438
1495
 
1439
1496
  wi.set_run_id(run_settings)
1440
1497
 
1441
- run_config = wi.make_run_config(
1442
- settings=run_settings,
1443
- config=config,
1444
- config_exclude_keys=config_exclude_keys,
1445
- config_include_keys=config_include_keys,
1446
- )
1498
+ with contextlib.ExitStack() as exit_stack:
1499
+ exit_stack.enter_context(wb_logging.log_to_run(run_settings.run_id))
1500
+
1501
+ run_config = wi.make_run_config(
1502
+ settings=run_settings,
1503
+ config=config,
1504
+ config_exclude_keys=config_exclude_keys,
1505
+ config_include_keys=config_include_keys,
1506
+ )
1507
+
1508
+ if run_settings._noop:
1509
+ return wi.make_disabled_run(run_config)
1447
1510
 
1448
- if run_settings._noop:
1449
- return wi.make_disabled_run(run_config)
1511
+ try_create_root_dir(run_settings)
1512
+ exit_stack.enter_context(wi.setup_run_log_directory(run_settings))
1450
1513
 
1451
- wi.setup_run_log_directory(run_settings)
1452
- if run_settings._jupyter:
1453
- wi.monkeypatch_ipython(run_settings)
1514
+ if run_settings._jupyter:
1515
+ wi.monkeypatch_ipython(run_settings)
1454
1516
 
1455
- if monitor_gym:
1456
- _monkeypatch_openai_gym()
1517
+ if monitor_gym:
1518
+ _monkeypatch_openai_gym()
1457
1519
 
1458
- if wandb.patched["tensorboard"]:
1459
- # NOTE: The user may have called the patch function directly.
1460
- init_telemetry.feature.tensorboard_patch = True
1461
- if run_settings.sync_tensorboard:
1462
- _monkeypatch_tensorboard()
1463
- init_telemetry.feature.tensorboard_sync = True
1520
+ if wandb.patched["tensorboard"]:
1521
+ # NOTE: The user may have called the patch function directly.
1522
+ init_telemetry.feature.tensorboard_patch = True
1523
+ if run_settings.sync_tensorboard:
1524
+ _monkeypatch_tensorboard()
1525
+ init_telemetry.feature.tensorboard_sync = True
1464
1526
 
1465
- if run_settings.x_server_side_derived_summary:
1466
- init_telemetry.feature.server_side_derived_summary = True
1527
+ if run_settings.x_server_side_derived_summary:
1528
+ init_telemetry.feature.server_side_derived_summary = True
1467
1529
 
1468
- return wi.init(run_settings, run_config)
1530
+ return wi.init(run_settings, run_config)
1469
1531
 
1470
1532
  except KeyboardInterrupt as e:
1471
1533
  if wl: