wandb 0.19.9__py3-none-musllinux_1_2_aarch64.whl → 0.19.10__py3-none-musllinux_1_2_aarch64.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 (128) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +4 -1
  3. wandb/_pydantic/__init__.py +14 -7
  4. wandb/_pydantic/base.py +44 -9
  5. wandb/_pydantic/utils.py +66 -0
  6. wandb/_pydantic/v1_compat.py +78 -56
  7. wandb/apis/public/__init__.py +2 -2
  8. wandb/apis/public/api.py +114 -2
  9. wandb/apis/public/artifacts.py +365 -673
  10. wandb/apis/public/automations.py +69 -0
  11. wandb/apis/public/integrations.py +168 -0
  12. wandb/apis/public/projects.py +29 -0
  13. wandb/apis/public/utils.py +107 -1
  14. wandb/automations/__init__.py +81 -0
  15. wandb/automations/_filters/__init__.py +40 -0
  16. wandb/automations/_filters/expressions.py +179 -0
  17. wandb/automations/_filters/operators.py +267 -0
  18. wandb/automations/_filters/run_metrics.py +183 -0
  19. wandb/automations/_generated/__init__.py +184 -0
  20. wandb/automations/_generated/create_filter_trigger.py +21 -0
  21. wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
  22. wandb/automations/_generated/delete_trigger.py +19 -0
  23. wandb/automations/_generated/enums.py +33 -0
  24. wandb/automations/_generated/fragments.py +343 -0
  25. wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
  26. wandb/automations/_generated/get_triggers.py +24 -0
  27. wandb/automations/_generated/get_triggers_by_entity.py +24 -0
  28. wandb/automations/_generated/input_types.py +104 -0
  29. wandb/automations/_generated/integrations_by_entity.py +22 -0
  30. wandb/automations/_generated/operations.py +710 -0
  31. wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
  32. wandb/automations/_generated/update_filter_trigger.py +21 -0
  33. wandb/automations/_utils.py +123 -0
  34. wandb/automations/_validators.py +73 -0
  35. wandb/automations/actions.py +205 -0
  36. wandb/automations/automations.py +109 -0
  37. wandb/automations/events.py +235 -0
  38. wandb/automations/integrations.py +26 -0
  39. wandb/automations/scopes.py +76 -0
  40. wandb/beta/workflows.py +9 -10
  41. wandb/bin/gpu_stats +0 -0
  42. wandb/bin/wandb-core +0 -0
  43. wandb/cli/cli.py +3 -3
  44. wandb/integration/keras/keras.py +2 -1
  45. wandb/integration/langchain/wandb_tracer.py +2 -1
  46. wandb/jupyter.py +137 -118
  47. wandb/old/summary.py +0 -2
  48. wandb/proto/v3/wandb_internal_pb2.py +293 -292
  49. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  50. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  51. wandb/proto/v4/wandb_internal_pb2.py +292 -292
  52. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  53. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  54. wandb/proto/v5/wandb_internal_pb2.py +292 -292
  55. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  56. wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
  57. wandb/proto/v6/wandb_base_pb2.py +41 -0
  58. wandb/proto/v6/wandb_internal_pb2.py +393 -0
  59. wandb/proto/v6/wandb_server_pb2.py +78 -0
  60. wandb/proto/v6/wandb_settings_pb2.py +58 -0
  61. wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
  62. wandb/proto/wandb_base_pb2.py +2 -0
  63. wandb/proto/wandb_deprecated.py +8 -0
  64. wandb/proto/wandb_internal_pb2.py +3 -1
  65. wandb/proto/wandb_server_pb2.py +2 -0
  66. wandb/proto/wandb_settings_pb2.py +2 -0
  67. wandb/proto/wandb_telemetry_pb2.py +2 -0
  68. wandb/sdk/artifacts/_generated/__init__.py +248 -0
  69. wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
  70. wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
  71. wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
  72. wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
  73. wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
  74. wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
  75. wandb/sdk/artifacts/_generated/enums.py +17 -0
  76. wandb/sdk/artifacts/_generated/fragments.py +186 -0
  77. wandb/sdk/artifacts/_generated/input_types.py +16 -0
  78. wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
  79. wandb/sdk/artifacts/_generated/operations.py +510 -0
  80. wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
  81. wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
  82. wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
  83. wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
  84. wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
  85. wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
  86. wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
  87. wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
  88. wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
  89. wandb/sdk/artifacts/_graphql_fragments.py +56 -79
  90. wandb/sdk/artifacts/artifact.py +40 -13
  91. wandb/sdk/artifacts/artifact_manifest_entry.py +2 -1
  92. wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
  93. wandb/sdk/data_types/base_types/media.py +2 -3
  94. wandb/sdk/data_types/base_types/wb_value.py +34 -11
  95. wandb/sdk/data_types/html.py +36 -9
  96. wandb/sdk/data_types/image.py +12 -12
  97. wandb/sdk/data_types/table.py +5 -0
  98. wandb/sdk/data_types/trace_tree.py +2 -0
  99. wandb/sdk/data_types/utils.py +1 -1
  100. wandb/sdk/data_types/video.py +14 -26
  101. wandb/sdk/interface/interface.py +2 -0
  102. wandb/sdk/internal/profiler.py +6 -5
  103. wandb/sdk/internal/run.py +13 -6
  104. wandb/sdk/lib/apikey.py +25 -4
  105. wandb/sdk/lib/asyncio_compat.py +1 -1
  106. wandb/sdk/lib/deprecate.py +13 -22
  107. wandb/sdk/lib/disabled.py +2 -1
  108. wandb/sdk/lib/printer.py +37 -8
  109. wandb/sdk/lib/printer_asyncio.py +46 -0
  110. wandb/sdk/lib/redirect.py +10 -5
  111. wandb/sdk/service/server_sock.py +19 -14
  112. wandb/sdk/service/service.py +9 -7
  113. wandb/sdk/service/streams.py +5 -0
  114. wandb/sdk/verify/verify.py +6 -3
  115. wandb/sdk/wandb_init.py +185 -65
  116. wandb/sdk/wandb_login.py +13 -4
  117. wandb/sdk/wandb_run.py +382 -286
  118. wandb/sdk/wandb_settings.py +21 -3
  119. wandb/sdk/wandb_setup.py +49 -0
  120. wandb/util.py +29 -29
  121. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/METADATA +5 -5
  122. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/RECORD +125 -72
  123. wandb/_globals.py +0 -19
  124. wandb/sdk/internal/_generated/base.py +0 -226
  125. wandb/sdk/internal/_generated/typing_compat.py +0 -14
  126. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/WHEEL +0 -0
  127. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/entry_points.txt +0 -0
  128. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/licenses/LICENSE +0 -0
