wandb 0.22.1__py3-none-win_amd64.whl → 0.22.3__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.
- wandb/__init__.py +1 -1
- wandb/__init__.pyi +7 -4
- wandb/_pydantic/__init__.py +8 -1
- wandb/_pydantic/base.py +54 -18
- wandb/_pydantic/field_types.py +8 -3
- wandb/_pydantic/pagination.py +46 -0
- wandb/_pydantic/utils.py +2 -2
- wandb/apis/public/api.py +24 -19
- wandb/apis/public/artifacts.py +259 -270
- wandb/apis/public/registries/_utils.py +40 -54
- wandb/apis/public/registries/registries_search.py +70 -85
- wandb/apis/public/registries/registry.py +173 -156
- wandb/apis/public/runs.py +27 -6
- wandb/apis/public/utils.py +43 -20
- wandb/automations/_generated/create_automation.py +2 -2
- wandb/automations/_generated/create_generic_webhook_integration.py +4 -4
- wandb/automations/_generated/delete_automation.py +2 -2
- wandb/automations/_generated/fragments.py +31 -52
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +3 -3
- wandb/automations/_generated/get_automations.py +3 -3
- wandb/automations/_generated/get_automations_by_entity.py +3 -3
- wandb/automations/_generated/input_types.py +9 -9
- wandb/automations/_generated/integrations_by_entity.py +3 -3
- wandb/automations/_generated/operations.py +6 -6
- wandb/automations/_generated/slack_integrations_by_entity.py +3 -3
- wandb/automations/_generated/update_automation.py +2 -2
- wandb/automations/_utils.py +3 -3
- wandb/automations/actions.py +3 -3
- wandb/automations/automations.py +6 -5
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/beta.py +23 -3
- wandb/cli/beta_leet.py +75 -0
- wandb/cli/beta_sync.py +1 -1
- wandb/cli/cli.py +34 -7
- wandb/errors/term.py +8 -8
- wandb/jupyter.py +0 -51
- wandb/old/settings.py +6 -6
- wandb/proto/v3/wandb_api_pb2.py +86 -0
- wandb/proto/v3/wandb_server_pb2.py +38 -37
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_sync_pb2.py +19 -6
- wandb/proto/v4/wandb_api_pb2.py +37 -0
- wandb/proto/v4/wandb_server_pb2.py +38 -37
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_sync_pb2.py +10 -6
- wandb/proto/v5/wandb_api_pb2.py +38 -0
- wandb/proto/v5/wandb_server_pb2.py +38 -37
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_sync_pb2.py +10 -6
- wandb/proto/v6/wandb_api_pb2.py +48 -0
- wandb/proto/v6/wandb_server_pb2.py +38 -37
- wandb/proto/v6/wandb_settings_pb2.py +2 -2
- wandb/proto/v6/wandb_sync_pb2.py +10 -6
- wandb/proto/wandb_api_pb2.py +18 -0
- wandb/proto/wandb_generate_proto.py +1 -0
- wandb/sdk/artifacts/_generated/__init__.py +96 -40
- wandb/sdk/artifacts/_generated/add_aliases.py +3 -3
- wandb/sdk/artifacts/_generated/add_artifact_collection_tags.py +26 -0
- wandb/sdk/artifacts/_generated/artifact_by_id.py +2 -2
- wandb/sdk/artifacts/_generated/artifact_by_name.py +3 -3
- wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +27 -8
- wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +27 -8
- wandb/sdk/artifacts/_generated/artifact_created_by.py +7 -20
- wandb/sdk/artifacts/_generated/artifact_file_urls.py +19 -6
- wandb/sdk/artifacts/_generated/artifact_membership_by_name.py +26 -0
- wandb/sdk/artifacts/_generated/artifact_type.py +5 -5
- wandb/sdk/artifacts/_generated/artifact_used_by.py +8 -17
- wandb/sdk/artifacts/_generated/artifact_version_files.py +19 -8
- wandb/sdk/artifacts/_generated/delete_aliases.py +3 -3
- wandb/sdk/artifacts/_generated/delete_artifact.py +4 -4
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tags.py +23 -0
- wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +4 -4
- wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +4 -4
- wandb/sdk/artifacts/_generated/delete_registry.py +21 -0
- wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +8 -20
- wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +13 -35
- wandb/sdk/artifacts/_generated/fetch_org_info_from_entity.py +28 -0
- wandb/sdk/artifacts/_generated/fetch_registries.py +18 -8
- wandb/sdk/{projects → artifacts}/_generated/fetch_registry.py +4 -4
- wandb/sdk/artifacts/_generated/fragments.py +183 -333
- wandb/sdk/artifacts/_generated/input_types.py +133 -7
- wandb/sdk/artifacts/_generated/link_artifact.py +5 -5
- wandb/sdk/artifacts/_generated/operations.py +1053 -548
- wandb/sdk/artifacts/_generated/project_artifact_collection.py +9 -77
- wandb/sdk/artifacts/_generated/project_artifact_collections.py +21 -9
- wandb/sdk/artifacts/_generated/project_artifact_type.py +3 -3
- wandb/sdk/artifacts/_generated/project_artifact_types.py +19 -6
- wandb/sdk/artifacts/_generated/project_artifacts.py +7 -8
- wandb/sdk/artifacts/_generated/registry_collections.py +21 -9
- wandb/sdk/artifacts/_generated/registry_versions.py +20 -9
- wandb/sdk/artifacts/_generated/rename_registry.py +25 -0
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +5 -9
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +5 -9
- wandb/sdk/artifacts/_generated/type_info.py +2 -2
- wandb/sdk/artifacts/_generated/unlink_artifact.py +3 -5
- wandb/sdk/artifacts/_generated/update_artifact.py +3 -3
- wandb/sdk/artifacts/_generated/update_artifact_collection_type.py +28 -0
- wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +7 -16
- wandb/sdk/artifacts/_generated/update_artifact_sequence.py +7 -16
- wandb/sdk/artifacts/_generated/upsert_registry.py +25 -0
- wandb/sdk/artifacts/_gqlutils.py +170 -6
- wandb/sdk/artifacts/_models/__init__.py +9 -0
- wandb/sdk/artifacts/_models/artifact_collection.py +109 -0
- wandb/sdk/artifacts/_models/manifest.py +26 -0
- wandb/sdk/artifacts/_models/pagination.py +26 -0
- wandb/sdk/artifacts/_models/registry.py +100 -0
- wandb/sdk/artifacts/_validators.py +45 -27
- wandb/sdk/artifacts/artifact.py +249 -244
- wandb/sdk/artifacts/artifact_file_cache.py +1 -1
- wandb/sdk/artifacts/artifact_manifest.py +37 -32
- wandb/sdk/artifacts/artifact_manifest_entry.py +82 -133
- wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +43 -61
- wandb/sdk/artifacts/storage_handler.py +18 -12
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +11 -6
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +17 -12
- wandb/sdk/artifacts/storage_handlers/http_handler.py +9 -4
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +10 -6
- wandb/sdk/artifacts/storage_handlers/multi_handler.py +5 -4
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +10 -8
- wandb/sdk/artifacts/storage_handlers/tracking_handler.py +6 -4
- wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +24 -21
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +4 -2
- wandb/sdk/artifacts/storage_policies/_multipart.py +187 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +61 -242
- wandb/sdk/artifacts/storage_policy.py +25 -12
- wandb/sdk/data_types/image.py +2 -2
- wandb/sdk/data_types/object_3d.py +67 -2
- wandb/sdk/interface/interface.py +72 -64
- wandb/sdk/interface/interface_queue.py +27 -18
- wandb/sdk/interface/interface_shared.py +61 -23
- wandb/sdk/interface/interface_sock.py +9 -5
- wandb/sdk/internal/_generated/server_features_query.py +4 -4
- wandb/sdk/internal/job_builder.py +27 -10
- wandb/sdk/internal/sender.py +4 -1
- wandb/sdk/launch/create_job.py +2 -1
- wandb/sdk/launch/inputs/schema.py +13 -10
- wandb/sdk/lib/apikey.py +8 -12
- wandb/sdk/lib/asyncio_compat.py +1 -1
- wandb/sdk/lib/asyncio_manager.py +5 -5
- wandb/sdk/lib/console_capture.py +38 -30
- wandb/sdk/lib/progress.py +151 -125
- wandb/sdk/lib/retry.py +3 -2
- wandb/sdk/lib/service/service_connection.py +2 -2
- wandb/sdk/lib/wb_logging.py +2 -1
- wandb/sdk/mailbox/mailbox.py +1 -1
- wandb/sdk/wandb_init.py +11 -14
- wandb/sdk/wandb_run.py +14 -48
- wandb/sdk/wandb_settings.py +114 -30
- {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/METADATA +2 -1
- {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/RECORD +154 -146
- wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +0 -26
- wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +0 -36
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +0 -25
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +0 -35
- wandb/sdk/projects/_generated/__init__.py +0 -26
- wandb/sdk/projects/_generated/delete_project.py +0 -22
- wandb/sdk/projects/_generated/enums.py +0 -4
- wandb/sdk/projects/_generated/fragments.py +0 -41
- wandb/sdk/projects/_generated/input_types.py +0 -13
- wandb/sdk/projects/_generated/operations.py +0 -88
- wandb/sdk/projects/_generated/rename_project.py +0 -27
- wandb/sdk/projects/_generated/upsert_registry_project.py +0 -27
- {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/WHEEL +0 -0
- {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/entry_points.txt +0 -0
- {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/lib/progress.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import contextlib
|
|
7
7
|
import time
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import Iterator, NoReturn
|
|
9
9
|
|
|
10
10
|
from wandb.proto import wandb_internal_pb2 as pb
|
|
11
11
|
from wandb.sdk.interface import interface
|
|
@@ -13,6 +13,10 @@ from wandb.sdk.lib import asyncio_compat
|
|
|
13
13
|
|
|
14
14
|
from . import printer as p
|
|
15
15
|
|
|
16
|
+
_INDENT = " "
|
|
17
|
+
_MAX_LINES_TO_PRINT = 6
|
|
18
|
+
_MAX_OPS_TO_PRINT = 5
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
async def loop_printing_operation_stats(
|
|
18
22
|
progress: ProgressPrinter,
|
|
@@ -93,168 +97,192 @@ class ProgressPrinter:
|
|
|
93
97
|
progress_text_area: p.DynamicText | None,
|
|
94
98
|
default_text: str,
|
|
95
99
|
) -> None:
|
|
96
|
-
self._show_operation_stats = True
|
|
97
100
|
self._printer = printer
|
|
98
101
|
self._progress_text_area = progress_text_area
|
|
99
102
|
self._default_text = default_text
|
|
100
|
-
self._tick =
|
|
103
|
+
self._tick = -1
|
|
101
104
|
self._last_printed_line = ""
|
|
102
105
|
|
|
103
106
|
def update(
|
|
104
107
|
self,
|
|
105
|
-
|
|
108
|
+
stats_or_groups: pb.OperationStats | dict[str, pb.OperationStats],
|
|
106
109
|
) -> None:
|
|
107
|
-
"""Update the displayed information.
|
|
108
|
-
|
|
110
|
+
"""Update the displayed information.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
stats_or_groups: A single group of operations, or zero or more
|
|
114
|
+
labeled operation groups.
|
|
115
|
+
"""
|
|
116
|
+
self._tick += 1
|
|
117
|
+
|
|
118
|
+
if not self._progress_text_area:
|
|
119
|
+
line = self._to_static_text(stats_or_groups)
|
|
120
|
+
if line and line != self._last_printed_line:
|
|
121
|
+
self._printer.display(line)
|
|
122
|
+
self._last_printed_line = line
|
|
109
123
|
return
|
|
110
124
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
self._update_single_run(progress[0])
|
|
119
|
-
else:
|
|
120
|
-
self._update_multiple_runs(progress)
|
|
125
|
+
lines = self._to_dynamic_text(stats_or_groups)
|
|
126
|
+
if not lines:
|
|
127
|
+
loading_symbol = self._printer.loading_symbol(self._tick)
|
|
128
|
+
if loading_symbol:
|
|
129
|
+
lines = [f"{loading_symbol} {self._default_text}"]
|
|
130
|
+
else:
|
|
131
|
+
lines = [self._default_text]
|
|
121
132
|
|
|
122
|
-
self.
|
|
133
|
+
self._progress_text_area.set_text("\n".join(lines))
|
|
123
134
|
|
|
124
|
-
def
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
def _to_dynamic_text(
|
|
136
|
+
self,
|
|
137
|
+
stats_or_groups: pb.OperationStats | dict[str, pb.OperationStats],
|
|
138
|
+
) -> list[str]:
|
|
139
|
+
"""Returns text to show in a dynamic text area."""
|
|
140
|
+
loading_symbol = self._printer.loading_symbol(self._tick)
|
|
141
|
+
|
|
142
|
+
if isinstance(stats_or_groups, dict):
|
|
143
|
+
return _GroupedOperationStatsPrinter(
|
|
127
144
|
self._printer,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
default_text=self._default_text,
|
|
132
|
-
).display(stats_list)
|
|
145
|
+
_MAX_LINES_TO_PRINT,
|
|
146
|
+
loading_symbol,
|
|
147
|
+
).render(stats_or_groups)
|
|
133
148
|
|
|
134
149
|
else:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
top_level_operations.append(op.desc)
|
|
141
|
-
else:
|
|
142
|
-
extra_operations += 1
|
|
143
|
-
|
|
144
|
-
line = "; ".join(top_level_operations)
|
|
145
|
-
if extra_operations > 0:
|
|
146
|
-
line += f" (+ {extra_operations} more)"
|
|
147
|
-
|
|
148
|
-
if line and line != self._last_printed_line:
|
|
149
|
-
self._printer.display(line)
|
|
150
|
-
self._last_printed_line = line
|
|
150
|
+
return _OperationStatsPrinter(
|
|
151
|
+
self._printer,
|
|
152
|
+
_MAX_LINES_TO_PRINT,
|
|
153
|
+
loading_symbol,
|
|
154
|
+
).render(stats_or_groups)
|
|
151
155
|
|
|
152
|
-
def
|
|
156
|
+
def _to_static_text(
|
|
153
157
|
self,
|
|
154
|
-
|
|
155
|
-
) ->
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if stats.deduped_bytes > 0:
|
|
163
|
-
line += f" ({_megabytes(stats.deduped_bytes):.3f} MB deduped)"
|
|
164
|
-
|
|
165
|
-
if stats.total_bytes > 0:
|
|
166
|
-
self._update_progress_text(
|
|
167
|
-
line,
|
|
168
|
-
stats.uploaded_bytes / stats.total_bytes,
|
|
158
|
+
stats_or_groups: pb.OperationStats | dict[str, pb.OperationStats],
|
|
159
|
+
) -> str:
|
|
160
|
+
"""Returns a single line of text to print out."""
|
|
161
|
+
if isinstance(stats_or_groups, dict):
|
|
162
|
+
sorted_prefixed_stats = list(
|
|
163
|
+
(f"[{group}] ", stats) #
|
|
164
|
+
for group, stats in sorted(stats_or_groups.items())
|
|
169
165
|
)
|
|
170
166
|
else:
|
|
171
|
-
|
|
167
|
+
sorted_prefixed_stats = [("", stats_or_groups)]
|
|
168
|
+
|
|
169
|
+
group_strs: list[str] = []
|
|
170
|
+
total_operations = 0
|
|
171
|
+
total_printed = 0
|
|
172
|
+
|
|
173
|
+
for prefix, stats in sorted_prefixed_stats:
|
|
174
|
+
total_operations += stats.total_operations
|
|
175
|
+
if not stats.operations:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
group_ops: list[str] = []
|
|
179
|
+
i = 0
|
|
180
|
+
while total_printed < _MAX_OPS_TO_PRINT and i < len(stats.operations):
|
|
181
|
+
group_ops.append(stats.operations[i].desc)
|
|
182
|
+
total_printed += 1
|
|
183
|
+
i += 1
|
|
184
|
+
|
|
185
|
+
if group_ops:
|
|
186
|
+
group_strs.append(prefix + "; ".join(group_ops))
|
|
187
|
+
|
|
188
|
+
line = "; ".join(group_strs)
|
|
189
|
+
remaining = total_operations - total_printed
|
|
190
|
+
if total_printed > 0 and remaining > 0:
|
|
191
|
+
line += f" (+ {remaining} more)"
|
|
192
|
+
|
|
193
|
+
return line
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class _GroupedOperationStatsPrinter:
|
|
197
|
+
"""Renders a list of labeled operation stats groups into lines of text."""
|
|
172
198
|
|
|
173
|
-
def
|
|
199
|
+
def __init__(
|
|
174
200
|
self,
|
|
175
|
-
|
|
201
|
+
printer: p.Printer,
|
|
202
|
+
max_lines: int,
|
|
203
|
+
loading_symbol: str,
|
|
176
204
|
) -> None:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
for progress in progress_list:
|
|
182
|
-
total_files += progress.file_counts.wandb_count
|
|
183
|
-
total_files += progress.file_counts.media_count
|
|
184
|
-
total_files += progress.file_counts.artifact_count
|
|
185
|
-
total_files += progress.file_counts.other_count
|
|
186
|
-
|
|
187
|
-
uploaded_bytes += progress.pusher_stats.uploaded_bytes
|
|
188
|
-
total_bytes += progress.pusher_stats.total_bytes
|
|
189
|
-
|
|
190
|
-
line = (
|
|
191
|
-
f"Processing {len(progress_list)} runs with {total_files} files"
|
|
192
|
-
f" ({_megabytes(uploaded_bytes):.2f} MB"
|
|
193
|
-
f" / {_megabytes(total_bytes):.2f} MB)"
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
if total_bytes > 0:
|
|
197
|
-
self._update_progress_text(line, uploaded_bytes / total_bytes)
|
|
198
|
-
else:
|
|
199
|
-
self._update_progress_text(line, 1.0)
|
|
205
|
+
self._printer = printer
|
|
206
|
+
self._max_lines = max_lines
|
|
207
|
+
self._loading_symbol = loading_symbol
|
|
200
208
|
|
|
201
|
-
def
|
|
202
|
-
|
|
203
|
-
return
|
|
204
|
-
self._last_printed_line = text
|
|
209
|
+
def render(self, groups: dict[str, pb.OperationStats]) -> list[str]:
|
|
210
|
+
"""Convert labeled operation stats groups into text to display.
|
|
205
211
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
Args:
|
|
213
|
+
groups: A mapping from group labels to stats for that group.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
The lines of text to print. The lines do not end with the newline
|
|
217
|
+
character. Returns an empty list if there are no operations.
|
|
218
|
+
"""
|
|
219
|
+
lines: list[str] = []
|
|
220
|
+
|
|
221
|
+
for key, stats in sorted(groups.items()):
|
|
222
|
+
# Don't display empty groups.
|
|
223
|
+
if not stats.operations:
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
# Ensure enough space left for the group header and at least
|
|
227
|
+
# one line of content.
|
|
228
|
+
remaining_lines = self._max_lines - len(lines)
|
|
229
|
+
if remaining_lines < 2:
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
# Group header.
|
|
233
|
+
lines.append(key)
|
|
210
234
|
|
|
235
|
+
# Group content.
|
|
236
|
+
stats_lines = _OperationStatsPrinter(
|
|
237
|
+
printer=self._printer,
|
|
238
|
+
max_lines=remaining_lines - 1, # minus one for the header
|
|
239
|
+
loading_symbol=self._loading_symbol,
|
|
240
|
+
).render(stats)
|
|
241
|
+
for line in stats_lines:
|
|
242
|
+
lines.append(f"{_INDENT}{line}")
|
|
211
243
|
|
|
212
|
-
|
|
213
|
-
|
|
244
|
+
return lines
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class _OperationStatsPrinter:
|
|
248
|
+
"""Renders operation stats into lines of text."""
|
|
214
249
|
|
|
215
250
|
def __init__(
|
|
216
251
|
self,
|
|
217
252
|
printer: p.Printer,
|
|
218
|
-
text_area: p.DynamicText,
|
|
219
253
|
max_lines: int,
|
|
220
254
|
loading_symbol: str,
|
|
221
|
-
default_text: str,
|
|
222
255
|
) -> None:
|
|
223
256
|
self._printer = printer
|
|
224
|
-
self._text_area = text_area
|
|
225
257
|
self._max_lines = max_lines
|
|
226
258
|
self._loading_symbol = loading_symbol
|
|
227
|
-
self._default_text = default_text
|
|
228
259
|
|
|
229
260
|
self._lines: list[str] = []
|
|
230
261
|
self._ops_shown = 0
|
|
231
262
|
|
|
232
|
-
def
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
total_operations = 0
|
|
238
|
-
for stats in stats_list:
|
|
239
|
-
for op in stats.operations:
|
|
240
|
-
self._add_operation(op, is_subtask=False, indent="")
|
|
241
|
-
total_operations += stats.total_operations
|
|
263
|
+
def render(self, stats: pb.OperationStats) -> list[str]:
|
|
264
|
+
"""Convert the stats into a list of lines to display.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
stats: Collection of operations to display.
|
|
242
268
|
|
|
243
|
-
|
|
269
|
+
Returns:
|
|
270
|
+
The lines of text to print. The lines do not end with the newline
|
|
271
|
+
character. Returns an empty list if there are no operations.
|
|
272
|
+
"""
|
|
273
|
+
for op in stats.operations:
|
|
274
|
+
self._add_operation(op, is_subtask=False, indent="")
|
|
275
|
+
|
|
276
|
+
if self._ops_shown < stats.total_operations:
|
|
244
277
|
if 1 <= self._max_lines <= len(self._lines):
|
|
278
|
+
self._ops_shown -= 1
|
|
245
279
|
self._lines.pop()
|
|
246
280
|
|
|
247
|
-
remaining = total_operations - self._ops_shown
|
|
281
|
+
remaining = stats.total_operations - self._ops_shown
|
|
248
282
|
|
|
249
283
|
self._lines.append(f"+ {remaining} more task(s)")
|
|
250
284
|
|
|
251
|
-
|
|
252
|
-
if self._loading_symbol:
|
|
253
|
-
self._text_area.set_text(f"{self._loading_symbol} {self._default_text}")
|
|
254
|
-
else:
|
|
255
|
-
self._text_area.set_text(self._default_text)
|
|
256
|
-
else:
|
|
257
|
-
self._text_area.set_text("\n".join(self._lines))
|
|
285
|
+
return self._lines
|
|
258
286
|
|
|
259
287
|
def _add_operation(self, op: pb.Operation, is_subtask: bool, indent: str) -> None:
|
|
260
288
|
"""Add the operation to `self._lines`."""
|
|
@@ -264,14 +292,17 @@ class _DynamicOperationStatsPrinter:
|
|
|
264
292
|
if not is_subtask:
|
|
265
293
|
self._ops_shown += 1
|
|
266
294
|
|
|
267
|
-
|
|
295
|
+
status_indent_level = 0 # alignment for the status message, if any
|
|
296
|
+
parts: list[str] = []
|
|
268
297
|
|
|
269
298
|
# Subtask indicator.
|
|
270
299
|
if is_subtask and self._printer.supports_unicode:
|
|
300
|
+
status_indent_level += 2 # +1 for space
|
|
271
301
|
parts.append("↳")
|
|
272
302
|
|
|
273
303
|
# Loading symbol.
|
|
274
304
|
if self._loading_symbol:
|
|
305
|
+
status_indent_level += 2 # +1 for space
|
|
275
306
|
parts.append(self._loading_symbol)
|
|
276
307
|
|
|
277
308
|
# Task name.
|
|
@@ -289,14 +320,14 @@ class _DynamicOperationStatsPrinter:
|
|
|
289
320
|
if op.error_status:
|
|
290
321
|
error_word = self._printer.error("ERROR")
|
|
291
322
|
error_desc = self._printer.secondary_text(op.error_status)
|
|
292
|
-
|
|
323
|
+
status_indent = " " * status_indent_level
|
|
293
324
|
self._lines.append(
|
|
294
|
-
f"{indent}{
|
|
325
|
+
f"{indent}{status_indent}{error_word} {error_desc}",
|
|
295
326
|
)
|
|
296
327
|
|
|
297
328
|
# Subtasks.
|
|
298
329
|
if op.subtasks:
|
|
299
|
-
subtask_indent = indent +
|
|
330
|
+
subtask_indent = indent + _INDENT
|
|
300
331
|
for task in op.subtasks:
|
|
301
332
|
self._add_operation(
|
|
302
333
|
task,
|
|
@@ -318,8 +349,3 @@ def _time_to_string(seconds: float) -> str:
|
|
|
318
349
|
hours = int(seconds / (60 * 60))
|
|
319
350
|
minutes = int((seconds / 60) % 60)
|
|
320
351
|
return f"{hours}h{minutes}m"
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def _megabytes(bytes: int) -> float:
|
|
324
|
-
"""Returns the number of megabytes in `bytes`."""
|
|
325
|
-
return bytes / (1 << 20)
|
wandb/sdk/lib/retry.py
CHANGED
|
@@ -77,9 +77,10 @@ class Retry(Generic[_R]):
|
|
|
77
77
|
self._retryable_exceptions = retryable_exceptions
|
|
78
78
|
else:
|
|
79
79
|
self._retryable_exceptions = (TransientError,)
|
|
80
|
-
self._index = 0
|
|
81
80
|
self.retry_callback = retry_callback
|
|
82
81
|
|
|
82
|
+
self._num_iter = 0
|
|
83
|
+
|
|
83
84
|
def _sleep_check_cancelled(
|
|
84
85
|
self, wait_seconds: float, cancel_event: Optional[threading.Event]
|
|
85
86
|
) -> bool:
|
|
@@ -194,7 +195,7 @@ class Retry(Generic[_R]):
|
|
|
194
195
|
else:
|
|
195
196
|
wandb.termlog(
|
|
196
197
|
f"{self._error_prefix}"
|
|
197
|
-
f" ({exception.__class__.__name__}), entering retry loop."
|
|
198
|
+
+ f" ({exception.__class__.__name__}), entering retry loop."
|
|
198
199
|
)
|
|
199
200
|
|
|
200
201
|
def _print_recovered(self, start_time: datetime.datetime) -> None:
|
|
@@ -190,8 +190,8 @@ class ServiceConnection:
|
|
|
190
190
|
except TimeoutError:
|
|
191
191
|
raise WandbAttachFailedError(
|
|
192
192
|
"Failed to attach because the run does not belong to"
|
|
193
|
-
" the current service process, or because the service"
|
|
194
|
-
" process is busy (unlikely)."
|
|
193
|
+
+ " the current service process, or because the service"
|
|
194
|
+
+ " process is busy (unlikely)."
|
|
195
195
|
) from None
|
|
196
196
|
|
|
197
197
|
else:
|
wandb/sdk/lib/wb_logging.py
CHANGED
|
@@ -136,7 +136,7 @@ def add_file_handler(run_id: str, filepath: pathlib.Path) -> logging.Handler:
|
|
|
136
136
|
return handler
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
class _RunIDFilter
|
|
139
|
+
class _RunIDFilter:
|
|
140
140
|
"""Filters out messages logged for a different run."""
|
|
141
141
|
|
|
142
142
|
def __init__(self, run_id: str) -> None:
|
|
@@ -148,6 +148,7 @@ class _RunIDFilter(logging.Filter):
|
|
|
148
148
|
self._run_id = run_id
|
|
149
149
|
|
|
150
150
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
151
|
+
"""Modify a log record and return whether it matches the run."""
|
|
151
152
|
run_id = _run_id.get()
|
|
152
153
|
|
|
153
154
|
if run_id is None:
|
wandb/sdk/mailbox/mailbox.py
CHANGED
wandb/sdk/wandb_init.py
CHANGED
|
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import contextlib
|
|
14
14
|
import dataclasses
|
|
15
|
+
import functools
|
|
15
16
|
import json
|
|
16
17
|
import logging
|
|
17
18
|
import os
|
|
@@ -727,7 +728,7 @@ class _WandbInit:
|
|
|
727
728
|
drun = Run(
|
|
728
729
|
settings=Settings(
|
|
729
730
|
mode="disabled",
|
|
730
|
-
|
|
731
|
+
root_dir=tempfile.gettempdir(),
|
|
731
732
|
run_id=run_id,
|
|
732
733
|
run_tags=tuple(),
|
|
733
734
|
run_notes=None,
|
|
@@ -988,25 +989,21 @@ class _WandbInit:
|
|
|
988
989
|
|
|
989
990
|
run_init_handle = backend.interface.deliver_run(run)
|
|
990
991
|
|
|
991
|
-
|
|
992
|
-
assert backend.interface
|
|
993
|
-
|
|
992
|
+
try:
|
|
994
993
|
with progress.progress_printer(
|
|
995
994
|
run_printer,
|
|
996
995
|
default_text="Waiting for wandb.init()...",
|
|
997
996
|
) as progress_printer:
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
997
|
+
result = wait_with_progress(
|
|
998
|
+
run_init_handle,
|
|
999
|
+
timeout=timeout,
|
|
1000
|
+
display_progress=functools.partial(
|
|
1001
|
+
progress.loop_printing_operation_stats,
|
|
1002
|
+
progress_printer,
|
|
1003
|
+
backend.interface,
|
|
1004
|
+
),
|
|
1001
1005
|
)
|
|
1002
1006
|
|
|
1003
|
-
try:
|
|
1004
|
-
result = wait_with_progress(
|
|
1005
|
-
run_init_handle,
|
|
1006
|
-
timeout=timeout,
|
|
1007
|
-
display_progress=display_init_message,
|
|
1008
|
-
)
|
|
1009
|
-
|
|
1010
1007
|
except TimeoutError:
|
|
1011
1008
|
run_init_handle.cancel(backend.interface)
|
|
1012
1009
|
|
wandb/sdk/wandb_run.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import functools
|
|
5
4
|
import glob
|
|
6
5
|
import json
|
|
@@ -33,7 +32,6 @@ from wandb.errors import CommError, UsageError
|
|
|
33
32
|
from wandb.errors.links import url_registry
|
|
34
33
|
from wandb.integration.torch import wandb_torch
|
|
35
34
|
from wandb.plot import CustomChart, Visualize
|
|
36
|
-
from wandb.proto import wandb_internal_pb2 as pb
|
|
37
35
|
from wandb.proto.wandb_deprecated import Deprecated
|
|
38
36
|
from wandb.proto.wandb_internal_pb2 import (
|
|
39
37
|
MetricRecord,
|
|
@@ -44,7 +42,7 @@ from wandb.proto.wandb_internal_pb2 import (
|
|
|
44
42
|
from wandb.sdk.artifacts._internal_artifact import InternalArtifact
|
|
45
43
|
from wandb.sdk.artifacts.artifact import Artifact
|
|
46
44
|
from wandb.sdk.internal import job_builder
|
|
47
|
-
from wandb.sdk.lib import
|
|
45
|
+
from wandb.sdk.lib import wb_logging
|
|
48
46
|
from wandb.sdk.lib.import_hooks import (
|
|
49
47
|
register_post_import_hook,
|
|
50
48
|
unregister_post_import_hook,
|
|
@@ -489,7 +487,7 @@ class Run:
|
|
|
489
487
|
|
|
490
488
|
You can log data to a run with `wandb.Run.log()`. Anything you log using
|
|
491
489
|
`wandb.Run.log()` is sent to that run. See
|
|
492
|
-
[Create an experiment](https://docs.wandb.ai/guides/track/
|
|
490
|
+
[Create an experiment](https://docs.wandb.ai/guides/track/create-an-experiment/) or
|
|
493
491
|
[`wandb.init`](https://docs.wandb.ai/ref/python/init/) API reference page
|
|
494
492
|
or more information.
|
|
495
493
|
|
|
@@ -1574,15 +1572,13 @@ class Run:
|
|
|
1574
1572
|
|
|
1575
1573
|
@_log_to_run
|
|
1576
1574
|
def _console_callback(self, name: str, data: str) -> None:
|
|
1577
|
-
# logger.info("console callback: %s, %s", name, data)
|
|
1578
1575
|
if self._backend and self._backend.interface:
|
|
1579
|
-
|
|
1576
|
+
# nowait=True so that this can be called from an asyncio context.
|
|
1577
|
+
self._backend.interface.publish_output(name, data, nowait=True)
|
|
1580
1578
|
|
|
1581
1579
|
@_log_to_run
|
|
1582
1580
|
@_raise_if_finished
|
|
1583
1581
|
def _console_raw_callback(self, name: str, data: str) -> None:
|
|
1584
|
-
# logger.info("console callback: %s, %s", name, data)
|
|
1585
|
-
|
|
1586
1582
|
# NOTE: console output is only allowed on the process which installed the callback
|
|
1587
1583
|
# this will prevent potential corruption in the socket to the service. Other methods
|
|
1588
1584
|
# are protected by the _attach run decorator, but this callback was installed on the
|
|
@@ -1592,7 +1588,8 @@ class Run:
|
|
|
1592
1588
|
return
|
|
1593
1589
|
|
|
1594
1590
|
if self._backend and self._backend.interface:
|
|
1595
|
-
|
|
1591
|
+
# nowait=True so that this can be called from an asyncio context.
|
|
1592
|
+
self._backend.interface.publish_output_raw(name, data, nowait=True)
|
|
1596
1593
|
|
|
1597
1594
|
@_log_to_run
|
|
1598
1595
|
def _tensorboard_callback(
|
|
@@ -1861,7 +1858,7 @@ class Run:
|
|
|
1861
1858
|
run.log({"accuracy": 0.8}, step=current_step)
|
|
1862
1859
|
current_step += 1
|
|
1863
1860
|
run.log({"train-loss": 0.4}, step=current_step)
|
|
1864
|
-
run.log({"accuracy": 0.9}, step=current_step)
|
|
1861
|
+
run.log({"accuracy": 0.9}, step=current_step, commit=True)
|
|
1865
1862
|
```
|
|
1866
1863
|
|
|
1867
1864
|
Args:
|
|
@@ -2051,6 +2048,9 @@ class Run:
|
|
|
2051
2048
|
When given an absolute path or glob and no `base_path`, one
|
|
2052
2049
|
directory level is preserved as in the example above.
|
|
2053
2050
|
|
|
2051
|
+
Files are automatically deduplicated: calling `save()` multiple times
|
|
2052
|
+
on the same file without modifications will not re-upload it.
|
|
2053
|
+
|
|
2054
2054
|
Args:
|
|
2055
2055
|
glob_str: A relative or absolute path or Unix glob.
|
|
2056
2056
|
base_path: A path to use to infer a directory structure; see examples.
|
|
@@ -2075,10 +2075,10 @@ class Run:
|
|
|
2075
2075
|
run.save("these/are/myfiles/*", base_path="these")
|
|
2076
2076
|
# => Saves files in an "are/myfiles/" folder in the run.
|
|
2077
2077
|
|
|
2078
|
-
run.save("/
|
|
2078
|
+
run.save("/Users/username/Documents/run123/*.txt")
|
|
2079
2079
|
# => Saves files in a "run123/" folder in the run. See note below.
|
|
2080
2080
|
|
|
2081
|
-
run.save("/
|
|
2081
|
+
run.save("/Users/username/Documents/run123/*.txt", base_path="/Users")
|
|
2082
2082
|
# => Saves files in a "username/Documents/run123/" folder in the run.
|
|
2083
2083
|
|
|
2084
2084
|
run.save("files/*/saveme.txt")
|
|
@@ -2679,41 +2679,6 @@ class Run:
|
|
|
2679
2679
|
else:
|
|
2680
2680
|
return artifact
|
|
2681
2681
|
|
|
2682
|
-
async def _display_finish_stats(
|
|
2683
|
-
self,
|
|
2684
|
-
progress_printer: progress.ProgressPrinter,
|
|
2685
|
-
) -> None:
|
|
2686
|
-
last_result: Result | None = None
|
|
2687
|
-
|
|
2688
|
-
async def loop_update_printer() -> None:
|
|
2689
|
-
while True:
|
|
2690
|
-
if last_result:
|
|
2691
|
-
progress_printer.update(
|
|
2692
|
-
[last_result.response.poll_exit_response],
|
|
2693
|
-
)
|
|
2694
|
-
await asyncio.sleep(0.1)
|
|
2695
|
-
|
|
2696
|
-
async def loop_poll_exit() -> None:
|
|
2697
|
-
nonlocal last_result
|
|
2698
|
-
assert self._backend and self._backend.interface
|
|
2699
|
-
|
|
2700
|
-
while True:
|
|
2701
|
-
handle = await self._backend.interface.deliver_async(
|
|
2702
|
-
pb.Record(request=pb.Request(poll_exit=pb.PollExitRequest()))
|
|
2703
|
-
)
|
|
2704
|
-
|
|
2705
|
-
time_start = time.monotonic()
|
|
2706
|
-
last_result = await handle.wait_async(timeout=None)
|
|
2707
|
-
|
|
2708
|
-
# Update at most once a second.
|
|
2709
|
-
time_elapsed = time.monotonic() - time_start
|
|
2710
|
-
if time_elapsed < 1:
|
|
2711
|
-
await asyncio.sleep(1 - time_elapsed)
|
|
2712
|
-
|
|
2713
|
-
async with asyncio_compat.open_task_group() as task_group:
|
|
2714
|
-
task_group.start_soon(loop_update_printer())
|
|
2715
|
-
task_group.start_soon(loop_poll_exit())
|
|
2716
|
-
|
|
2717
2682
|
def _on_finish(self) -> None:
|
|
2718
2683
|
trigger.call("on_finished")
|
|
2719
2684
|
|
|
@@ -2738,8 +2703,9 @@ class Run:
|
|
|
2738
2703
|
exit_handle,
|
|
2739
2704
|
timeout=None,
|
|
2740
2705
|
display_progress=functools.partial(
|
|
2741
|
-
|
|
2706
|
+
progress.loop_printing_operation_stats,
|
|
2742
2707
|
progress_printer,
|
|
2708
|
+
self._backend.interface,
|
|
2743
2709
|
),
|
|
2744
2710
|
)
|
|
2745
2711
|
|