wandb 0.21.3__py3-none-win32.whl → 0.22.0__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 (76) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +1 -1
  3. wandb/_analytics.py +65 -0
  4. wandb/_iterutils.py +8 -0
  5. wandb/_pydantic/__init__.py +10 -11
  6. wandb/_pydantic/base.py +3 -53
  7. wandb/_pydantic/field_types.py +29 -0
  8. wandb/_pydantic/v1_compat.py +47 -30
  9. wandb/_strutils.py +40 -0
  10. wandb/apis/public/__init__.py +42 -0
  11. wandb/apis/public/api.py +17 -4
  12. wandb/apis/public/artifacts.py +5 -4
  13. wandb/apis/public/automations.py +2 -1
  14. wandb/apis/public/registries/_freezable_list.py +6 -6
  15. wandb/apis/public/registries/_utils.py +2 -1
  16. wandb/apis/public/registries/registries_search.py +4 -0
  17. wandb/apis/public/registries/registry.py +7 -0
  18. wandb/apis/public/runs.py +24 -6
  19. wandb/automations/_filters/expressions.py +3 -2
  20. wandb/automations/_filters/operators.py +2 -1
  21. wandb/automations/_validators.py +20 -0
  22. wandb/automations/actions.py +4 -2
  23. wandb/automations/events.py +4 -5
  24. wandb/bin/gpu_stats.exe +0 -0
  25. wandb/bin/wandb-core +0 -0
  26. wandb/cli/beta.py +48 -130
  27. wandb/cli/beta_sync.py +226 -0
  28. wandb/integration/dspy/__init__.py +5 -0
  29. wandb/integration/dspy/dspy.py +422 -0
  30. wandb/integration/weave/weave.py +55 -0
  31. wandb/proto/v3/wandb_internal_pb2.py +234 -224
  32. wandb/proto/v3/wandb_server_pb2.py +38 -57
  33. wandb/proto/v3/wandb_sync_pb2.py +87 -0
  34. wandb/proto/v3/wandb_telemetry_pb2.py +12 -12
  35. wandb/proto/v4/wandb_internal_pb2.py +226 -224
  36. wandb/proto/v4/wandb_server_pb2.py +38 -41
  37. wandb/proto/v4/wandb_sync_pb2.py +38 -0
  38. wandb/proto/v4/wandb_telemetry_pb2.py +12 -12
  39. wandb/proto/v5/wandb_internal_pb2.py +226 -224
  40. wandb/proto/v5/wandb_server_pb2.py +38 -41
  41. wandb/proto/v5/wandb_sync_pb2.py +39 -0
  42. wandb/proto/v5/wandb_telemetry_pb2.py +12 -12
  43. wandb/proto/v6/wandb_base_pb2.py +3 -3
  44. wandb/proto/v6/wandb_internal_pb2.py +229 -227
  45. wandb/proto/v6/wandb_server_pb2.py +41 -44
  46. wandb/proto/v6/wandb_settings_pb2.py +3 -3
  47. wandb/proto/v6/wandb_sync_pb2.py +49 -0
  48. wandb/proto/v6/wandb_telemetry_pb2.py +15 -15
  49. wandb/proto/wandb_generate_proto.py +1 -0
  50. wandb/proto/wandb_sync_pb2.py +12 -0
  51. wandb/sdk/artifacts/_validators.py +50 -49
  52. wandb/sdk/artifacts/artifact.py +7 -7
  53. wandb/sdk/artifacts/exceptions.py +2 -1
  54. wandb/sdk/artifacts/storage_handlers/gcs_handler.py +1 -1
  55. wandb/sdk/artifacts/storage_handlers/http_handler.py +1 -3
  56. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +1 -1
  57. wandb/sdk/artifacts/storage_handlers/s3_handler.py +3 -2
  58. wandb/sdk/artifacts/storage_policies/_factories.py +63 -0
  59. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +59 -124
  60. wandb/sdk/interface/interface.py +10 -0
  61. wandb/sdk/interface/interface_shared.py +9 -0
  62. wandb/sdk/lib/asyncio_compat.py +88 -23
  63. wandb/sdk/lib/gql_request.py +18 -7
  64. wandb/sdk/lib/printer.py +9 -13
  65. wandb/sdk/lib/progress.py +8 -6
  66. wandb/sdk/lib/service/service_connection.py +42 -12
  67. wandb/sdk/mailbox/wait_with_progress.py +1 -1
  68. wandb/sdk/wandb_init.py +9 -9
  69. wandb/sdk/wandb_run.py +13 -1
  70. wandb/sdk/wandb_settings.py +55 -0
  71. wandb/wandb_agent.py +35 -4
  72. {wandb-0.21.3.dist-info → wandb-0.22.0.dist-info}/METADATA +1 -1
  73. {wandb-0.21.3.dist-info → wandb-0.22.0.dist-info}/RECORD +76 -64
  74. {wandb-0.21.3.dist-info → wandb-0.22.0.dist-info}/WHEEL +0 -0
  75. {wandb-0.21.3.dist-info → wandb-0.22.0.dist-info}/entry_points.txt +0 -0
  76. {wandb-0.21.3.dist-info → wandb-0.22.0.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/lib/printer.py CHANGED
@@ -156,14 +156,10 @@ class Printer(abc.ABC):
156
156
  """
157
157
 
158
158
  @abc.abstractmethod
159
- def progress_close(self, text: str | None = None) -> None:
159
+ def progress_close(self) -> None:
160
160
  """Close the progress indicator.
161
161
 
162
162
  After this, `progress_update` should not be used.
163
-
164
- Args:
165
- text: The final text to set on the progress indicator.
166
- Ignored in Jupyter notebooks.
167
163
  """
168
164
 
169
165
  @staticmethod
@@ -342,13 +338,10 @@ class _PrinterTerm(Printer):
342
338
  wandb.termlog(f"{next(self._progress)} {text}", newline=False)
343
339
 
344
340
  @override
345
- def progress_close(self, text: str | None = None) -> None:
341
+ def progress_close(self) -> None:
346
342
  if self._settings and self._settings.silent:
347
343
  return
348
344
 
349
- text = text or " " * 79
350
- wandb.termlog(text)
351
-
352
345
  @property
353
346
  @override
354
347
  def supports_html(self) -> bool:
@@ -462,11 +455,14 @@ class _PrinterJupyter(Printer):
462
455
  display_id=True,
463
456
  )
464
457
 
465
- if handle:
458
+ if not handle:
459
+ yield None
460
+ return
461
+
462
+ try:
466
463
  yield _DynamicJupyterText(handle)
464
+ finally:
467
465
  handle.update(self._ipython_display.HTML(""))
468
- else:
469
- yield None
470
466
 
471
467
  @override
472
468
  def display(
@@ -539,7 +535,7 @@ class _PrinterJupyter(Printer):
539
535
  self._progress.update(percent_done, text)
540
536
 
541
537
  @override
542
- def progress_close(self, text: str | None = None) -> None:
538
+ def progress_close(self) -> None:
543
539
  if self._progress:
544
540
  self._progress.close()
545
541
 
wandb/sdk/lib/progress.py CHANGED
@@ -70,12 +70,14 @@ def progress_printer(
70
70
  default_text: The text to show if no information is available.
71
71
  """
72
72
  with printer.dynamic_text() as text_area:
73
- yield ProgressPrinter(
74
- printer,
75
- text_area,
76
- default_text=default_text,
77
- )
78
- printer.progress_close()
73
+ try:
74
+ yield ProgressPrinter(
75
+ printer,
76
+ text_area,
77
+ default_text=default_text,
78
+ )
79
+ finally:
80
+ printer.progress_close()
79
81
 
80
82
 
81
83
  class ProgressPrinter:
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import atexit
4
+ import pathlib
4
5
  from typing import Callable
5
6
 
6
7
  from wandb.proto import wandb_server_pb2 as spb
7
- from wandb.proto import wandb_settings_pb2
8
+ from wandb.proto import wandb_settings_pb2, wandb_sync_pb2
8
9
  from wandb.sdk import wandb_settings
9
10
  from wandb.sdk.interface.interface import InterfaceBase
10
11
  from wandb.sdk.interface.interface_sock import InterfaceSock
@@ -12,6 +13,7 @@ from wandb.sdk.lib import asyncio_manager
12
13
  from wandb.sdk.lib.exit_hooks import ExitHooks
13
14
  from wandb.sdk.lib.service.service_client import ServiceClient
14
15
  from wandb.sdk.mailbox import HandleAbandonedError, MailboxClosedError
16
+ from wandb.sdk.mailbox.mailbox_handle import MailboxHandle
15
17
 
16
18
  from . import service_process, service_token
17
19
 
@@ -96,6 +98,45 @@ class ServiceConnection:
96
98
  """Returns an interface for communicating with the service."""
97
99
  return InterfaceSock(self._client, stream_id=stream_id)
98
100
 
101
+ def init_sync(
102
+ self,
103
+ paths: set[pathlib.Path],
104
+ settings: wandb_settings.Settings,
105
+ ) -> MailboxHandle[wandb_sync_pb2.ServerInitSyncResponse]:
106
+ """Send a ServerInitSyncRequest."""
107
+ init_sync = wandb_sync_pb2.ServerInitSyncRequest(
108
+ path=(str(path) for path in paths),
109
+ settings=settings.to_proto(),
110
+ )
111
+ request = spb.ServerRequest(init_sync=init_sync)
112
+
113
+ handle = self._client.deliver(request)
114
+ return handle.map(lambda r: r.init_sync_response)
115
+
116
+ def sync(
117
+ self,
118
+ id: str,
119
+ *,
120
+ parallelism: int,
121
+ ) -> MailboxHandle[wandb_sync_pb2.ServerSyncResponse]:
122
+ """Send a ServerSyncRequest."""
123
+ sync = wandb_sync_pb2.ServerSyncRequest(id=id, parallelism=parallelism)
124
+ request = spb.ServerRequest(sync=sync)
125
+
126
+ handle = self._client.deliver(request)
127
+ return handle.map(lambda r: r.sync_response)
128
+
129
+ def sync_status(
130
+ self,
131
+ id: str,
132
+ ) -> MailboxHandle[wandb_sync_pb2.ServerSyncStatusResponse]:
133
+ """Send a ServerSyncStatusRequest."""
134
+ sync_status = wandb_sync_pb2.ServerSyncStatusRequest(id=id)
135
+ request = spb.ServerRequest(sync_status=sync_status)
136
+
137
+ handle = self._client.deliver(request)
138
+ return handle.map(lambda r: r.sync_status_response)
139
+
99
140
  def inform_init(
100
141
  self,
101
142
  settings: wandb_settings_pb2.Settings,
@@ -143,17 +184,6 @@ class ServiceConnection:
143
184
  else:
144
185
  return response.inform_attach_response.settings
145
186
 
146
- def inform_start(
147
- self,
148
- settings: wandb_settings_pb2.Settings,
149
- run_id: str,
150
- ) -> None:
151
- """Send a start request to the service."""
152
- request = spb.ServerInformStartRequest()
153
- request.settings.CopyFrom(settings)
154
- request._info.stream_id = run_id
155
- self._client.publish(spb.ServerRequest(inform_start=request))
156
-
157
187
  def teardown(self, exit_code: int) -> int | None:
158
188
  """Close the connection.
159
189
 
@@ -62,7 +62,7 @@ def wait_all_with_progress(
62
62
  start_time = time.monotonic()
63
63
 
64
64
  async def progress_loop_with_timeout() -> list[_T]:
65
- with asyncio_compat.cancel_on_exit(display_progress()):
65
+ async with asyncio_compat.cancel_on_exit(display_progress()):
66
66
  if timeout is not None:
67
67
  elapsed_time = time.monotonic() - start_time
68
68
  remaining_timeout = timeout - elapsed_time
wandb/sdk/wandb_init.py CHANGED
@@ -839,6 +839,13 @@ class _WandbInit:
839
839
  " and reinit is set to 'create_new', so continuing"
840
840
  )
841
841
 
842
+ elif settings.resume == "must":
843
+ raise wandb.Error(
844
+ "Cannot resume a run while another run is active."
845
+ " You must either finish it using run.finish(),"
846
+ " or use reinit='create_new' when calling wandb.init()."
847
+ )
848
+
842
849
  else:
843
850
  run_printer.display(
844
851
  "wandb.init() called while a run is active and reinit is"
@@ -864,7 +871,6 @@ class _WandbInit:
864
871
  backend.ensure_launched()
865
872
  self._logger.info("backend started and connected")
866
873
 
867
- # resuming needs access to the server, check server_status()?
868
874
  run = Run(
869
875
  config=config.base_no_artifacts,
870
876
  settings=settings,
@@ -1009,14 +1015,6 @@ class _WandbInit:
1009
1015
  run._set_run_obj(result.run_result.run)
1010
1016
 
1011
1017
  self._logger.info("starting run threads in backend")
1012
- # initiate run (stats and metadata probing)
1013
-
1014
- if service:
1015
- assert settings.run_id
1016
- service.inform_start(
1017
- settings=settings.to_proto(),
1018
- run_id=settings.run_id,
1019
- )
1020
1018
 
1021
1019
  assert backend.interface
1022
1020
 
@@ -1027,6 +1025,8 @@ class _WandbInit:
1027
1025
  except TimeoutError:
1028
1026
  pass
1029
1027
 
1028
+ backend.interface.publish_probe_system_info()
1029
+
1030
1030
  assert self._wl is not None
1031
1031
  self.run = run
1032
1032
 
wandb/sdk/wandb_run.py CHANGED
@@ -892,7 +892,19 @@ class Run:
892
892
  def tags(self, tags: Sequence) -> None:
893
893
  with telemetry.context(run=self) as tel:
894
894
  tel.feature.set_run_tags = True
895
- self._settings.run_tags = tuple(tags)
895
+
896
+ try:
897
+ self._settings.run_tags = tuple(tags)
898
+ except ValueError as e:
899
+ # For runtime tag setting, warn instead of crash
900
+ # Extract the core error message without the pydantic wrapper
901
+ error_msg = str(e)
902
+ if "Value error," in error_msg:
903
+ # Extract the actual error message after "Value error, "
904
+ error_msg = error_msg.split("Value error, ")[1].split(" [type=")[0]
905
+ wandb.termwarn(f"Invalid tag detected: {error_msg} Tags not updated.")
906
+ return
907
+
896
908
  if self._backend and self._backend.interface:
897
909
  self._backend.interface.publish_run(self)
898
910
 
@@ -1435,6 +1435,61 @@ class Settings(BaseModel, validate_assignment=True):
1435
1435
  raise UsageError("Sweep ID cannot contain only whitespace")
1436
1436
  return value
1437
1437
 
1438
+ @field_validator("run_tags", mode="before")
1439
+ @classmethod
1440
+ def validate_run_tags(cls, value):
1441
+ """Validate run tags.
1442
+
1443
+ Validates that each tag:
1444
+ - Is between 1 and 64 characters in length (inclusive)
1445
+ - Converts single string values to tuple format
1446
+ - Preserves None values
1447
+
1448
+ Args:
1449
+ value: A string, list, tuple, or None representing tags
1450
+
1451
+ Returns:
1452
+ tuple: A tuple of validated tags, or None
1453
+
1454
+ Raises:
1455
+ ValueError: If any tag is empty or exceeds 64 characters
1456
+
1457
+ <!-- lazydoc-ignore-classmethod: internal -->
1458
+ """
1459
+ if value is None:
1460
+ return None
1461
+
1462
+ # Convert to tuple if needed
1463
+ if isinstance(value, str):
1464
+ tags = (value,)
1465
+ else:
1466
+ tags = tuple(value)
1467
+
1468
+ # Validate each tag and accumulate errors
1469
+ errors = []
1470
+ for i, tag in enumerate(tags):
1471
+ tag_str = str(tag)
1472
+ if len(tag_str) == 0:
1473
+ errors.append(
1474
+ f"Tag at index {i} is empty. Tags must be between 1 and 64 characters"
1475
+ )
1476
+ elif len(tag_str) > 64:
1477
+ # Truncate long tags for display
1478
+ display_tag = (
1479
+ f"{tag_str[:20]}...{tag_str[-20:]}"
1480
+ if len(tag_str) > 43
1481
+ else tag_str
1482
+ )
1483
+ errors.append(
1484
+ f"Tag '{display_tag}' is {len(tag_str)} characters. Tags must be between 1 and 64 characters"
1485
+ )
1486
+
1487
+ # Raise combined error if any validation issues were found
1488
+ if errors:
1489
+ raise ValueError("; ".join(errors))
1490
+
1491
+ return tags
1492
+
1438
1493
  @field_validator("sweep_param_path", mode="before")
1439
1494
  @classmethod
1440
1495
  def validate_sweep_param_path(cls, value):
wandb/wandb_agent.py CHANGED
@@ -42,11 +42,42 @@ class AgentProcess:
42
42
  if command:
43
43
  if platform.system() == "Windows":
44
44
  kwargs = dict(creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
45
+ env.pop(wandb.env.SERVICE, None)
46
+ # TODO: Determine if we need the same stdin workaround as POSIX case below.
47
+ self._popen = subprocess.Popen(command, env=env, **kwargs)
45
48
  else:
46
- kwargs = dict(preexec_fn=os.setpgrp)
47
- if env.get(wandb.env.SERVICE):
48
- env.pop(wandb.env.SERVICE)
49
- self._popen = subprocess.Popen(command, env=env, **kwargs)
49
+ if sys.version_info >= (3, 11):
50
+ # preexec_fn=os.setpgrp is not thread-safe; process_group was introduced in
51
+ # python 3.11 to replace it, so use that when possible
52
+ kwargs = dict(process_group=0)
53
+ else:
54
+ kwargs = dict(preexec_fn=os.setpgrp)
55
+ env.pop(wandb.env.SERVICE, None)
56
+ # Upon spawning the subprocess in a new process group, the child's process group is
57
+ # not connected to the controlling terminal's stdin. If it tries to access stdin,
58
+ # it gets a SIGTTIN and blocks until we give it the terminal, which we don't want
59
+ # to do.
60
+ #
61
+ # By using subprocess.PIPE, we give it an independent stdin. However, it will still
62
+ # block if it tries to read from stdin, because we're not writing anything to it.
63
+ # We immediately close the subprocess's stdin here so it can fail fast and get an
64
+ # EOF.
65
+ #
66
+ # (One situation that makes this relevant is that importing `readline` even
67
+ # indirectly can cause the child to attempt to access stdin, which can trigger the
68
+ # deadlock. In Python 3.13, `import torch` indirectly imports `readline` via `pdb`,
69
+ # meaning `import torch` in a run script can deadlock unless we override stdin.
70
+ # See https://github.com/wandb/wandb/pull/10489 description for more details.)
71
+ #
72
+ # Also, we avoid spawning a new session because that breaks preempted child process
73
+ # handling.
74
+ self._popen = subprocess.Popen(
75
+ command,
76
+ env=env,
77
+ stdin=subprocess.PIPE,
78
+ **kwargs,
79
+ )
80
+ self._popen.stdin.close()
50
81
  elif function:
51
82
  self._proc = multiprocessing.Process(
52
83
  target=self._start,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wandb
3
- Version: 0.21.3
3
+ Version: 0.22.0
4
4
  Summary: A CLI and library for interacting with the Weights & Biases API.
5
5
  Project-URL: Source, https://github.com/wandb/wandb
6
6
  Project-URL: Bug Reports, https://github.com/wandb/wandb/issues