@@ -16,19 +16,46 @@ if TYPE_CHECKING: # pragma: no cover
16
16
 
17
17
 
18
18
  class Html(BatchableMedia):
19
- """Wandb class for arbitrary html.
20
-
21
- Args:
22
- data: (string or io object) HTML to display in wandb
23
- inject: (boolean) Add a stylesheet to the HTML object. If set
24
- to False the HTML will pass through unchanged.
25
- """
19
+ """A class for logging HTML content to W&B."""
26
20
 
27
21
  _log_type = "html-file"
28
22
 
29
- def __init__(self, data: Union[str, "TextIO"], inject: bool = True) -> None:
23
+ def __init__(
24
+ self,
25
+ data: Union[str, "TextIO"],
26
+ inject: bool = True,
27
+ data_is_not_path: bool = False,
28
+ ) -> None:
29
+ """Creates a W&B HTML object.
30
+
31
+ It can be initialized by providing a path to a file:
32
+ ```
33
+ with wandb.init() as run:
34
+ run.log({"html": wandb.Html("./index.html")})
35
+ ```
36
+
37
+ Alternatively, it can be initialized by providing literal HTML,
38
+ in either a string or IO object:
39
+ ```
40
+ with wandb.init() as run:
41
+ run.log({"html": wandb.Html("<h1>Hello, world!</h1>")})
42
+ ```
43
+
44
+ Args:
45
+ data:
46
+ A string that is a path to a file with the extension ".html",
47
+ or a string or IO object containing literal HTML.
48
+ inject: Add a stylesheet to the HTML object. If set
49
+ to False the HTML will pass through unchanged.
50
+ data_is_not_path: If set to False, the data will be
51
+ treated as a path to a file.
52
+ """
30
53
  super().__init__()
