wandb 0.21.0__py3-none-win_amd64.whl → 0.21.2__py3-none-win_amd64.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 (153) hide show
  1. wandb/__init__.py +16 -14
  2. wandb/__init__.pyi +427 -450
  3. wandb/agents/pyagent.py +41 -12
  4. wandb/analytics/sentry.py +7 -2
  5. wandb/apis/importers/mlflow.py +1 -1
  6. wandb/apis/public/__init__.py +1 -1
  7. wandb/apis/public/api.py +525 -360
  8. wandb/apis/public/artifacts.py +207 -13
  9. wandb/apis/public/automations.py +19 -3
  10. wandb/apis/public/files.py +172 -33
  11. wandb/apis/public/history.py +67 -15
  12. wandb/apis/public/integrations.py +25 -2
  13. wandb/apis/public/jobs.py +90 -2
  14. wandb/apis/public/projects.py +130 -79
  15. wandb/apis/public/query_generator.py +11 -1
  16. wandb/apis/public/registries/_utils.py +14 -16
  17. wandb/apis/public/registries/registries_search.py +183 -304
  18. wandb/apis/public/reports.py +96 -15
  19. wandb/apis/public/runs.py +299 -105
  20. wandb/apis/public/sweeps.py +222 -22
  21. wandb/apis/public/teams.py +41 -4
  22. wandb/apis/public/users.py +45 -4
  23. wandb/automations/_generated/delete_automation.py +1 -3
  24. wandb/automations/_generated/enums.py +13 -11
  25. wandb/beta/workflows.py +66 -30
  26. wandb/bin/gpu_stats.exe +0 -0
  27. wandb/bin/wandb-core +0 -0
  28. wandb/cli/cli.py +127 -3
  29. wandb/env.py +8 -0
  30. wandb/errors/errors.py +4 -1
  31. wandb/integration/lightning/fabric/logger.py +3 -4
  32. wandb/integration/metaflow/__init__.py +6 -0
  33. wandb/integration/metaflow/data_pandas.py +74 -0
  34. wandb/integration/metaflow/data_pytorch.py +75 -0
  35. wandb/integration/metaflow/data_sklearn.py +76 -0
  36. wandb/integration/metaflow/errors.py +13 -0
  37. wandb/integration/metaflow/metaflow.py +167 -223
  38. wandb/integration/openai/fine_tuning.py +1 -2
  39. wandb/integration/weave/__init__.py +6 -0
  40. wandb/integration/weave/interface.py +49 -0
  41. wandb/integration/weave/weave.py +63 -0
  42. wandb/jupyter.py +5 -5
  43. wandb/plot/custom_chart.py +30 -7
  44. wandb/proto/v3/wandb_internal_pb2.py +281 -280
  45. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  46. wandb/proto/v4/wandb_internal_pb2.py +280 -280
  47. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  48. wandb/proto/v5/wandb_internal_pb2.py +280 -280
  49. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  50. wandb/proto/v6/wandb_internal_pb2.py +280 -280
  51. wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
  52. wandb/proto/wandb_deprecated.py +6 -0
  53. wandb/sdk/artifacts/_factories.py +17 -0
  54. wandb/sdk/artifacts/_generated/__init__.py +221 -13
  55. wandb/sdk/artifacts/_generated/artifact_by_id.py +17 -0
  56. wandb/sdk/artifacts/_generated/artifact_by_name.py +22 -0
  57. wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +43 -0
  58. wandb/sdk/artifacts/_generated/artifact_created_by.py +47 -0
  59. wandb/sdk/artifacts/_generated/artifact_file_urls.py +22 -0
  60. wandb/sdk/artifacts/_generated/artifact_type.py +31 -0
  61. wandb/sdk/artifacts/_generated/artifact_used_by.py +43 -0
  62. wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +26 -0
  63. wandb/sdk/artifacts/_generated/delete_artifact.py +28 -0
  64. wandb/sdk/artifacts/_generated/enums.py +5 -0
  65. wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +38 -0
  66. wandb/sdk/artifacts/_generated/fetch_registries.py +32 -0
  67. wandb/sdk/artifacts/_generated/fragments.py +279 -41
  68. wandb/sdk/artifacts/_generated/link_artifact.py +6 -0
  69. wandb/sdk/artifacts/_generated/operations.py +654 -51
  70. wandb/sdk/artifacts/_generated/registry_collections.py +34 -0
  71. wandb/sdk/artifacts/_generated/registry_versions.py +34 -0
  72. wandb/sdk/artifacts/_generated/unlink_artifact.py +25 -0
  73. wandb/sdk/artifacts/_graphql_fragments.py +3 -86
  74. wandb/sdk/artifacts/_internal_artifact.py +19 -8
  75. wandb/sdk/artifacts/_validators.py +14 -4
  76. wandb/sdk/artifacts/artifact.py +512 -618
  77. wandb/sdk/artifacts/artifact_file_cache.py +10 -6
  78. wandb/sdk/artifacts/artifact_manifest.py +10 -9
  79. wandb/sdk/artifacts/artifact_manifest_entry.py +9 -10
  80. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +5 -3
  81. wandb/sdk/artifacts/storage_handlers/http_handler.py +1 -1
  82. wandb/sdk/artifacts/storage_handlers/s3_handler.py +1 -1
  83. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +1 -1
  84. wandb/sdk/data_types/audio.py +38 -10
  85. wandb/sdk/data_types/base_types/media.py +6 -56
  86. wandb/sdk/data_types/graph.py +48 -14
  87. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
  88. wandb/sdk/data_types/helper_types/image_mask.py +1 -3
  89. wandb/sdk/data_types/histogram.py +34 -21
  90. wandb/sdk/data_types/html.py +35 -12
  91. wandb/sdk/data_types/image.py +104 -68
  92. wandb/sdk/data_types/molecule.py +32 -19
  93. wandb/sdk/data_types/object_3d.py +36 -17
  94. wandb/sdk/data_types/plotly.py +18 -5
  95. wandb/sdk/data_types/saved_model.py +4 -6
  96. wandb/sdk/data_types/table.py +59 -30
  97. wandb/sdk/data_types/video.py +53 -26
  98. wandb/sdk/integration_utils/auto_logging.py +2 -2
  99. wandb/sdk/interface/interface_queue.py +1 -4
  100. wandb/sdk/interface/interface_shared.py +26 -37
  101. wandb/sdk/interface/interface_sock.py +24 -14
  102. wandb/sdk/internal/internal_api.py +6 -0
  103. wandb/sdk/internal/job_builder.py +6 -0
  104. wandb/sdk/internal/settings_static.py +2 -3
  105. wandb/sdk/launch/agent/agent.py +8 -1
  106. wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
  107. wandb/sdk/launch/create_job.py +15 -2
  108. wandb/sdk/launch/inputs/internal.py +3 -4
  109. wandb/sdk/launch/inputs/schema.py +1 -0
  110. wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
  111. wandb/sdk/launch/runner/kubernetes_runner.py +323 -1
  112. wandb/sdk/launch/sweeps/scheduler.py +2 -3
  113. wandb/sdk/lib/asyncio_compat.py +19 -16
  114. wandb/sdk/lib/asyncio_manager.py +252 -0
  115. wandb/sdk/lib/deprecate.py +1 -7
  116. wandb/sdk/lib/disabled.py +1 -1
  117. wandb/sdk/lib/hashutil.py +27 -5
  118. wandb/sdk/lib/module.py +7 -13
  119. wandb/sdk/lib/printer.py +2 -2
  120. wandb/sdk/lib/printer_asyncio.py +3 -1
  121. wandb/sdk/lib/progress.py +0 -19
  122. wandb/sdk/lib/retry.py +185 -78
  123. wandb/sdk/lib/service/service_client.py +106 -0
  124. wandb/sdk/lib/service/service_connection.py +20 -26
  125. wandb/sdk/lib/service/service_token.py +30 -13
  126. wandb/sdk/mailbox/mailbox.py +13 -5
  127. wandb/sdk/mailbox/mailbox_handle.py +22 -13
  128. wandb/sdk/mailbox/response_handle.py +42 -106
  129. wandb/sdk/mailbox/wait_with_progress.py +7 -42
  130. wandb/sdk/wandb_init.py +77 -116
  131. wandb/sdk/wandb_login.py +19 -15
  132. wandb/sdk/wandb_metric.py +2 -0
  133. wandb/sdk/wandb_run.py +497 -469
  134. wandb/sdk/wandb_settings.py +145 -4
  135. wandb/sdk/wandb_setup.py +204 -124
  136. wandb/sdk/wandb_sweep.py +14 -13
  137. wandb/sdk/wandb_watch.py +4 -6
  138. wandb/sync/sync.py +10 -0
  139. wandb/util.py +58 -1
  140. wandb/wandb_run.py +1 -2
  141. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/METADATA +1 -1
  142. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/RECORD +145 -129
  143. wandb/sdk/interface/interface_relay.py +0 -38
  144. wandb/sdk/interface/router.py +0 -89
  145. wandb/sdk/interface/router_queue.py +0 -43
  146. wandb/sdk/interface/router_relay.py +0 -50
  147. wandb/sdk/interface/router_sock.py +0 -32
  148. wandb/sdk/lib/sock_client.py +0 -236
  149. wandb/vendor/pynvml/__init__.py +0 -0
  150. wandb/vendor/pynvml/pynvml.py +0 -4779
  151. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/WHEEL +0 -0
  152. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/entry_points.txt +0 -0
  153. {wandb-0.21.0.dist-info → wandb-0.21.2.dist-info}/licenses/LICENSE +0 -0
