wandb 0.19.1rc1__py3-none-win32.whl → 0.19.3__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.
Files changed (80) hide show
  1. wandb/__init__.py +1 -7
  2. wandb/__init__.pyi +15 -7
  3. wandb/agents/pyagent.py +1 -1
  4. wandb/apis/importers/wandb.py +1 -1
  5. wandb/apis/public/files.py +1 -1
  6. wandb/apis/public/jobs.py +1 -1
  7. wandb/apis/public/runs.py +2 -7
  8. wandb/apis/reports/v1/__init__.py +1 -1
  9. wandb/apis/reports/v2/__init__.py +1 -1
  10. wandb/apis/workspaces/__init__.py +1 -1
  11. wandb/bin/gpu_stats.exe +0 -0
  12. wandb/bin/wandb-core +0 -0
  13. wandb/cli/beta.py +7 -4
  14. wandb/cli/cli.py +5 -7
  15. wandb/docker/__init__.py +4 -4
  16. wandb/integration/fastai/__init__.py +4 -6
  17. wandb/integration/keras/keras.py +5 -3
  18. wandb/integration/metaflow/metaflow.py +14 -16
  19. wandb/integration/prodigy/prodigy.py +3 -11
  20. wandb/integration/sagemaker/__init__.py +5 -3
  21. wandb/integration/sagemaker/config.py +17 -8
  22. wandb/integration/sagemaker/files.py +0 -1
  23. wandb/integration/sagemaker/resources.py +47 -18
  24. wandb/integration/torch/wandb_torch.py +1 -1
  25. wandb/proto/v3/wandb_internal_pb2.py +273 -235
  26. wandb/proto/v4/wandb_internal_pb2.py +222 -214
  27. wandb/proto/v5/wandb_internal_pb2.py +222 -214
  28. wandb/sdk/artifacts/artifact.py +3 -9
  29. wandb/sdk/backend/backend.py +1 -1
  30. wandb/sdk/data_types/base_types/wb_value.py +1 -1
  31. wandb/sdk/data_types/graph.py +2 -2
  32. wandb/sdk/data_types/saved_model.py +1 -1
  33. wandb/sdk/data_types/video.py +1 -1
  34. wandb/sdk/interface/interface.py +25 -25
  35. wandb/sdk/interface/interface_shared.py +21 -5
  36. wandb/sdk/internal/handler.py +19 -1
  37. wandb/sdk/internal/internal.py +1 -1
  38. wandb/sdk/internal/internal_api.py +4 -5
  39. wandb/sdk/internal/sample.py +2 -2
  40. wandb/sdk/internal/sender.py +1 -2
  41. wandb/sdk/internal/settings_static.py +3 -1
  42. wandb/sdk/internal/system/assets/disk.py +4 -4
  43. wandb/sdk/internal/system/assets/gpu.py +1 -1
  44. wandb/sdk/internal/system/assets/memory.py +1 -1
  45. wandb/sdk/internal/system/system_info.py +1 -1
  46. wandb/sdk/internal/system/system_monitor.py +3 -1
  47. wandb/sdk/internal/tb_watcher.py +1 -1
  48. wandb/sdk/launch/_project_spec.py +3 -3
  49. wandb/sdk/launch/builder/abstract.py +1 -1
  50. wandb/sdk/lib/apikey.py +2 -3
  51. wandb/sdk/lib/fsm.py +1 -1
  52. wandb/sdk/lib/gitlib.py +1 -1
  53. wandb/sdk/lib/gql_request.py +1 -1
  54. wandb/sdk/lib/interrupt.py +37 -0
  55. wandb/sdk/lib/lazyloader.py +1 -1
  56. wandb/sdk/lib/progress.py +7 -1
  57. wandb/sdk/lib/service_connection.py +1 -1
  58. wandb/sdk/lib/telemetry.py +1 -1
  59. wandb/sdk/service/_startup_debug.py +1 -1
  60. wandb/sdk/service/server_sock.py +3 -2
  61. wandb/sdk/service/service.py +1 -1
  62. wandb/sdk/service/streams.py +19 -17
  63. wandb/sdk/verify/verify.py +13 -13
  64. wandb/sdk/wandb_init.py +316 -246
  65. wandb/sdk/wandb_login.py +1 -1
  66. wandb/sdk/wandb_metadata.py +547 -0
  67. wandb/sdk/wandb_run.py +134 -39
  68. wandb/sdk/wandb_settings.py +7 -63
  69. wandb/sdk/wandb_setup.py +83 -82
  70. wandb/sdk/wandb_sweep.py +2 -2
  71. wandb/sdk/wandb_sync.py +15 -18
  72. wandb/sync/sync.py +10 -10
  73. wandb/util.py +11 -3
  74. wandb/wandb_agent.py +11 -16
  75. wandb/wandb_controller.py +7 -7
  76. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/METADATA +3 -2
  77. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/RECORD +80 -78
  78. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/WHEEL +0 -0
  79. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/entry_points.txt +0 -0
  80. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_run.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import _thread as thread
4
3
  import atexit
5
4
  import functools
6
5
  import glob
@@ -30,13 +29,13 @@ import wandb.util
30
29
  from wandb import trigger
31
30
  from wandb._globals import _datatypes_set_callback
32
31
  from wandb.apis import internal, public
33
- from wandb.apis.internal import Api
34
32
  from wandb.apis.public import Api as PublicApi
35
33
  from wandb.errors import CommError, UnsupportedError, UsageError
36
34
  from wandb.errors.links import url_registry
37
35
  from wandb.integration.torch import wandb_torch
38
36
  from wandb.plot import CustomChart, Visualize
39
37
  from wandb.proto.wandb_internal_pb2 import (
38
+ MetadataRequest,
40
39
  MetricRecord,
41
40
  PollExitResponse,
42
41
  Result,
@@ -73,6 +72,7 @@ from .lib import (
73
72
  deprecate,
74
73
  filenames,
75
74
  filesystem,
75
+ interrupt,
76
76
  ipython,
77
77
  module,
78
78
  printer,
@@ -84,6 +84,7 @@ from .lib import (
84
84
  from .lib.exit_hooks import ExitHooks
85
85
  from .lib.mailbox import MailboxError, MailboxHandle, MailboxProbe, MailboxProgress
86
86
  from .wandb_alerts import AlertLevel
87
+ from .wandb_metadata import Metadata
87
88
  from .wandb_settings import Settings
88
89
  from .wandb_setup import _WandbSetup
89
90
 
@@ -287,7 +288,7 @@ class RunStatusChecker:
287
288
  # TODO(frz): This check is required
288
289
  # until WB-3606 is resolved on server side.
289
290
  if not wandb.agents.pyagent.is_running(): # type: ignore
290
- thread.interrupt_main()
291
+ interrupt.interrupt_main()
291
292
  return
292
293
 
293
294
  try:
@@ -595,15 +596,17 @@ class Run:
595
596
  self._config._set_artifact_callback(self._config_artifact_callback)
596
597
  self._config._set_settings(self._settings)
597
598
 
598
- # todo: perhaps this should be a property that is a noop on a finished run
599
+ # TODO: perhaps this should be a property that is a noop on a finished run
599
600
  self.summary = wandb_summary.Summary(
600
601
  self._summary_get_current_summary_callback,
601
602
  )
602
603
  self.summary._set_update_callback(self._summary_update_callback)
603
604
 
605
+ self.__metadata: Metadata | None = None
606
+
604
607
  self._step = 0
605
608
  self._starting_step = 0
606
- # todo: eventually would be nice to make this configurable using self._settings._start_time
609
+ # TODO: eventually would be nice to make this configurable using self._settings._start_time
607
610
  # need to test (jhr): if you set start time to 2 days ago and run a test for 15 minutes,
608
611
  # does the total time get calculated right (not as 2 days and 15 minutes)?
609
612
  self._start_time = time.time()
@@ -1359,32 +1362,68 @@ class Run:
1359
1362
  files: FilesDict = dict(files=[(GlobStr(glob.escape(fname)), "now")])
1360
1363
  self._backend.interface.publish_files(files)
1361
1364
 
1362
- def _serialize_custom_charts(self, data: dict[str, Any]) -> dict[str, Any]:
1363
- if not data:
1364
- return data
1365
+ def _pop_all_charts(
1366
+ self,
1367
+ data: dict[str, Any],
1368
+ key_prefix: str | None = None,
1369
+ ) -> dict[str, Any]:
1370
+ """Pops all charts from a dictionary including nested charts.
1365
1371
 
1366
- chart_keys = set()
1372
+ This function will return a mapping of the charts and a dot-separated
1373
+ key for each chart. Indicating the path to the chart in the data dictionary.
1374
+ """
1375
+ keys_to_remove = set()
1376
+ charts: dict[str, Any] = {}
1367
1377
  for k, v in data.items():
1378
+ key = f"{key_prefix}.{k}" if key_prefix else k
1368
1379
  if isinstance(v, Visualize):
1369
- data[k] = v.table
1370
- v.set_key(k)
1371
- self._config_callback(
1372
- val=v.spec.config_value,
1373
- key=v.spec.config_key,
1374
- )
1380
+ keys_to_remove.add(k)
1381
+ charts[key] = v
1375
1382
  elif isinstance(v, CustomChart):
1376
- chart_keys.add(k)
1377
- v.set_key(k)
1378
- self._config_callback(
1379
- key=v.spec.config_key,
1380
- val=v.spec.config_value,
1381
- )
1383
+ keys_to_remove.add(k)
1384
+ charts[key] = v
1385
+ elif isinstance(v, dict):
1386
+ nested_charts = self._pop_all_charts(v, key)
1387
+ charts.update(nested_charts)
1388
+
1389
+ for k in keys_to_remove:
1390
+ data.pop(k)
1391
+
1392
+ return charts
1393
+
1394
+ def _serialize_custom_charts(
1395
+ self,
1396
+ data: dict[str, Any],
1397
+ ) -> dict[str, Any]:
1398
+ """Process and replace chart objects with their underlying table values.
1399
+
1400
+ This processes the chart objects passed to `run.log()`, replacing their entries
1401
+ in the given dictionary (which is saved to the run's history) and adding them
1402
+ to the run's config.
1403
+
1404
+ Args:
1405
+ data: Dictionary containing data that may include plot objects
1406
+ Plot objects can be nested in dictionaries, which will be processed recursively.
1407
+
1408
+ Returns:
1409
+ The processed dictionary with custom charts transformed into tables.
1410
+ """
1411
+ if not data:
1412
+ return data
1413
+
1414
+ charts = self._pop_all_charts(data)
1415
+ for k, v in charts.items():
1416
+ v.set_key(k)
1417
+ self._config_callback(
1418
+ val=v.spec.config_value,
1419
+ key=v.spec.config_key,
1420
+ )
1382
1421
 
1383
- for k in chart_keys:
1384
- # remove the chart key from the row
1385
- v = data.pop(k)
1386
1422
  if isinstance(v, CustomChart):
1387
1423
  data[v.spec.table_key] = v.table
1424
+ elif isinstance(v, Visualize):
1425
+ data[k] = v.table
1426
+
1388
1427
  return data
1389
1428
 
1390
1429
  def _partial_history_callback(
@@ -1774,7 +1813,10 @@ class Run:
1774
1813
  examples = []
1775
1814
  for i in range(3):
1776
1815
  pixels = np.random.randint(
1777
- low=0, high=256, size=(100, 100, 3), dtype=np.uint8
1816
+ low=0,
1817
+ high=256,
1818
+ size=(100, 100, 3),
1819
+ dtype=np.uint8,
1778
1820
  )
1779
1821
  pil_image = PILImage.fromarray(pixels, mode="RGB")
1780
1822
  image = wandb.Image(pil_image, caption=f"random field {i}")
@@ -1791,7 +1833,10 @@ class Run:
1791
1833
  run = wandb.init()
1792
1834
  # axes are (time, channel, height, width)
1793
1835
  frames = np.random.randint(
1794
- low=0, high=256, size=(10, 3, 100, 100), dtype=np.uint8
1836
+ low=0,
1837
+ high=256,
1838
+ size=(10, 3, 100, 100),
1839
+ dtype=np.uint8,
1795
1840
  )
1796
1841
  run.log({"video": wandb.Video(frames, fps=4)})
1797
1842
  ```
@@ -2133,8 +2178,9 @@ class Run:
2133
2178
  # Inform the service that we're done sending messages for this run.
2134
2179
  #
2135
2180
  # TODO: Why not do this in _atexit_cleanup()?
2136
- service = self._wl and self._wl.service
2137
- if service and self._settings.run_id:
2181
+ if self._settings.run_id:
2182
+ assert self._wl
2183
+ service = self._wl.assert_service()
2138
2184
  service.inform_finish(run_id=self._settings.run_id)
2139
2185
 
2140
2186
  finally:
@@ -2313,7 +2359,7 @@ class Run:
2313
2359
  self._err_redir = err_redir
2314
2360
  logger.info("Redirects installed.")
2315
2361
  except Exception as e:
2316
- print(e)
2362
+ wandb.termwarn(f"Failed to redirect: {e}")
2317
2363
  logger.error("Failed to redirect.", exc_info=e)
2318
2364
  return
2319
2365
 
@@ -2374,8 +2420,7 @@ class Run:
2374
2420
  logger.info("atexit reg")
2375
2421
  self._hooks = ExitHooks()
2376
2422
 
2377
- service = self._wl and self._wl.service
2378
- if not service:
2423
+ if self.settings.x_disable_service:
2379
2424
  self._hooks.hook()
2380
2425
  # NB: manager will perform atexit hook like behavior for outstanding runs
2381
2426
  atexit.register(lambda: self._atexit_cleanup())
@@ -2388,10 +2433,6 @@ class Run:
2388
2433
  self._output_writer.close()
2389
2434
  self._output_writer = None
2390
2435
 
2391
- def _on_init(self) -> None:
2392
- if self._settings._offline:
2393
- return
2394
-
2395
2436
  def _on_start(self) -> None:
2396
2437
  # would like to move _set_global to _on_ready to unify _on_start and _on_attach
2397
2438
  # (we want to do the set globals after attach)
@@ -3230,8 +3271,7 @@ class Run:
3230
3271
  is_user_created: bool = False,
3231
3272
  use_after_commit: bool = False,
3232
3273
  ) -> Artifact:
3233
- api = internal.Api()
3234
- if api.settings().get("anonymous") == "true":
3274
+ if self._settings.anonymous in ["allow", "must"]:
3235
3275
  wandb.termwarn(
3236
3276
  "Artifacts logged anonymously cannot be claimed and expire after 7 days."
3237
3277
  )
@@ -3665,6 +3705,62 @@ class Run:
3665
3705
  logger.error("Error getting system metrics: %s", e)
3666
3706
  return {}
3667
3707
 
3708
+ @property
3709
+ @_run_decorator._attach
3710
+ @_run_decorator._noop_on_finish()
3711
+ def _metadata(self) -> Metadata | None:
3712
+ """The metadata associated with this run.
3713
+
3714
+ NOTE: Automatically collected metadata can be overridden by the user.
3715
+ """
3716
+ if not self._backend or not self._backend.interface:
3717
+ return self.__metadata
3718
+
3719
+ # Initialize the metadata object if it doesn't exist.
3720
+ if self.__metadata is None:
3721
+ self.__metadata = Metadata()
3722
+ self.__metadata._set_callback(self._metadata_callback)
3723
+
3724
+ handle = self._backend.interface.deliver_get_system_metadata()
3725
+ result = handle.wait(timeout=1)
3726
+
3727
+ if not result:
3728
+ logger.error("Error getting run metadata: no result")
3729
+ return None
3730
+
3731
+ try:
3732
+ response = result.response.get_system_metadata_response
3733
+
3734
+ # Temporarily disable the callback to prevent triggering
3735
+ # an update call to wandb-core with the callback.
3736
+ with self.__metadata.disable_callback():
3737
+ # Values stored in the metadata object take precedence.
3738
+ self.__metadata.update_from_proto(response.metadata, skip_existing=True)
3739
+
3740
+ return self.__metadata
3741
+ except Exception as e:
3742
+ logger.error("Error getting run metadata: %s", e)
3743
+
3744
+ return None
3745
+
3746
+ @_run_decorator._noop_on_finish()
3747
+ @_run_decorator._attach
3748
+ def _metadata_callback(
3749
+ self,
3750
+ metadata: MetadataRequest,
3751
+ ) -> None:
3752
+ """Callback to publish Metadata to wandb-core upon user updates."""
3753
+ # ignore updates if the attached to another run
3754
+ if self._is_attached:
3755
+ wandb.termwarn(
3756
+ "Metadata updates are ignored when attached to another run.",
3757
+ repeat=False,
3758
+ )
3759
+ return
3760
+
3761
+ if self._backend and self._backend.interface:
3762
+ self._backend.interface.publish_metadata(metadata)
3763
+
3668
3764
  # ------------------------------------------------------------------------------
3669
3765
  # HEADER
3670
3766
  # ------------------------------------------------------------------------------
@@ -3772,8 +3868,7 @@ class Run:
3772
3868
  f'{printer.emoji("rocket")} View run at {printer.link(run_url)}',
3773
3869
  )
3774
3870
 
3775
- # TODO(settings) use `wandb_settings` (if self.settings.anonymous == "true":)
3776
- if run_name and Api().api.settings().get("anonymous") == "true":
3871
+ if run_name and settings.anonymous in ["allow", "must"]:
3777
3872
  printer.display(
3778
3873
  (
3779
3874
  "Do NOT share these links with anyone."
@@ -34,14 +34,12 @@ from pydantic_core import SchemaValidator, core_schema
34
34
 
35
35
  import wandb
36
36
  from wandb import env, termwarn, util
37
- from wandb.apis.internal import Api
38
37
  from wandb.errors import UsageError
39
38
  from wandb.proto import wandb_settings_pb2
40
39
 
41
- from .lib import apikey, credentials, filesystem, ipython
40
+ from .lib import apikey, credentials, ipython
42
41
  from .lib.gitlib import GitRepo
43
42
  from .lib.run_moment import RunMoment
44
- from .lib.runid import generate_id
45
43
 
46
44
 
47
45
  def _path_convert(*args: str) -> str:
@@ -305,7 +303,7 @@ class Settings(BaseModel, validate_assignment=True):
305
303
  x_save_requirements: bool = True
306
304
  x_service_transport: str | None = None
307
305
  x_service_wait: float = 30.0
308
- x_show_operation_stats: bool = False
306
+ x_show_operation_stats: bool = True
309
307
  # The start time of the run in seconds since the Unix epoch.
310
308
  x_start_time: float | None = None
311
309
  # PID of the process that started the wandb-core process to collect system stats for.
@@ -349,7 +347,7 @@ class Settings(BaseModel, validate_assignment=True):
349
347
  @model_validator(mode="before")
350
348
  @classmethod
351
349
  def catch_private_settings(cls, values):
352
- """Check if a private field is provided and assign to the corrsponding public one.
350
+ """Check if a private field is provided and assign to the corresponding public one.
353
351
 
354
352
  This is a compatibility layer to handle previous versions of the settings.
355
353
  """
@@ -961,7 +959,7 @@ class Settings(BaseModel, validate_assignment=True):
961
959
  # The Settings class does not track the source of the settings,
962
960
  # so it is up to the developer to ensure that the settings are applied
963
961
  # in the correct order. Most of the updates are done in
964
- # wandb/sdk/wandb_setup.py::_WandbSetup__WandbSetup._settings_setup.
962
+ # wandb/sdk/wandb_setup.py::_WandbSetup._settings_setup.
965
963
 
966
964
  def update_from_system_config_file(self):
967
965
  """Update settings from the system config file."""
@@ -1063,7 +1061,8 @@ class Settings(BaseModel, validate_assignment=True):
1063
1061
  )
1064
1062
  self.x_executable = _executable
1065
1063
 
1066
- self.docker = env.get_docker(util.image_id_from_k8s())
1064
+ if self.docker is None:
1065
+ self.docker = env.get_docker(util.image_id_from_k8s())
1067
1066
 
1068
1067
  # proceed if not in CLI mode
1069
1068
  if self.x_cli_only_mode:
@@ -1153,61 +1152,6 @@ class Settings(BaseModel, validate_assignment=True):
1153
1152
 
1154
1153
  return settings_proto
1155
1154
 
1156
- def handle_resume_logic(self):
1157
- """Handle logic for resuming runs."""
1158
- # handle auto resume logic
1159
- if self.resume == "auto":
1160
- if os.path.exists(self.resume_fname):
1161
- with open(self.resume_fname) as f:
1162
- resume_run_id = json.load(f)["run_id"]
1163
- if self.run_id is None:
1164
- self.run_id = resume_run_id
1165
- elif self.run_id != resume_run_id:
1166
- wandb.termwarn(
1167
- "Tried to auto resume run with "
1168
- f"id {resume_run_id} but id {self.run_id} is set.",
1169
- )
1170
- if self.run_id is None:
1171
- self.run_id = generate_id()
1172
-
1173
- # persist run_id in case of failure
1174
- if self.resume == "auto" and self.resume_fname is not None:
1175
- filesystem.mkdir_exists_ok(self.wandb_dir)
1176
- with open(self.resume_fname, "w") as f:
1177
- f.write(json.dumps({"run_id": self.run_id}))
1178
-
1179
- def handle_sweep_logic(self):
1180
- """Update settings based on sweep context.
1181
-
1182
- When running a sweep, the project, entity, and run_id are handled externally,
1183
- and should be ignored if they are set.
1184
- """
1185
- if self.sweep_id is None:
1186
- return
1187
-
1188
- for key in ("project", "entity", "run_id"):
1189
- value = getattr(self, key)
1190
- if value is not None:
1191
- wandb.termwarn(f"Ignoring {key} {value!r} when running a sweep.")
1192
- setattr(self, key, None)
1193
-
1194
- def handle_launch_logic(self):
1195
- """Update settings based on launch context.
1196
-
1197
- When running in a launch context, the project, entity, and run_id are handled
1198
- externally, and should be ignored if they are set.
1199
- """
1200
- if not self.launch:
1201
- return
1202
-
1203
- for key in ("project", "entity", "run_id"):
1204
- value = getattr(self, key)
1205
- if value is not None:
1206
- wandb.termwarn(
1207
- f"Ignoring {key} {value!r} when running from wandb launch context."
1208
- )
1209
- setattr(self, key, None)
1210
-
1211
1155
  @staticmethod
1212
1156
  def validate_url(url: str) -> None:
1213
1157
  """Validate a URL string."""
@@ -1293,7 +1237,7 @@ class Settings(BaseModel, validate_assignment=True):
1293
1237
  def _get_url_query_string(self) -> str:
1294
1238
  """Construct the query string for project, run, and sweep URLs."""
1295
1239
  # TODO: remove dependency on Api()
1296
- if Api().settings().get("anonymous") != "true":
1240
+ if self.anonymous not in ["allow", "must"]:
1297
1241
  return ""
1298
1242
 
1299
1243
  api_key = apikey.api_key(settings=self)