wandb 0.19.1rc1__py3-none-any.whl → 0.19.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) 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 +0 -0
  12. wandb/cli/beta.py +7 -4
  13. wandb/cli/cli.py +5 -7
  14. wandb/docker/__init__.py +4 -4
  15. wandb/integration/fastai/__init__.py +4 -6
  16. wandb/integration/keras/keras.py +5 -3
  17. wandb/integration/metaflow/metaflow.py +14 -16
  18. wandb/integration/prodigy/prodigy.py +3 -11
  19. wandb/integration/sagemaker/__init__.py +5 -3
  20. wandb/integration/sagemaker/config.py +17 -8
  21. wandb/integration/sagemaker/files.py +0 -1
  22. wandb/integration/sagemaker/resources.py +47 -18
  23. wandb/integration/torch/wandb_torch.py +1 -1
  24. wandb/proto/v3/wandb_internal_pb2.py +273 -235
  25. wandb/proto/v4/wandb_internal_pb2.py +222 -214
  26. wandb/proto/v5/wandb_internal_pb2.py +222 -214
  27. wandb/sdk/artifacts/artifact.py +3 -9
  28. wandb/sdk/backend/backend.py +1 -1
  29. wandb/sdk/data_types/base_types/wb_value.py +1 -1
  30. wandb/sdk/data_types/graph.py +2 -2
  31. wandb/sdk/data_types/saved_model.py +1 -1
  32. wandb/sdk/data_types/video.py +1 -1
  33. wandb/sdk/interface/interface.py +25 -25
  34. wandb/sdk/interface/interface_shared.py +21 -5
  35. wandb/sdk/internal/handler.py +19 -1
  36. wandb/sdk/internal/internal.py +1 -1
  37. wandb/sdk/internal/internal_api.py +4 -5
  38. wandb/sdk/internal/sample.py +2 -2
  39. wandb/sdk/internal/sender.py +1 -2
  40. wandb/sdk/internal/settings_static.py +3 -1
  41. wandb/sdk/internal/system/assets/disk.py +4 -4
  42. wandb/sdk/internal/system/assets/gpu.py +1 -1
  43. wandb/sdk/internal/system/assets/memory.py +1 -1
  44. wandb/sdk/internal/system/system_info.py +1 -1
  45. wandb/sdk/internal/system/system_monitor.py +3 -1
  46. wandb/sdk/internal/tb_watcher.py +1 -1
  47. wandb/sdk/launch/_project_spec.py +3 -3
  48. wandb/sdk/launch/builder/abstract.py +1 -1
  49. wandb/sdk/lib/apikey.py +2 -3
  50. wandb/sdk/lib/fsm.py +1 -1
  51. wandb/sdk/lib/gitlib.py +1 -1
  52. wandb/sdk/lib/gql_request.py +1 -1
  53. wandb/sdk/lib/interrupt.py +37 -0
  54. wandb/sdk/lib/lazyloader.py +1 -1
  55. wandb/sdk/lib/progress.py +7 -1
  56. wandb/sdk/lib/service_connection.py +1 -1
  57. wandb/sdk/lib/telemetry.py +1 -1
  58. wandb/sdk/service/_startup_debug.py +1 -1
  59. wandb/sdk/service/server_sock.py +3 -2
  60. wandb/sdk/service/service.py +1 -1
  61. wandb/sdk/service/streams.py +19 -17
  62. wandb/sdk/verify/verify.py +13 -13
  63. wandb/sdk/wandb_init.py +316 -246
  64. wandb/sdk/wandb_login.py +1 -1
  65. wandb/sdk/wandb_metadata.py +547 -0
  66. wandb/sdk/wandb_run.py +134 -39
  67. wandb/sdk/wandb_settings.py +7 -63
  68. wandb/sdk/wandb_setup.py +83 -82
  69. wandb/sdk/wandb_sweep.py +2 -2
  70. wandb/sdk/wandb_sync.py +15 -18
  71. wandb/sync/sync.py +10 -10
  72. wandb/util.py +11 -3
  73. wandb/wandb_agent.py +11 -16
  74. wandb/wandb_controller.py +7 -7
  75. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/METADATA +5 -3
  76. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/RECORD +79 -77
  77. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/WHEEL +1 -1
  78. {wandb-0.19.1rc1.dist-info → wandb-0.19.3.dist-info}/entry_points.txt +0 -0
  79. {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)