@@ -9,6 +9,7 @@ import os
9
9
  import shutil
10
10
  import subprocess
11
11
  import sys
12
+ from functools import lru_cache
12
13
  from pathlib import Path
13
14
  from tempfile import NamedTemporaryFile
14
15
  from typing import IO, ContextManager, Iterator, Protocol
@@ -236,12 +237,15 @@ class ArtifactFileCache:
236
237
  ) from e
237
238
 
238
239
 
239
- _artifact_file_cache: ArtifactFileCache | None = None
240
+ # Memo `ArtifactFileCache` instances while avoiding reliance on global
241
+ # variable(s). Notes:
242
+ # - @lru_cache should be thread-safe.
243
+ # - We don't memoize `get_artifact_file_cache` directly, as the cache_dir
244
+ # may change at runtime. This is likely rare in practice, though.
245
+ @lru_cache(maxsize=1)
246
+ def _build_artifact_file_cache(cache_dir: StrPath) -> ArtifactFileCache:
247
+ return ArtifactFileCache(cache_dir)
240
248
 
241
249
 
242
250
  def get_artifact_file_cache() -> ArtifactFileCache:
243
- global _artifact_file_cache
244
- cache_dir = env.get_cache_dir() / "artifacts"
245
- if _artifact_file_cache is None or _artifact_file_cache._cache_dir != cache_dir:
246
- _artifact_file_cache = ArtifactFileCache(cache_dir)
247
- return _artifact_file_cache
251
+ return _build_artifact_file_cache(env.get_cache_dir() / "artifacts")
@@ -50,10 +50,12 @@ class ArtifactManifest:
50
50
 