31
- data_is_path = isinstance(data, str) and os.path.exists(data)
54
+ data_is_path = (
55
+ isinstance(data, str)
56
+ and os.path.isfile(data)
57
+ and os.path.splitext(data)[1] == ".html"
58
+ ) and not data_is_not_path
32
59
  data_path = ""
33
60
  if data_is_path:
34
61
  assert isinstance(data, str)
@@ -34,8 +34,8 @@ if TYPE_CHECKING: # pragma: no cover
34
34
  TorchTensorType = Union["torch.Tensor", "torch.Variable"]
35
35
 
36
36
 
37
- def _server_accepts_image_filenames() -> bool:
38
- if util._is_offline():
37
+ def _server_accepts_image_filenames(run: "LocalRun") -> bool:
38
+ if run.offline:
39
39
  return True
40
40
 
41
41
  # Newer versions of wandb accept large image filenames arrays
@@ -51,15 +51,15 @@ def _server_accepts_image_filenames() -> bool:
51
51
  return accepts_image_filenames
52
52
 
53
53
 
54
- def _server_accepts_artifact_path() -> bool:
55
- from wandb.util import parse_version
54
+ def _server_accepts_artifact_path(run: "LocalRun") -> bool:
55
+ if run.offline:
56
+ return False
57
+
58
+ max_cli_version = util._get_max_cli_version()
59
+ if max_cli_version is None:
60
+ return False
56
61
 
57
- target_version = "0.12.14"
58
- max_cli_version = util._get_max_cli_version() if not util._is_offline() else None
59
- accepts_artifact_path: bool = max_cli_version is not None and parse_version(
60
- target_version
61
- ) <= parse_version(max_cli_version)
62
- return accepts_artifact_path
62
+ return util.parse_version("0.12.14") <= util.parse_version(max_cli_version)
63
63
 
64
64
 
65
65
  class Image(BatchableMedia):
@@ -401,7 +401,7 @@ class Image(BatchableMedia):
401
401
  )
402
402
 
403
403
  if (
404
- not _server_accepts_artifact_path()
404
+ not _server_accepts_artifact_path(run)
405
405
  or self._get_artifact_entry_ref_url() is None
406
406
  ):
407
407
  super().bind_to_run(run, key, step, id_, ignore_copy_err=ignore_copy_err)
@@ -575,7 +575,7 @@ class Image(BatchableMedia):
575
575
  "format": format,
576
576
  "count": num_images_to_log,
577
577
  }
578
- if _server_accepts_image_filenames():
578
+ if _server_accepts_image_filenames(run):
579
579
  meta["filenames"] = [
580
580
  obj.get("path", obj.get("artifact_path")) for obj in jsons
581
581
  ]
@@ -480,6 +480,11 @@ class Table(Media):
480
480
  max_rows = Table.MAX_ROWS
481
481
  n_rows = len(self.data)
482
482
  if n_rows > max_rows and warn:
483
+ # NOTE: Never raises for reinit="create_new" runs.
484
+ # Since this is called by bind_to_run(), this can be fixed by
485
+ # propagating the run. It cannot be fixed for to_json() calls
486
+ # that are given an artifact, other than by deferring to singleton
487
+ # settings.
483
488
  if wandb.run and (
484
489
  wandb.run.settings.table_raise_on_max_row_limit_exceeded
485
490
  or wandb.run.settings.strict
@@ -431,6 +431,8 @@ class Trace:
431
431
  name: The name of the trace to be logged
432
432
  """
433
433
  trace_tree = WBTraceTree(self._span, self._model_dict)
434
+ # NOTE: Does not work for reinit="create_new" runs.
435
+ # This method should be deprecated and users should call run.log().
434
436
  assert (
435
437
  wandb.run is not None
436
438
  ), "You must call wandb.init() before logging a trace"
@@ -101,7 +101,7 @@ def val_to_json(
101
101
 
102
102
  items = _prune_max_seq(val)
103
103
 
104
- if _server_accepts_image_filenames():
104
+ if _server_accepts_image_filenames(run):
105
105
  for item in items:
106
106
  item.bind_to_run(
107
107
  run=run,
@@ -1,11 +1,12 @@
1
+ import functools
1
2
  import logging
2
3
  import os
3
4
  from io import BytesIO
4
- from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Type, Union
5
+ from typing import TYPE_CHECKING, Any, Optional, Sequence, Type, Union
5
6
 
6
7
  import wandb
7
8
  from wandb import util
8
- from wandb.sdk.lib import filesystem, runid
9
+ from wandb.sdk.lib import filesystem, printer, printer_asyncio, runid
9
10
 
10
11
  from . import _dtypes
11
12
  from ._private import MEDIA_TMP
@@ -134,7 +135,11 @@ class Video(BatchableMedia):
134
135
  "wandb.Video accepts a file path or numpy like data as input"
135
136
  )
136
137
  fps = fps or 4
137
- self.encode(fps=fps)
138
+ printer_asyncio.run_async_with_spinner(
139
+ printer.new_printer(),
140
+ "Encoding video...",
141
+ functools.partial(self.encode, fps=fps),
142
+ )
138
143
 
139
144
  def encode(self, fps: int = 4) -> None:
140
145
  # import ImageSequenceClip from the appropriate MoviePy module
@@ -152,29 +157,12 @@ class Video(BatchableMedia):
152
157
  filename = os.path.join(
153
158
  MEDIA_TMP.name, runid.generate_id() + "." + self._format
154
159
  )
155
- if TYPE_CHECKING:
156
- kwargs: Dict[str, Optional[bool]] = {}
157
- try: # older versions of moviepy do not support logger argument
158
- kwargs = {"logger": None}
159
- if self._format == "gif":
160
- write_gif_with_image_io(clip, filename)
161
- else:
162
- clip.write_videofile(filename, **kwargs)
163
- except TypeError:
164
- try: # even older versions of moviepy do not support progress_bar argument
165
- kwargs = {"verbose": False, "progress_bar": False}
166
- if self._format == "gif":
167
- clip.write_gif(filename, **kwargs)
168
- else:
169
- clip.write_videofile(filename, **kwargs)
170
- except TypeError:
171
- kwargs = {
172
- "verbose": False,
173
- }
174
- if self._format == "gif":
175
- clip.write_gif(filename, **kwargs)
176
- else:
177
- clip.write_videofile(filename, **kwargs)
160
+
161
+ if self._format == "gif":
162
+ write_gif_with_image_io(clip, filename)
163
+ else:
164
+ clip.write_videofile(filename, logger=None)
165
+
178
166
  self._set_file(filename, is_tmp=True)
179
167
 
180
168
  @classmethod
@@ -173,6 +173,8 @@ class InterfaceBase:
173
173
  self._make_config(data=config_dict, obj=proto_run.config)
174
174
  if run._telemetry_obj:
175
175
  proto_run.telemetry.MergeFrom(run._telemetry_obj)
176
+ if run._start_runtime:
177
+ proto_run.runtime = run._start_runtime
176
178
  return proto_run
177
179
 
178
180
  def publish_run(self, run: "Run") -> None:
@@ -18,12 +18,13 @@ def torch_trace_handler():
18
18
  torch.profiler.profile(..., on_trace_ready=wandb.profiler.torch_trace_handler())
19
19
  ```
20
20
 
21
- Calling this function ensures that profiler charts & tables can be viewed in your run dashboard
22
- on wandb.ai.
21
+ Calling this function ensures that profiler charts & tables can be viewed in
22
+ your run dashboard on wandb.ai.
23
23
 
24
- Please note that `wandb.init()` must be called before this function is invoked.
25
- The PyTorch (torch) version must also be at least 1.9, in order to ensure stability
26
- of their Profiler API.
24
+ Please note that `wandb.init()` must be called before this function is
25
+ invoked, and the reinit setting must not be set to "create_new". The PyTorch
26
+ (torch) version must also be at least 1.9, in order to ensure stability of
27
+ their Profiler API.
27
28
 
28
29
  Args:
29
30
  None
wandb/sdk/internal/run.py CHANGED
@@ -5,21 +5,28 @@ Semi-stubbed run for internal process use.
5
5
 
6
6
  """
7
7
 
8
- from wandb._globals import _datatypes_set_callback
8
+ import sys
9
9
 
10
- from .. import wandb_run
10
+ if sys.version_info >= (3, 12):
11
+ from typing import override
12
+ else:
13
+ from typing_extensions import override
14
+
15
+ from wandb.sdk import wandb_run
11
16
 
12
17
 
13
18
  class InternalRun(wandb_run.Run):
14
19
  def __init__(self, run_obj, settings, datatypes_cb):
15
20
  super().__init__(settings=settings)
16
21
  self._run_obj = run_obj
22
+ self._datatypes_cb = datatypes_cb
17
23
 
18
- # TODO: This overwrites what's done in the constructor of wandb_run.Run.
19
- # We really want a common interface for wandb_run.Run and InternalRun.
20
- _datatypes_set_callback(datatypes_cb)
21
-
24
+ @override
22
25
  def _set_backend(self, backend):
23
26
  # This type of run object can't have a backend
24
27
  # or do any writes.
25
28
  pass
29
+
30
+ @override
31
+ def _publish_file(self, fname: str) -> None:
32
+ self._datatypes_cb(fname)
wandb/sdk/lib/apikey.py CHANGED
@@ -83,6 +83,25 @@ def get_netrc_file_path() -> str:
83
83
  return os.path.join(os.path.expanduser("~"), netrc_file)
84
84
 
85
85
 
86
+ def _api_key_prompt_str(app_url: str, referrer: str | None = None) -> str:
87
+ """Generate a prompt string for API key authorization.
88
+
89
+ Creates a URL string that directs users to the authorization page where they
90
+ can find their API key.
91
+
92
+ Args:
93
+ app_url: The base URL of the W&B application.
94
+ referrer: Optional referrer parameter to include in the URL.
95
+
96
+ Returns:
97
+ A formatted string with instructions and the authorization URL.
98
+ """
99
+ ref = ""
100
+ if referrer:
101
+ ref = f"?ref={referrer}"
102
+ return f"You can find your API key in your browser here: {app_url}/authorize{ref}"
103
+
104
+
86
105
  def prompt_api_key( # noqa: C901
87
106
  settings: Settings,
88
107
  api: InternalApi | None = None,
@@ -91,6 +110,7 @@ def prompt_api_key( # noqa: C901
91
110
  no_offline: bool = False,
92
111
  no_create: bool = False,
93
112
  local: bool = False,
113
+ referrer: str | None = None,
94
114
  ) -> str | bool | None:
95
115
  """Prompt for api key.
96
116
 
@@ -149,7 +169,10 @@ def prompt_api_key( # noqa: C901
149
169
  key = browser_callback(signup=True) if browser_callback else None
150
170
 
151
171
  if not key:
152
- wandb.termlog(f"Create an account here: {app_url}/authorize?signup=true")
172
+ ref = f"&ref={referrer}" if referrer else ""
173
+ wandb.termlog(
174
+ f"Create an account here: {app_url}/authorize?signup=true{ref}"
175
+ )
153
176
  key = input_callback(api_ask).strip()
154
177
  elif result == LOGIN_CHOICE_EXISTS:
155
178
  key = browser_callback() if browser_callback else None
@@ -164,9 +187,7 @@ def prompt_api_key( # noqa: C901
164
187
  f"Logging into {host}. (Learn how to deploy a W&B server "
165
188
  f"locally: {url_registry.url('wandb-server')})"
166
189
  )
167
- wandb.termlog(
168
- f"You can find your API key in your browser here: {app_url}/authorize"
169
- )
190
+ wandb.termlog(_api_key_prompt_str(app_url, referrer))
170
191
  key = input_callback(api_ask).strip()
171
192
  elif result == LOGIN_CHOICE_NOTTY:
172
193
  # TODO: Needs refactor as this needs to be handled by caller
@@ -53,7 +53,7 @@ class _Runner:
53
53
  """
54
54
 
55
55
  def __init__(self) -> None:
56
- self._lock = threading.Condition()
56
+ self._lock = threading.Lock()
57
57
 
58
58
  self._is_cancelled = False
59
59
  self._started = False
@@ -1,42 +1,33 @@
1
- __all__ = ["deprecate", "Deprecated"]
1
+ from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Optional, Tuple
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  import wandb
6
- from wandb.proto.wandb_deprecated import DEPRECATED_FEATURES, Deprecated
7
- from wandb.proto.wandb_telemetry_pb2 import Deprecated as TelemetryDeprecated
6
+ from wandb.proto.wandb_deprecated import DEPRECATED_FEATURES
7
+ from wandb.sdk.lib import telemetry
8
8
 
9
- # avoid cycle, use string type reference
9
+ # Necessary to break import cycle.
10
10
  if TYPE_CHECKING:
11
- from .. import wandb_run
12
-
13
-
14
- deprecated_field_names: Tuple[str, ...] = tuple(
15
- str(v) for k, v in Deprecated.__dict__.items() if not k.startswith("_")
16
- )
11
+ from wandb import wandb_run
17
12
 
18
13
 
19
14
  def deprecate(
20
15
  field_name: DEPRECATED_FEATURES,
21
16
  warning_message: str,
22
- run: Optional["wandb_run.Run"] = None,
17
+ run: wandb_run.Run | None = None,
23
18
  ) -> None:
24
19
  """Warn the user that a feature has been deprecated.
25
20
 
26
- Also stores the information about the event in telemetry.
21
+ If a run is provided, the given field on its telemetry is updated.
22
+ Otherwise, the global run is used.
27
23
 
28
24
  Args:
29
- field_name: The name of the feature that has been deprecated.
30
- Defined in wandb/proto/wandb_telemetry.proto::Deprecated
25
+ field_name: The field on the Deprecated proto for this deprecation.
31
26
  warning_message: The message to display to the user.
32
- run: The run to whose telemetry the event will be added.
27
+ run: The run whose telemetry to update.
33
28
  """
34
- known_fields = TelemetryDeprecated.DESCRIPTOR.fields_by_name.keys()
35
- if field_name not in known_fields:
36
- raise ValueError(
37
- f"Unknown field name: {field_name}. Known fields: {known_fields}"
38
- )
39
29
  _run = run or wandb.run
40
- with wandb.wandb_lib.telemetry.context(run=_run) as tel: # type: ignore[attr-defined]
30
+ with telemetry.context(run=_run) as tel:
41
31
  setattr(tel.deprecated, field_name, True)
32
+
42
33
  wandb.termwarn(warning_message, repeat=False)
wandb/sdk/lib/disabled.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import Any
2
2
 
3
+ from wandb.proto.wandb_deprecated import Deprecated
3
4
  from wandb.sdk.lib import deprecate
4
5
 
5
6
 
@@ -23,7 +24,7 @@ class RunDisabled:
23
24
 
24
25
  def __getattr__(self, name: str) -> Any:
25
26
  deprecate.deprecate(
26
- field_name=deprecate.Deprecated.run_disabled,
27
+ field_name=Deprecated.run_disabled,
27
28
  warning_message="RunDisabled is deprecated and is a no-op. "
28
29
  '`wandb.init(mode="disabled")` now returns and instance of `wandb.sdk.wandb_run.Run`.',
29
30
  )
wandb/sdk/lib/printer.py CHANGED
@@ -20,6 +20,7 @@ import click
20
20
 
21
21
  import wandb
22
22
  from wandb.errors import term
23
+ from wandb.sdk import wandb_setup
23
24
 
24
25
  from . import ipython, sparkline
25
26
 
@@ -98,12 +99,21 @@ _JUPYTER_PANEL_STYLES = """
98
99
  """
99
100
 
100
101
 
101
- def new_printer() -> Printer:
102
- """Returns a new printer based on the environment we're in."""
102
+ def new_printer(settings: wandb.Settings | None = None) -> Printer:
103
+ """Returns a printer appropriate for the environment we're in.
104
+
105
+ Args:
106
+ settings: The settings of a run. If not provided and `wandb.setup()`
107
+ has been called, then global settings are used. Otherwise,
108
+ settings (such as silent mode) are ignored.
109
+ """
110
+ if not settings and (singleton := wandb_setup.singleton()):
111
+ settings = singleton.settings
112
+
103
113
  if ipython.in_jupyter():
104
- return _PrinterJupyter()
114
+ return _PrinterJupyter(settings=settings)
105
115
  else:
106
- return _PrinterTerm()
116
+ return _PrinterTerm(settings=settings)
107
117
 
108
118
 
109
119
  class Printer(abc.ABC):
@@ -281,13 +291,18 @@ class DynamicText(abc.ABC):
281
291
 
282
292
 
283
293
  class _PrinterTerm(Printer):
284
- def __init__(self) -> None:
294
+ def __init__(self, *, settings: wandb.Settings | None) -> None:
285
295
  super().__init__()
296
+ self._settings = settings
286
297
  self._progress = itertools.cycle(["-", "\\", "|", "/"])
287
298
 
288
299
  @override
289
300
  @contextlib.contextmanager
290
301
  def dynamic_text(self) -> Iterator[DynamicText | None]:
302
+ if self._settings and self._settings.silent:
303
+ yield None
304
+ return
305
+
291
306
  with term.dynamic_text() as handle:
292
307
  if not handle:
293
308
  yield None
@@ -301,6 +316,9 @@ class _PrinterTerm(Printer):
301
316
  *,
302
317
  level: str | int | None = None,
303
318
  ) -> None:
319
+ if self._settings and self._settings.silent:
320
+ return
321
+
304
322
  text = "\n".join(text) if isinstance(text, (list, tuple)) else text
305
323
  self._display_fn_mapping(level)(text)
306
324
 
@@ -323,10 +341,16 @@ class _PrinterTerm(Printer):
323
341
 
324
342
  @override
325
343
  def progress_update(self, text: str, percent_done: float | None = None) -> None:
344
+ if self._settings and self._settings.silent:
345
+ return
346
+
326
347
  wandb.termlog(f"{next(self._progress)} {text}", newline=False)
327
348
 
328
349
  @override
329
350
  def progress_close(self, text: str | None = None) -> None:
351
+ if self._settings and self._settings.silent:
352
+ return
353
+
330
354
  text = text or " " * 79
331
355
  wandb.termlog(text)
332
356
 
@@ -422,8 +446,9 @@ class _DynamicTermText(DynamicText):
422
446
 
423
447
 
424
448
  class _PrinterJupyter(Printer):
425
- def __init__(self) -> None:
449
+ def __init__(self, *, settings: wandb.Settings | None) -> None:
426
450
  super().__init__()
451
+ self._settings = settings
427
452
  self._progress = ipython.jupyter_progress_bar()
428
453
 
429
454
  from IPython import display
@@ -433,6 +458,10 @@ class _PrinterJupyter(Printer):
433
458
  @override
434
459
  @contextlib.contextmanager
435
460
  def dynamic_text(self) -> Iterator[DynamicText | None]:
461
+ if self._settings and self._settings.silent:
462
+ yield None
463
+ return
464
+
436
465
  handle = self._ipython_display.display(
437
466
  self._ipython_display.HTML(""),
438
467
  display_id=True,
@@ -452,7 +481,7 @@ class _PrinterJupyter(Printer):
452
481
  *,
453
482
  level: str | int | None = None,
454
483
  ) -> None:
455
- if wandb.run and wandb.run._settings.silent:
484
+ if self._settings and self._settings.silent:
456
485
  return
457
486
 
458
487
  text = "<br>".join(text) if isinstance(text, (list, tuple)) else text
@@ -507,7 +536,7 @@ class _PrinterJupyter(Printer):
507
536
  text: str,
508
537
  percent_done: float | None = None,
509
538
  ) -> None:
510
- if not self._progress:
539
+ if (self._settings and self._settings.silent) or not self._progress:
511
540
  return
512
541
 
513
542
  if percent_done is None:
@@ -0,0 +1,46 @@
1
+ import asyncio
2
+ from typing import Callable, TypeVar
3
+
4
+ from wandb.sdk.lib import asyncio_compat, printer
5
+
6
+ _T = TypeVar("_T")
7
+
8
+
9
+ def run_async_with_spinner(
10
+ spinner_printer: printer.Printer,
11
+ text: str,
12
+ func: Callable[[], _T],
13
+ ) -> _T:
14
+ """Run a slow function while displaying a loading icon.
15
+
16
+ Args:
17
+ spinner_printer: The printer to use to display text.
18
+ text: The text to display next to the spinner while the function runs.
19
+ func: The function to run.
20
+
21
+ Returns:
22
+ The result of func.
23
+ """
24
+
25
+ async def _loop_run_with_spinner() -> _T:
26
+ func_running = asyncio.Event()
27
+
28
+ async def update_spinner() -> None:
29
+ tick = 0
30
+ with spinner_printer.dynamic_text() as text_area:
31
+ if text_area:
32
+ while not func_running.is_set():
33
+ spinner = spinner_printer.loading_symbol(tick)
34
+ text_area.set_text(f"{spinner} {text}")
35
+ tick += 1
36
+ await asyncio.sleep(0.1)
37
+ else:
38
+ spinner_printer.display(text)
39
+
40
+ async with asyncio_compat.open_task_group() as group:
41
+ group.start_soon(update_spinner())
42
+ res = await asyncio.get_running_loop().run_in_executor(None, func)
43
+ func_running.set()
44
+ return res
45
+
46
+ return asyncio_compat.run(_loop_run_with_spinner)
wandb/sdk/lib/redirect.py CHANGED
@@ -534,12 +534,15 @@ class StreamWrapper(RedirectBase):
534
534
  self,
535
535
  src: Literal["stdout", "stderr"],
536
536
  cbs: Iterable[Callable[[str], None]] = (),
537
+ *,
538
+ flush_periodically: bool,
537
539
  ) -> None:
538
540
  super().__init__(src=src, cbs=cbs)
539
541
  self._uninstall: Callable[[], None] | None = None
540
542
  self._emulator = TerminalEmulator()
541
543
  self._queue: queue.Queue[str] = queue.Queue()
542
544
  self._stopped = threading.Event()
545
+ self._flush_periodically = flush_periodically
543
546
 
544
547
  def _emulator_write(self) -> None:
545
548
  while True:
@@ -600,7 +603,7 @@ class StreamWrapper(RedirectBase):
600
603
  self._emulator_write_thread.daemon = True
601
604
  self._emulator_write_thread.start()
602
605
 
603
- if not wandb.run or wandb.run._settings.mode == "online":
606
+ if self._flush_periodically:
604
607
  self._callback_thread = threading.Thread(target=self._callback)
605
608
  self._callback_thread.daemon = True
606
609
  self._callback_thread.start()
@@ -732,10 +735,11 @@ _redirects: dict[str, Redirect | None] = {"stdout": None, "stderr": None}
732
735
  class Redirect(RedirectBase):
733
736
  """Redirect low level file descriptors."""