51
51
  def add_entry(self, entry: ArtifactManifestEntry, overwrite: bool = False) -> None:
52
52
  path = entry.path
53
- if not overwrite:
54
- prev_entry = self.entries.get(path)
55
- if prev_entry and (entry.digest != prev_entry.digest):
56
- raise ValueError(f"Cannot add the same path twice: {path!r}")
53
+ if (
54
+ (not overwrite)
55
+ and (old_entry := self.entries.get(path))
56
+ and (entry.digest != old_entry.digest)
57
+ ):
58
+ raise ValueError(f"Cannot add the same path twice: {path!r}")
57
59
  self.entries[path] = entry
58
60
 
59
61
  def remove_entry(self, entry: ArtifactManifestEntry) -> None:
@@ -67,9 +69,8 @@ class ArtifactManifest:
67
69
 
68
70
  def get_entries_in_directory(self, directory: str) -> list[ArtifactManifestEntry]:
69
71
  return [
70
- self.entries[entry_key]
71
- for entry_key in self.entries
72
- if entry_key.startswith(
73
- directory + "/"
74
- ) # entries use forward slash even for windows
72
+ entry
73
+ for key, entry in self.entries.items()
74
+ # entry keys (paths) use forward slash even for windows
75
+ if key.startswith(f"{directory}/")
75
76
  ]
@@ -40,6 +40,9 @@ if TYPE_CHECKING:
40
40
  local_path: str
41
41
 
42
42
 
43
+ _WB_ARTIFACT_SCHEME = "wandb-artifact"
44
+
45
+
43
46
  class ArtifactManifestEntry:
44
47
  """A single entry in an artifact manifest."""
45
48
 
@@ -221,15 +224,11 @@ class ArtifactManifestEntry:
221
224
  derived_artifact.add_reference(ref_url)