734
737
 
735
- def __init__(self, src, cbs=()):
738
+ def __init__(self, src, cbs=(), *, flush_periodically: bool):
736
739
  super().__init__(src=src, cbs=cbs)
737
740
  self._installed = False
738
741
  self._emulator = TerminalEmulator()
742
+ self._flush_periodically = flush_periodically
739
743
 
740
744
  def _pipe(self):
741
745
  if pty:
@@ -767,7 +771,7 @@ class Redirect(RedirectBase):
767
771
  self._emulator_write_thread = threading.Thread(target=self._emulator_write)
768
772
  self._emulator_write_thread.daemon = True
769
773
  self._emulator_write_thread.start()
770
- if not wandb.run or wandb.run._settings.mode == "online":
774
+ if self._flush_periodically:
771
775
  self._callback_thread = threading.Thread(target=self._callback)
772
776
  self._callback_thread.daemon = True
773
777
  self._callback_thread.start()
@@ -776,8 +780,9 @@ class Redirect(RedirectBase):
776
780
  if not self._installed:
777
781
  return
778
782
  self._installed = False
779
- # If the user printed a very long string (millions of chars) right before wandb.finish(),
780
- # it will take a while for it to reach pipe relay. 1 second is enough time for ~5 million chars.
783
+ # If the user printed a very long string (millions of chars) right
784
+ # before run.finish(), it will take a while for it to reach pipe relay.
785
+ # 1 second is enough time for ~5 million chars.
781
786
  time.sleep(1)
782
787
  self._stopped.set()
783
788
  os.dup2(self._orig_src_fd, self.src_fd)