222
225
  ```
223
226
  """
224
- if self._parent_artifact is None:
225
- raise NotImplementedError
226
- assert self._parent_artifact.id is not None
227
- return (
228
- "wandb-artifact://"
229
- + b64_to_hex_id(B64MD5(self._parent_artifact.id))
230
- + "/"
231
- + self.path
232
- )
227
+ if (parent_artifact := self.parent_artifact()) is None:
228
+ raise ValueError("Parent artifact is not set")
229
+ elif (parent_id := parent_artifact.id) is None:
230
+ raise ValueError("Parent artifact ID is not set")
231
+ return f"{_WB_ARTIFACT_SCHEME}://{b64_to_hex_id(B64MD5(parent_id))}/{self.path}"
233
232
 
234
233
  def to_json(self) -> ArtifactManifestEntryDict:
235
234
  contents: ArtifactManifestEntryDict = {
@@ -251,7 +250,7 @@ class ArtifactManifestEntry:
251
250
  return contents
252
251
 
253
252
  def _is_artifact_reference(self) -> bool:
254
- return self.ref is not None and urlparse(self.ref).scheme == "wandb-artifact"
253
+ return self.ref is not None and urlparse(self.ref).scheme == _WB_ARTIFACT_SCHEME
255
254
 
256
255
  def _referenced_artifact_id(self) -> str | None:
257
256
  if not self._is_artifact_reference():
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from operator import itemgetter
5
6
  from typing import Any, Mapping
6
7
 
7
8
  from wandb.sdk.artifacts.artifact_manifest import ArtifactManifest
@@ -64,7 +65,7 @@ class ArtifactManifestV1(ArtifactManifest):
64
65
  contents.
65
66
  """
66
67
  contents = {}
67
- for entry in sorted(self.entries.values(), key=lambda k: k.path):
68
+ for name, entry in sorted(self.entries.items(), key=itemgetter(0)):
68
69
  json_entry: dict[str, Any] = {
69
70
  "digest": entry.digest,
70
71
  }
@@ -76,7 +77,7 @@ class ArtifactManifestV1(ArtifactManifest):
76
77
  json_entry["extra"] = entry.extra
77
78
  if entry.size is not None:
78
79
  json_entry["size"] = entry.size
79
- contents[entry.path] = json_entry
80
+ contents[name] = json_entry
80
81
  return {
81
82
  "version": self.__class__.version(),
82
83
  "storagePolicy": self.storage_policy.name(),
@@ -87,6 +88,7 @@ class ArtifactManifestV1(ArtifactManifest):
87
88
  def digest(self) -> HexMD5:
88
89
  hasher = _md5()
89
90
  hasher.update(b"wandb-artifact-manifest-v1\n")
90
- for name, entry in sorted(self.entries.items(), key=lambda kv: kv[0]):
91
+ # sort by key (path)
92
+ for name, entry in sorted(self.entries.items(), key=itemgetter(0)):
91
93
  hasher.update(f"{name}:{entry.digest}\n".encode())
92
94
  return HexMD5(hasher.hexdigest())
@@ -42,7 +42,7 @@ class HTTPHandler(StorageHandler):
42
42
 
43
43
  path, hit, cache_open = self._cache.check_etag_obj_path(
44
44
  URIStr(manifest_entry.ref),
45
- ETag(manifest_entry.digest), # TODO(spencerpearson): unsafe cast
45
+ ETag(manifest_entry.digest),
46
46
  manifest_entry.size if manifest_entry.size is not None else 0,
47
47
  )
48
48
  if hit:
@@ -94,7 +94,7 @@ class S3Handler(StorageHandler):
94
94
 
95
95
  path, hit, cache_open = self._cache.check_etag_obj_path(
96
96
  URIStr(manifest_entry.ref),
97
- ETag(manifest_entry.digest), # TODO(spencerpearson): unsafe cast
97
+ ETag(manifest_entry.digest),
98
98
  manifest_entry.size if manifest_entry.size is not None else 0,
99
99
  )
100
100
  if hit:
@@ -167,7 +167,7 @@ class WandbStoragePolicy(StoragePolicy):
167
167
  self._cache._override_cache_path = dest_path
168
168
 
169
169
  path, hit, cache_open = self._cache.check_md5_obj_path(
170
- B64MD5(manifest_entry.digest), # TODO(spencerpearson): unsafe cast
170
+ B64MD5(manifest_entry.digest),
171
171
  manifest_entry.size if manifest_entry.size is not None else 0,
172
172
  )
173
173
  if hit:
@@ -15,15 +15,7 @@ if TYPE_CHECKING:
15
15
 
16
16
 
17
17
  class Audio(BatchableMedia):
18
- """Wandb class for audio clips.
19
-
20
- Args:
21
- data_or_path: (string or numpy array) A path to an audio file
22
- or a numpy array of audio data.
23
- sample_rate: (int) Sample rate, required when passing in raw
24
- numpy array of audio data.
25
- caption: (string) Caption to display with audio.
26
- """
18
+ """W&B class for audio clips."""
27
19
 
28
20
  _log_type = "audio-file"
29
21
 
@@ -38,7 +30,13 @@ class Audio(BatchableMedia):
38
30
  sample_rate: Optional[int] = None,
39
31
  caption: Optional[str] = None,
40
32
  ):
41
- """Accept a path to an audio file or a numpy array of audio data."""
33
+ """Accept a path to an audio file or a numpy array of audio data.
34
+
35
+ Args:
36
+ data_or_path: A path to an audio file or a NumPy array of audio data.
37
+ sample_rate: Sample rate, required when passing in raw NumPy array of audio data.
38
+ caption: Caption to display with audio.
39
+ """
42
40
  super().__init__(caption=caption)
43
41
  self._duration = None
44
42
  self._sample_rate = sample_rate
@@ -72,10 +70,18 @@ class Audio(BatchableMedia):
72
70
 
73
71
  @classmethod
74
72
  def get_media_subdir(cls):
73
+ """Get media subdirectory.
74
+
75
+ <!-- lazydoc-ignore-classmethod: internal -->
76
+ """
75
77
  return os.path.join("media", "audio")
76
78
 
77
79
  @classmethod
78
80
  def from_json(cls, json_obj, source_artifact):
81
+ """Deserialize JSON object into it's class representation.
82
+
83
+ <!-- lazydoc-ignore-classmethod: internal -->
84
+ """
79
85
  return cls(
80
86
  source_artifact.get_entry(json_obj["path"]).download(),
81
87
  caption=json_obj["caption"],
@@ -84,6 +90,10 @@ class Audio(BatchableMedia):
84
90
  def bind_to_run(
85
91
  self, run, key, step, id_=None, ignore_copy_err: Optional[bool] = None
86
92
  ):
93
+ """Bind this object to a run.
94
+
95
+ <!-- lazydoc-ignore: internal -->
96
+ """
87
97
  if self.path_is_reference(self._path):
88
98
  raise ValueError(
89
99
  "Audio media created by a reference to external storage cannot currently be added to a run"
@@ -92,6 +102,10 @@ class Audio(BatchableMedia):
92
102
  return super().bind_to_run(run, key, step, id_, ignore_copy_err)
93
103
 
94
104
  def to_json(self, run):
105
+ """Returns the JSON representation expected by the backend.
106
+
107
+ <!-- lazydoc-ignore: internal -->
108
+ """
95
109
  json_dict = super().to_json(run)
96
110
  json_dict.update(
97
111
  {
@@ -102,6 +116,10 @@ class Audio(BatchableMedia):
102
116
 
103
117
  @classmethod
104
118
  def seq_to_json(cls, seq, run, key, step):
119
+ """Convert a sequence of Audio objects to a JSON representation.
120
+
121
+ <!-- lazydoc-ignore-classmethod: internal -->
122
+ """
105
123
  audio_list = list(seq)
106
124
 
107
125
  util.get_module(
@@ -129,14 +147,20 @@ class Audio(BatchableMedia):
129
147
 
130
148
  @classmethod
131
149
  def durations(cls, audio_list):
150
+ """Calculate the duration of the audio files."""
132
151
  return [a._duration for a in audio_list]
133
152
 
134
153
  @classmethod
135
154
  def sample_rates(cls, audio_list):
155
+ """Get sample rates of the audio files."""
136
156
  return [a._sample_rate for a in audio_list]
137
157
 
138
158
  @classmethod
139
159
  def captions(cls, audio_list):
160
+ """Get the captions of the audio files.
161
+
162
+ <!-- lazydoc-ignore-classmethod: internal -->
163
+ """
140
164
  captions = [a._caption for a in audio_list]
141
165
  if all(c is None for c in captions):
142
166
  return False
@@ -144,6 +168,10 @@ class Audio(BatchableMedia):
144
168
  return ["" if c is None else c for c in captions]
145
169
 
146
170
  def resolve_ref(self):
171
+ """Resolve the reference to the actual file path.
172
+
173
+ <!-- lazydoc-ignore: internal -->
174
+ """
147
175
  if self.path_is_reference(self._path):
148
176
  # this object was already created using a ref:
149
177
  return self._path
@@ -1,7 +1,6 @@
1
1
  import hashlib
2
2
  import os
3
3
  import pathlib
4
- import platform
5
4
  import re
6
5
  import shutil
7
6
  from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Type, Union, cast
@@ -18,39 +17,12 @@ if TYPE_CHECKING: # pragma: no cover
18
17
 
19
18
  from wandb.sdk.artifacts.artifact import Artifact
20
19
 
21
- from ...wandb_run import Run as LocalRun
22
-
23
-
24
- SYS_PLATFORM = platform.system()
25
-
26
-
27
- def check_windows_valid_filename(path: Union[int, str]) -> bool:
28
- r"""Verify that the given path does not contain any invalid characters for a Windows filename.
29
-
30
- Windows filenames cannot contain the following characters:
31
- < > : " \ / | ? *
32
-
33
- For more details, refer to the official documentation:
34
- https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
35
-
36
- Args:
37
- path: The file path to check, which can be either an integer or a string.
38
-
39
- Returns:
40
- bool: True if the path does not contain any invalid characters, False otherwise.
41
- """
42
- return not bool(re.search(r'[<>:"\\?*]', path)) # type: ignore
43
-
44
20
 
45
21
  def _wb_filename(
46
22
  key: Union[str, int], step: Union[str, int], id: Union[str, int], extension: str
47
23
  ) -> str:
48
24
  r"""Generates a safe filename/path for storing media files, using the provided key, step, and id.
49
25
 
50
- The filename is made safe by:
51
- 1. Removing any leading slashes to prevent writing to absolute paths
52
- 2. Replacing '.' and '..' with underscores to prevent directory traversal attacks
53
-
54
26
  If the key contains slashes (e.g. 'images/cats/fluffy.jpg'), subdirectories will be created:
55
27
  media/
56
28
  images/
@@ -70,28 +42,7 @@ def _wb_filename(
70
42
  ValueError: If running on Windows and the key contains invalid filename characters
71
43
  (\\, :, *, ?, ", <, >, |)
72
44
  """
73
- if SYS_PLATFORM == "Windows" and not check_windows_valid_filename(key):
74
- raise ValueError(
75
- f'Media {key} is invalid. Please remove invalid filename characters (\\, :, *, ?, ", <, >, |)'
76
- )
77
-
78
- # On Windows, convert forward slashes to backslashes.
79
- # This ensures that the key is a valid filename on Windows.
80
- if SYS_PLATFORM == "Windows":
81
- key = str(key).replace("/", os.sep)
82
-
83
- # Avoid writing to absolute paths by striping any leading slashes.
84
- # The key has already been validated for windows operating systems in util.check_windows_valid_filename
85
- # This ensures the key does not contain invalid characters for windows, such as '\' or ':'.
86
- # So we can check only for '/' in the key.
87
- key = str(key).lstrip(os.sep)
88
-
89
- # Avoid directory traversal by replacing dots with underscores.
90
- keys = key.split(os.sep)
91
- keys = [k.replace(".", "_") if k in (os.curdir, os.pardir) else k for k in keys]
92
-
93
- # Recombine the key into a relative path.
94
- key = os.sep.join(keys)
45
+ key = util.make_file_path_upload_safe(str(key))
95
46
 
96
47
  return f"{str(key)}_{str(step)}_{str(id)}{extension}"
97
48
 
@@ -104,7 +55,7 @@ class Media(WBValue):
104
55
  """
105
56
 
106
57
  _path: Optional[str]
107
- _run: Optional["LocalRun"]
58
+ _run: Optional["wandb.Run"]
108
59
  _caption: Optional[str]
109
60
  _is_tmp: Optional[bool]
110
61
  _extension: Optional[str]
@@ -156,7 +107,7 @@ class Media(WBValue):
156
107
 
157
108
  def bind_to_run(
158
109
  self,
159
- run: "LocalRun",
110
+ run: "wandb.Run",
160
111
  key: Union[int, str],
161
112
  step: Union[int, str],
162
113
  id_: Optional[Union[int, str]] = None,
@@ -205,7 +156,7 @@ class Media(WBValue):
205
156
  self._path = new_path
206
157
  run._publish_file(media_path)
207
158
 
208
- def to_json(self, run: Union["LocalRun", "Artifact"]) -> dict:
159
+ def to_json(self, run: Union["wandb.Run", "Artifact"]) -> dict:
209
160
  """Serialize the object into a JSON blob.
210
161
 
211
162
  Uses run or artifact to store additional data. If `run_or_artifact` is a
@@ -223,14 +174,13 @@ class Media(WBValue):
223
174
  # into Media itself we should get rid of them
224
175
  from wandb import Image
225
176
  from wandb.data_types import Audio
226
- from wandb.sdk.wandb_run import Run
227
177
 
228
178
  json_obj: Dict[str, Any] = {}
229
179
 
230
180
  if self._caption is not None:
231
181
  json_obj["caption"] = self._caption
232
182
 
233
- if isinstance(run, Run):
183
+ if isinstance(run, wandb.Run):
234
184
  json_obj.update(
235
185
  {
236
186
  "_type": "file", # TODO(adrian): This isn't (yet) a real media type we support on the frontend.
@@ -357,7 +307,7 @@ class BatchableMedia(Media):
357
307
  def seq_to_json(
358
308
  cls: Type["BatchableMedia"],
359
309
  seq: Sequence["BatchableMedia"],
360
- run: "LocalRun",
310
+ run: "wandb.Run",
361
311
  key: str,
362
312
  step: Union[int, str],
363
313
  ) -> dict:
@@ -243,25 +243,29 @@ class Node(WBValue):
243
243
 
244
244
 
245
245
  class Graph(Media):
246
- """Wandb class for graphs.
246
+ """W&B class for graphs.
247
247
 
248
- This class is typically used for saving and displaying neural net models. It
249
- represents the graph as an array of nodes and edges. The nodes can have
248
+ This class is typically used for saving and displaying neural net models.
249
+ It represents the graph as an array of nodes and edges. The nodes can have
250
250
  labels that can be visualized by wandb.
251
251
 
252
- Examples:
253
- Import a keras model:
254
- ```
255
- Graph.from_keras(keras_model)
256
- ```
257
-
258
252
  Attributes:
259
253
  format (string): Format to help wandb display the graph nicely.
260
- nodes ([wandb.Node]): List of wandb.Nodes
254
+ nodes ([wandb.Node]): List of `wandb.Nodes`.
261
255
  nodes_by_id (dict): dict of ids -> nodes
262
- edges ([(wandb.Node, wandb.Node)]): List of pairs of nodes interpreted as edges
263
- loaded (boolean): Flag to tell whether the graph is completely loaded
264
- root (wandb.Node): root node of the graph
256
+ edges ([(wandb.Node, wandb.Node)]): List of pairs of nodes interpreted
257
+ as edges.
258
+ loaded (boolean): Flag to tell whether the graph is completely loaded.
259
+ root (wandb.Node): Root node of the graph.
260
+
261
+ Examples:
262
+ Import a keras model.
263
+
264
+ ```python
265
+ import wandb
266
+
267
+ wandb.Graph.from_keras(keras_model)
268
+ ```
265
269
  """
266
270
 
267
271
  _log_type = "graph-file"
@@ -287,6 +291,10 @@ class Graph(Media):
287
291
  }
288
292
 
289
293
  def bind_to_run(self, *args, **kwargs):
294
+ """Bind this object to a run.
295
+
296
+ <!-- lazydoc-ignore: internal -->
297
+ """
290
298
  data = self._to_graph_json()
291
299
  tmp_path = os.path.join(MEDIA_TMP.name, runid.generate_id() + ".graph.json")
292
300
  data = _numpy_arrays_to_lists(data)
@@ -299,9 +307,17 @@ class Graph(Media):
299
307
 
300
308
  @classmethod
301
309
  def get_media_subdir(cls):
310
+ """Get media subdirectory.
311
+
312
+ "<!-- lazydoc-ignore-classmethod: internal -->
313
+ """
302
314
  return os.path.join("media", "graph")
303
315
 
304
316
  def to_json(self, run):
317
+ """Returns the JSON representation expected by the backend.
318
+
319
+ <!-- lazydoc-ignore: internal -->
320
+ """
305
321
  json_dict = super().to_json(run)
306
322
  json_dict["_type"] = self._log_type
307
323
  return json_dict
@@ -310,12 +326,20 @@ class Graph(Media):
310
326
  return self.nodes_by_id[nid]
311
327
 
312
328
  def pprint(self):
329
+ """Pretty print the graph.
330
+
331
+ <!-- lazydoc-ignore: internal -->
332
+ """
313
333
  for edge in self.edges:
314
334
  pprint.pprint(edge.attributes) # noqa: T203
315
335
  for node in self.nodes:
316
336
  pprint.pprint(node.attributes) # noqa: T203
317
337
 
318
338
  def add_node(self, node=None, **node_kwargs):
339
+ """Add a node to the graph.
340
+
341
+ <!-- lazydoc-ignore: internal -->
342
+ """
319
343
  if node is None:
320
344
  node = Node(**node_kwargs)
321
345
  elif node_kwargs:
@@ -328,6 +352,10 @@ class Graph(Media):
328
352
  return node
329
353
 
330
354
  def add_edge(self, from_node, to_node):
355
+ """Add an edge to the graph.
356
+
357
+ <!-- lazydoc-ignore: internal -->
358
+ """
331
359
  edge = Edge(from_node, to_node)
332
360
  self.edges.append(edge)
333
361
 
@@ -335,7 +363,13 @@ class Graph(Media):
335
363
 
336
364
  @classmethod
337
365
  def from_keras(cls, model):
338
- # TODO: his method requires a refactor to work with the keras 3.
366
+ """Create a graph from a Keras model.
367
+
368
+ This method is not supported for Keras 3.0.0 and above.
369
+ Requires a refactor.
370
+
371
+ "<!-- lazydoc-ignore-classmethod: internal -->
372
+ """
339
373
  graph = cls()
340
374
  # Shamelessly copied (then modified) from keras/keras/utils/layer_utils.py
341
375
  sequential_like = cls._is_sequential(model)
@@ -310,9 +310,7 @@ class BoundingBoxes2D(JSONMetadata):
310
310
  return True
311
311
 
312
312
  def to_json(self, run_or_artifact: Union["LocalRun", "Artifact"]) -> dict:
313
- from wandb.sdk.wandb_run import Run
314
-
315
- if isinstance(run_or_artifact, Run):
313
+ if isinstance(run_or_artifact, wandb.Run):
316
314
  return super().to_json(run_or_artifact)
317
315
  elif isinstance(run_or_artifact, wandb.Artifact):
318
316
  # TODO (tim): I would like to log out a proper dictionary representing this object, but don't
@@ -209,11 +209,9 @@ class ImageMask(Media):
209
209
  )
210
210
 
211
211
  def to_json(self, run_or_artifact: Union["LocalRun", "Artifact"]) -> dict:
212
- from wandb.sdk.wandb_run import Run
213
-
214
212
  json_dict = super().to_json(run_or_artifact)
215
213
 
216
- if isinstance(run_or_artifact, Run):
214
+ if isinstance(run_or_artifact, wandb.Run):
217
215
  json_dict["_type"] = self.type_name()
218
216
  return json_dict
219
217
  elif isinstance(run_or_artifact, wandb.Artifact):
@@ -16,32 +16,14 @@ if TYPE_CHECKING: # pragma: no cover
16
16
 
17
17
 
18
18
  class Histogram(WBValue):
19
- """wandb class for histograms.
19
+ """W&B class for histograms.
20
20
 
21
21
  This object works just like numpy's histogram function
22
22
  https://docs.scipy.org/doc/numpy/reference/generated/numpy.histogram.html
23
23
 
24
- Examples:
25
- Generate histogram from a sequence
26
- ```python
27
- wandb.Histogram([1, 2, 3])
28
- ```
29
-
30
- Efficiently initialize from np.histogram.
31
- ```python
32
- hist = np.histogram(data)
33
- wandb.Histogram(np_histogram=hist)
34
- ```
35
-
36
- Args:
37
- sequence: (array_like) input data for histogram
38
- np_histogram: (numpy histogram) alternative input of a precomputed histogram
39
- num_bins: (int) Number of bins for the histogram. The default number of bins
40
- is 64. The maximum number of bins is 512
41
-
42
24
  Attributes:
43
- bins: ([float]) edges of bins
44
- histogram: ([int]) number of elements falling in each bin
25
+ bins ([float]): Edges of bins
26
+ histogram ([int]): Number of elements falling in each bin.
45
27
  """
46
28
 
47
29
  MAX_LENGTH: int = 512
@@ -53,6 +35,33 @@ class Histogram(WBValue):
53
35
  np_histogram: Optional["NumpyHistogram"] = None,
54
36
  num_bins: int = 64,
55
37
  ) -> None:
38
+ """Initialize a Histogram object.
39
+
40
+ Args:
41
+ sequence: Input data for histogram.
42
+ np_histogram: Alternative input of a precomputed histogram.
43
+ num_bins: Number of bins for the histogram. The default number of bins
44
+ is 64. The maximum number of bins is 512.
45
+
46
+ Examples:
47
+ Generate histogram from a sequence.
48
+
49
+ ```python
50
+ import wandb
51
+
52
+ wandb.Histogram([1, 2, 3])
53
+ ```
54
+
55
+ Efficiently initialize from np.histogram.
56
+
57
+ ```python
58
+ import numpy as np
59
+ import wandb
60
+
61
+ hist = np.histogram(data)
62
+ wandb.Histogram(np_histogram=hist)
63
+ ```
64
+ """
56
65
  if np_histogram:
57
66
  if len(np_histogram) == 2:
58
67
  self.histogram = (
@@ -83,6 +92,10 @@ class Histogram(WBValue):
83
92
  raise ValueError("len(bins) must be len(histogram) + 1")
84
93
 
85
94
  def to_json(self, run: Optional[Union["LocalRun", "Artifact"]] = None) -> dict:
95
+ """Returns the JSON representation expected by the backend.
96
+
97
+ <!-- lazydoc-ignore: internal -->
98
+ """
86
99
  return {"_type": self._log_type, "values": self.histogram, "bins": self.bins}
87
100
 
88
101
  def __sizeof__(self) -> int: