wandb 0.22.2__py3-none-win_arm64.whl → 0.22.3__py3-none-win_arm64.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 +2 -2
- 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 +8 -2
- wandb/cli/beta_leet.py +2 -1
- wandb/cli/beta_sync.py +1 -1
- wandb/errors/term.py +8 -8
- wandb/jupyter.py +0 -51
- wandb/old/settings.py +6 -6
- wandb/proto/v3/wandb_internal_pb2.py +351 -352
- 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_internal_pb2.py +351 -352
- 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_internal_pb2.py +351 -352
- 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_internal_pb2.py +351 -352
- 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/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 +220 -215
- wandb/sdk/artifacts/artifact_file_cache.py +1 -1
- wandb/sdk/artifacts/artifact_manifest.py +37 -32
- wandb/sdk/artifacts/artifact_manifest_entry.py +80 -125
- wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +43 -61
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +8 -6
- wandb/sdk/data_types/image.py +2 -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/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 +159 -64
- 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 +10 -13
- wandb/sdk/wandb_run.py +9 -46
- wandb/sdk/wandb_settings.py +102 -19
- {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/METADATA +2 -1
- {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/RECORD +135 -134
- 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.2.dist-info → wandb-0.22.3.dist-info}/WHEEL +0 -0
- {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/entry_points.txt +0 -0
- {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/licenses/LICENSE +0 -0
wandb/apis/public/artifacts.py
CHANGED
|
@@ -7,14 +7,25 @@ collections.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
|
-
import re
|
|
11
10
|
from copy import copy
|
|
12
|
-
from typing import
|
|
11
|
+
from typing import (
|
|
12
|
+
TYPE_CHECKING,
|
|
13
|
+
Any,
|
|
14
|
+
ClassVar,
|
|
15
|
+
Collection,
|
|
16
|
+
Iterable,
|
|
17
|
+
List,
|
|
18
|
+
Literal,
|
|
19
|
+
Mapping,
|
|
20
|
+
Sequence,
|
|
21
|
+
)
|
|
13
22
|
|
|
14
23
|
from typing_extensions import override
|
|
15
|
-
from wandb_gql import
|
|
24
|
+
from wandb_gql import gql
|
|
16
25
|
|
|
17
26
|
import wandb
|
|
27
|
+
from wandb._iterutils import always_list
|
|
28
|
+
from wandb._pydantic import ConnectionWithTotal, Edge
|
|
18
29
|
from wandb._strutils import nameof
|
|
19
30
|
from wandb.apis import public
|
|
20
31
|
from wandb.apis.normalize import normalize_exceptions
|
|
@@ -23,13 +34,12 @@ from wandb.errors.term import termlog
|
|
|
23
34
|
from wandb.proto.wandb_deprecated import Deprecated
|
|
24
35
|
from wandb.proto.wandb_internal_pb2 import ServerFeature
|
|
25
36
|
from wandb.sdk.artifacts._generated import (
|
|
37
|
+
ADD_ARTIFACT_COLLECTION_TAGS_GQL,
|
|
26
38
|
ARTIFACT_COLLECTION_MEMBERSHIP_FILES_GQL,
|
|
27
39
|
ARTIFACT_VERSION_FILES_GQL,
|
|
28
|
-
|
|
29
|
-
DELETE_ARTIFACT_COLLECTION_TAG_ASSIGNMENTS_GQL,
|
|
40
|
+
DELETE_ARTIFACT_COLLECTION_TAGS_GQL,
|
|
30
41
|
DELETE_ARTIFACT_PORTFOLIO_GQL,
|
|
31
42
|
DELETE_ARTIFACT_SEQUENCE_GQL,
|
|
32
|
-
MOVE_ARTIFACT_COLLECTION_GQL,
|
|
33
43
|
PROJECT_ARTIFACT_COLLECTION_GQL,
|
|
34
44
|
PROJECT_ARTIFACT_COLLECTIONS_GQL,
|
|
35
45
|
PROJECT_ARTIFACT_TYPE_GQL,
|
|
@@ -37,36 +47,42 @@ from wandb.sdk.artifacts._generated import (
|
|
|
37
47
|
PROJECT_ARTIFACTS_GQL,
|
|
38
48
|
RUN_INPUT_ARTIFACTS_GQL,
|
|
39
49
|
RUN_OUTPUT_ARTIFACTS_GQL,
|
|
50
|
+
UPDATE_ARTIFACT_COLLECTION_TYPE_GQL,
|
|
40
51
|
UPDATE_ARTIFACT_PORTFOLIO_GQL,
|
|
41
52
|
UPDATE_ARTIFACT_SEQUENCE_GQL,
|
|
53
|
+
ArtifactCollectionFragment,
|
|
42
54
|
ArtifactCollectionMembershipFiles,
|
|
43
|
-
|
|
44
|
-
ArtifactsFragment,
|
|
55
|
+
ArtifactFragment,
|
|
45
56
|
ArtifactTypeFragment,
|
|
46
|
-
ArtifactTypesFragment,
|
|
47
57
|
ArtifactVersionFiles,
|
|
48
|
-
|
|
58
|
+
CreateArtifactCollectionTagAssignmentsInput,
|
|
59
|
+
DeleteArtifactCollectionTagAssignmentsInput,
|
|
60
|
+
MoveArtifactSequenceInput,
|
|
49
61
|
ProjectArtifactCollection,
|
|
50
62
|
ProjectArtifactCollections,
|
|
51
63
|
ProjectArtifacts,
|
|
52
64
|
ProjectArtifactType,
|
|
53
65
|
ProjectArtifactTypes,
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
UpdateArtifactPortfolioInput,
|
|
67
|
+
UpdateArtifactSequenceInput,
|
|
56
68
|
)
|
|
57
69
|
from wandb.sdk.artifacts._gqlutils import omit_artifact_fields
|
|
58
|
-
from wandb.sdk.artifacts.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
from wandb.sdk.artifacts._models import ArtifactCollectionData
|
|
71
|
+
from wandb.sdk.artifacts._models.pagination import (
|
|
72
|
+
ArtifactCollectionConnection,
|
|
73
|
+
ArtifactFileConnection,
|
|
74
|
+
ArtifactTypeConnection,
|
|
75
|
+
RunArtifactConnection,
|
|
63
76
|
)
|
|
77
|
+
from wandb.sdk.artifacts._validators import FullArtifactPath, validate_artifact_type
|
|
64
78
|
from wandb.sdk.internal.internal_api import Api as InternalApi
|
|
65
79
|
from wandb.sdk.lib import deprecate
|
|
66
80
|
|
|
67
81
|
from .utils import gql_compat
|
|
68
82
|
|
|
69
83
|
if TYPE_CHECKING:
|
|
84
|
+
from wandb_gql import Client
|
|
85
|
+
|
|
70
86
|
from wandb.sdk.artifacts.artifact import Artifact
|
|
71
87
|
|
|
72
88
|
from . import RetryingClient, Run
|
|
@@ -80,7 +96,7 @@ class ArtifactTypes(Paginator["ArtifactType"]):
|
|
|
80
96
|
|
|
81
97
|
QUERY = gql(PROJECT_ARTIFACT_TYPES_GQL)
|
|
82
98
|
|
|
83
|
-
last_response:
|
|
99
|
+
last_response: ArtifactTypeConnection | None
|
|
84
100
|
|
|
85
101
|
def __init__(
|
|
86
102
|
self,
|
|
@@ -108,7 +124,7 @@ class ArtifactTypes(Paginator["ArtifactType"]):
|
|
|
108
124
|
if not ((proj := result.project) and (conn := proj.artifact_types)):
|
|
109
125
|
raise ValueError(f"Unable to parse {nameof(type(self))!r} response data")
|
|
110
126
|
|
|
111
|
-
self.last_response =
|
|
127
|
+
self.last_response = ArtifactTypeConnection.model_validate(conn)
|
|
112
128
|
|
|
113
129
|
@property
|
|
114
130
|
def _length(self) -> None:
|
|
@@ -125,9 +141,7 @@ class ArtifactTypes(Paginator["ArtifactType"]):
|
|
|
125
141
|
|
|
126
142
|
<!-- lazydoc-ignore: internal -->
|
|
127
143
|
"""
|
|
128
|
-
|
|
129
|
-
return True
|
|
130
|
-
return self.last_response.page_info.has_next_page
|
|
144
|
+
return (conn := self.last_response) is None or conn.has_next
|
|
131
145
|
|
|
132
146
|
@property
|
|
133
147
|
def cursor(self) -> str | None:
|
|
@@ -135,9 +149,7 @@ class ArtifactTypes(Paginator["ArtifactType"]):
|
|
|
135
149
|
|
|
136
150
|
<!-- lazydoc-ignore: internal -->
|
|
137
151
|
"""
|
|
138
|
-
if self.last_response
|
|
139
|
-
return None
|
|
140
|
-
return self.last_response.edges[-1].cursor
|
|
152
|
+
return conn.next_cursor if (conn := self.last_response) else None
|
|
141
153
|
|
|
142
154
|
def update_variables(self) -> None:
|
|
143
155
|
"""Update the cursor variable for pagination.
|
|
@@ -160,10 +172,9 @@ class ArtifactTypes(Paginator["ArtifactType"]):
|
|
|
160
172
|
entity=self.entity,
|
|
161
173
|
project=self.project,
|
|
162
174
|
type_name=node.name,
|
|
163
|
-
attrs=node
|
|
175
|
+
attrs=node,
|
|
164
176
|
)
|
|
165
|
-
for
|
|
166
|
-
if edge.node and (node := ArtifactTypeFragment.model_validate(edge.node))
|
|
177
|
+
for node in self.last_response.nodes()
|
|
167
178
|
]
|
|
168
179
|
|
|
169
180
|
|
|
@@ -181,51 +192,50 @@ class ArtifactType:
|
|
|
181
192
|
<!-- lazydoc-ignore-init: internal -->
|
|
182
193
|
"""
|
|
183
194
|
|
|
195
|
+
_attrs: ArtifactTypeFragment
|
|
196
|
+
|
|
184
197
|
def __init__(
|
|
185
198
|
self,
|
|
186
199
|
client: Client,
|
|
187
200
|
entity: str,
|
|
188
201
|
project: str,
|
|
189
202
|
type_name: str,
|
|
190
|
-
attrs:
|
|
203
|
+
attrs: ArtifactTypeFragment | None = None,
|
|
191
204
|
):
|
|
192
205
|
self.client = client
|
|
193
206
|
self.entity = entity
|
|
194
207
|
self.project = project
|
|
195
208
|
self.type = type_name
|
|
196
|
-
self._attrs = attrs
|
|
197
|
-
if self._attrs is None:
|
|
198
|
-
self.load()
|
|
199
209
|
|
|
200
|
-
|
|
210
|
+
# FIXME: Make this lazy, so we don't (re-)fetch the attributes until they are needed
|
|
211
|
+
self._attrs = ArtifactTypeFragment.model_validate(attrs or self.load())
|
|
212
|
+
|
|
213
|
+
def load(self) -> ArtifactTypeFragment:
|
|
201
214
|
"""Load the artifact type attributes from W&B.
|
|
202
215
|
|
|
203
216
|
<!-- lazydoc-ignore: internal -->
|
|
204
217
|
"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
)
|
|
218
|
+
gql_op = gql(PROJECT_ARTIFACT_TYPE_GQL)
|
|
219
|
+
gql_vars = {
|
|
220
|
+
"entityName": self.entity,
|
|
221
|
+
"projectName": self.project,
|
|
222
|
+
"artifactTypeName": self.type,
|
|
223
|
+
}
|
|
224
|
+
data = self.client.execute(gql_op, variable_values=gql_vars)
|
|
213
225
|
result = ProjectArtifactType.model_validate(data)
|
|
214
226
|
if not ((proj := result.project) and (artifact_type := proj.artifact_type)):
|
|
215
|
-
raise ValueError(f"Could not find artifact type {self.type}")
|
|
216
|
-
|
|
217
|
-
self._attrs = artifact_type.model_dump(exclude_unset=True)
|
|
218
|
-
return self._attrs
|
|
227
|
+
raise ValueError(f"Could not find artifact type {self.type!r}")
|
|
228
|
+
return ArtifactTypeFragment.model_validate(artifact_type)
|
|
219
229
|
|
|
220
230
|
@property
|
|
221
231
|
def id(self) -> str:
|
|
222
232
|
"""The unique identifier of the artifact type."""
|
|
223
|
-
return self._attrs
|
|
233
|
+
return self._attrs.id
|
|
224
234
|
|
|
225
235
|
@property
|
|
226
236
|
def name(self) -> str:
|
|
227
237
|
"""The name of the artifact type."""
|
|
228
|
-
return self._attrs
|
|
238
|
+
return self._attrs.name
|
|
229
239
|
|
|
230
240
|
@normalize_exceptions
|
|
231
241
|
def collections(self, per_page: int = 50) -> ArtifactCollections:
|
|
@@ -235,7 +245,12 @@ class ArtifactType:
|
|
|
235
245
|
per_page (int): The number of artifact collections to fetch per page.
|
|
236
246
|
Default is 50.
|
|
237
247
|
"""
|
|
238
|
-
return ArtifactCollections(
|
|
248
|
+
return ArtifactCollections(
|
|
249
|
+
self.client,
|
|
250
|
+
entity=self.entity,
|
|
251
|
+
project=self.project,
|
|
252
|
+
type_name=self.type,
|
|
253
|
+
)
|
|
239
254
|
|
|
240
255
|
def collection(self, name: str) -> ArtifactCollection:
|
|
241
256
|
"""Get a specific artifact collection by name.
|
|
@@ -244,7 +259,11 @@ class ArtifactType:
|
|
|
244
259
|
name (str): The name of the artifact collection to retrieve.
|
|
245
260
|
"""
|
|
246
261
|
return ArtifactCollection(
|
|
247
|
-
self.client,
|
|
262
|
+
self.client,
|
|
263
|
+
entity=self.entity,
|
|
264
|
+
project=self.project,
|
|
265
|
+
name=name,
|
|
266
|
+
type=self.type,
|
|
248
267
|
)
|
|
249
268
|
|
|
250
269
|
def __repr__(self) -> str:
|
|
@@ -264,7 +283,7 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
264
283
|
<!-- lazydoc-ignore-init: internal -->
|
|
265
284
|
"""
|
|
266
285
|
|
|
267
|
-
last_response:
|
|
286
|
+
last_response: ArtifactCollectionConnection | None
|
|
268
287
|
|
|
269
288
|
def __init__(
|
|
270
289
|
self,
|
|
@@ -278,11 +297,7 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
278
297
|
self.project = project
|
|
279
298
|
self.type_name = type_name
|
|
280
299
|
|
|
281
|
-
|
|
282
|
-
"entityName": entity,
|
|
283
|
-
"projectName": project,
|
|
284
|
-
"artifactTypeName": type_name,
|
|
285
|
-
}
|
|
300
|
+
variables = {"entity": entity, "project": project, "artifactType": type_name}
|
|
286
301
|
|
|
287
302
|
if server_supports_artifact_collections_gql_edges(client):
|
|
288
303
|
rename_fields = None
|
|
@@ -293,7 +308,7 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
293
308
|
PROJECT_ARTIFACT_COLLECTIONS_GQL, rename_fields=rename_fields
|
|
294
309
|
)
|
|
295
310
|
|
|
296
|
-
super().__init__(client,
|
|
311
|
+
super().__init__(client, variables, per_page)
|
|
297
312
|
|
|
298
313
|
@override
|
|
299
314
|
def _update_response(self) -> None:
|
|
@@ -304,12 +319,12 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
304
319
|
# Extract the inner `*Connection` result for faster/easier access.
|
|
305
320
|
if not (
|
|
306
321
|
(proj := result.project)
|
|
307
|
-
and (
|
|
308
|
-
and (conn :=
|
|
322
|
+
and (artifact_type := proj.artifact_type)
|
|
323
|
+
and (conn := artifact_type.artifact_collections)
|
|
309
324
|
):
|
|
310
325
|
raise ValueError(f"Unable to parse {nameof(type(self))!r} response data")
|
|
311
326
|
|
|
312
|
-
self.last_response =
|
|
327
|
+
self.last_response = ArtifactCollectionConnection.model_validate(conn)
|
|
313
328
|
|
|
314
329
|
@property
|
|
315
330
|
def _length(self) -> int:
|
|
@@ -322,24 +337,20 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
322
337
|
return self.last_response.total_count
|
|
323
338
|
|
|
324
339
|
@property
|
|
325
|
-
def more(self):
|
|
340
|
+
def more(self) -> bool:
|
|
326
341
|
"""Returns whether there are more artifacts to fetch.
|
|
327
342
|
|
|
328
343
|
<!-- lazydoc-ignore: internal -->
|
|
329
344
|
"""
|
|
330
|
-
|
|
331
|
-
return True
|
|
332
|
-
return self.last_response.page_info.has_next_page
|
|
345
|
+
return (conn := self.last_response) is None or conn.has_next
|
|
333
346
|
|
|
334
347
|
@property
|
|
335
|
-
def cursor(self):
|
|
348
|
+
def cursor(self) -> str | None:
|
|
336
349
|
"""Returns the cursor for the next page of results.
|
|
337
350
|
|
|
338
351
|
<!-- lazydoc-ignore: internal -->
|
|
339
352
|
"""
|
|
340
|
-
if self.last_response
|
|
341
|
-
return None
|
|
342
|
-
return self.last_response.edges[-1].cursor
|
|
353
|
+
return conn.next_cursor if (conn := self.last_response) else None
|
|
343
354
|
|
|
344
355
|
def update_variables(self) -> None:
|
|
345
356
|
"""Update the cursor variable for pagination.
|
|
@@ -358,13 +369,14 @@ class ArtifactCollections(SizedPaginator["ArtifactCollection"]):
|
|
|
358
369
|
return [
|
|
359
370
|
ArtifactCollection(
|
|
360
371
|
client=self.client,
|
|
361
|
-
entity=
|
|
362
|
-
project=
|
|
372
|
+
entity=node.project.entity.name,
|
|
373
|
+
project=node.project.name,
|
|
363
374
|
name=node.name,
|
|
364
|
-
type=
|
|
375
|
+
type=node.type.name,
|
|
376
|
+
attrs=node,
|
|
365
377
|
)
|
|
366
|
-
for
|
|
367
|
-
if
|
|
378
|
+
for node in self.last_response.nodes()
|
|
379
|
+
if node.project
|
|
368
380
|
]
|
|
369
381
|
|
|
370
382
|
|
|
@@ -385,6 +397,12 @@ class ArtifactCollection:
|
|
|
385
397
|
<!-- lazydoc-ignore-init: internal -->
|
|
386
398
|
"""
|
|
387
399
|
|
|
400
|
+
_saved: ArtifactCollectionData
|
|
401
|
+
"""The saved artifact collection data as last fetched from the W&B server."""
|
|
402
|
+
|
|
403
|
+
_current: ArtifactCollectionData
|
|
404
|
+
"""The local, editable artifact collection data."""
|
|
405
|
+
|
|
388
406
|
def __init__(
|
|
389
407
|
self,
|
|
390
408
|
client: Client,
|
|
@@ -393,32 +411,34 @@ class ArtifactCollection:
|
|
|
393
411
|
name: str,
|
|
394
412
|
type: str,
|
|
395
413
|
organization: str | None = None,
|
|
396
|
-
attrs:
|
|
397
|
-
is_sequence: bool | None = None,
|
|
414
|
+
attrs: ArtifactCollectionFragment | None = None,
|
|
398
415
|
):
|
|
399
416
|
self.client = client
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
self.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
self.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (attrs is None) or (is_sequence is None):
|
|
410
|
-
self.load()
|
|
411
|
-
self._aliases = [a["node"]["alias"] for a in self._attrs["aliases"]["edges"]]
|
|
412
|
-
self._description = self._attrs["description"]
|
|
413
|
-
self._created_at = self._attrs["createdAt"]
|
|
414
|
-
self._tags = [a["node"]["name"] for a in self._attrs["tags"]["edges"]]
|
|
415
|
-
self._saved_tags = copy(self._tags)
|
|
417
|
+
|
|
418
|
+
# FIXME: Make this lazy, so we don't (re-)fetch the attributes until they are needed
|
|
419
|
+
fragment = attrs or self.load(entity, project, type, name)
|
|
420
|
+
|
|
421
|
+
# Separate "saved" vs "current" copies of the artifact collection data
|
|
422
|
+
validated = ArtifactCollectionData.from_fragment(fragment)
|
|
423
|
+
self._saved = validated
|
|
424
|
+
self._current = validated.model_copy(deep=True)
|
|
425
|
+
|
|
416
426
|
self.organization = organization
|
|
417
427
|
|
|
418
428
|
@property
|
|
419
429
|
def id(self) -> str:
|
|
420
430
|
"""The unique identifier of the artifact collection."""
|
|
421
|
-
return self.
|
|
431
|
+
return self._current.id
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def entity(self) -> str:
|
|
435
|
+
"""The entity (user or team) that owns the project."""
|
|
436
|
+
return self._current.entity
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
def project(self) -> str:
|
|
440
|
+
"""The project that contains the artifact collection."""
|
|
441
|
+
return self._current.project
|
|
422
442
|
|
|
423
443
|
@normalize_exceptions
|
|
424
444
|
def artifacts(self, per_page: int = 50) -> Artifacts:
|
|
@@ -427,23 +447,26 @@ class ArtifactCollection:
|
|
|
427
447
|
client=self.client,
|
|
428
448
|
entity=self.entity,
|
|
429
449
|
project=self.project,
|
|
430
|
-
|
|
431
|
-
|
|
450
|
+
# Use the saved name and type, since they're mutable and may have been edited locally.
|
|
451
|
+
collection_name=self._saved.name,
|
|
452
|
+
type=self._saved.type,
|
|
432
453
|
per_page=per_page,
|
|
433
454
|
)
|
|
434
455
|
|
|
435
456
|
@property
|
|
436
457
|
def aliases(self) -> list[str]:
|
|
437
458
|
"""Artifact Collection Aliases."""
|
|
438
|
-
return self.
|
|
459
|
+
return list(self._saved.aliases)
|
|
439
460
|
|
|
440
461
|
@property
|
|
441
462
|
def created_at(self) -> str:
|
|
442
463
|
"""The creation date of the artifact collection."""
|
|
443
|
-
return self.
|
|
464
|
+
return self._saved.created_at
|
|
444
465
|
|
|
445
|
-
def load(
|
|
446
|
-
|
|
466
|
+
def load(
|
|
467
|
+
self, entity: str, project: str, artifact_type: str, name: str
|
|
468
|
+
) -> ArtifactCollectionFragment:
|
|
469
|
+
"""Fetch and return the validated artifact collection data from W&B.
|
|
447
470
|
|
|
448
471
|
<!-- lazydoc-ignore: internal -->
|
|
449
472
|
"""
|
|
@@ -452,34 +475,25 @@ class ArtifactCollection:
|
|
|
452
475
|
else:
|
|
453
476
|
rename_fields = {"artifactCollection": "artifactSequence"}
|
|
454
477
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
variable_values={
|
|
458
|
-
"entityName": self.entity,
|
|
459
|
-
"projectName": self.project,
|
|
460
|
-
"artifactTypeName": self._saved_type,
|
|
461
|
-
"artifactCollectionName": self._saved_name,
|
|
462
|
-
},
|
|
478
|
+
gql_op = gql_compat(
|
|
479
|
+
PROJECT_ARTIFACT_COLLECTION_GQL, rename_fields=rename_fields
|
|
463
480
|
)
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
481
|
+
gql_vars = {
|
|
482
|
+
"entity": entity,
|
|
483
|
+
"project": project,
|
|
484
|
+
"artifactType": artifact_type,
|
|
485
|
+
"name": name,
|
|
486
|
+
}
|
|
487
|
+
data = self.client.execute(gql_op, variable_values=gql_vars)
|
|
488
|
+
result = ProjectArtifactCollection.model_validate(data)
|
|
467
489
|
if not (
|
|
468
490
|
result.project
|
|
469
491
|
and (proj := result.project)
|
|
470
492
|
and (type_ := proj.artifact_type)
|
|
471
493
|
and (collection := type_.artifact_collection)
|
|
472
494
|
):
|
|
473
|
-
raise ValueError(f"Could not find artifact type {
|
|
474
|
-
|
|
475
|
-
sequence = type_.artifact_sequence
|
|
476
|
-
self._is_sequence = (
|
|
477
|
-
sequence is not None
|
|
478
|
-
) and sequence.typename__ == SOURCE_ARTIFACT_COLLECTION_TYPE
|
|
479
|
-
|
|
480
|
-
if self._attrs is None:
|
|
481
|
-
self._attrs = collection.model_dump(exclude_unset=True)
|
|
482
|
-
return self._attrs
|
|
495
|
+
raise ValueError(f"Could not find artifact type {artifact_type!s}")
|
|
496
|
+
return collection
|
|
483
497
|
|
|
484
498
|
@normalize_exceptions
|
|
485
499
|
def change_type(self, new_type: str) -> None:
|
|
@@ -489,174 +503,178 @@ class ArtifactCollection:
|
|
|
489
503
|
warning_message="ArtifactCollection.change_type(type) is deprecated, use ArtifactCollection.save() instead.",
|
|
490
504
|
)
|
|
491
505
|
|
|
492
|
-
if self.
|
|
506
|
+
if (old_type := self._saved.type) != new_type:
|
|
493
507
|
try:
|
|
494
|
-
validate_artifact_type(
|
|
508
|
+
validate_artifact_type(old_type, self.name)
|
|
495
509
|
except ValueError as e:
|
|
496
510
|
raise ValueError(
|
|
497
|
-
f"The current type
|
|
511
|
+
f"The current type {old_type!r} is an internal type and cannot be changed."
|
|
498
512
|
) from e
|
|
499
513
|
|
|
500
514
|
# Check that the new type is not going to conflict with internal types
|
|
501
|
-
validate_artifact_type(new_type, self.name)
|
|
515
|
+
new_type = validate_artifact_type(new_type, self.name)
|
|
502
516
|
|
|
503
517
|
if not self.is_sequence():
|
|
504
518
|
raise ValueError("Artifact collection needs to be a sequence")
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
"destinationArtifactTypeName": new_type,
|
|
513
|
-
},
|
|
519
|
+
|
|
520
|
+
termlog(f"Changing artifact collection type of {old_type!r} to {new_type!r}")
|
|
521
|
+
|
|
522
|
+
gql_op = gql(UPDATE_ARTIFACT_COLLECTION_TYPE_GQL)
|
|
523
|
+
gql_input = MoveArtifactSequenceInput(
|
|
524
|
+
artifact_sequence_id=self.id,
|
|
525
|
+
destination_artifact_type_name=new_type,
|
|
514
526
|
)
|
|
515
|
-
self.
|
|
516
|
-
self.
|
|
527
|
+
self.client.execute(gql_op, variable_values={"input": gql_input.model_dump()})
|
|
528
|
+
self._saved.type = new_type
|
|
529
|
+
self._current.type = new_type
|
|
517
530
|
|
|
518
531
|
def is_sequence(self) -> bool:
|
|
519
532
|
"""Return whether the artifact collection is a sequence."""
|
|
520
|
-
return self.
|
|
533
|
+
return self._saved.is_sequence
|
|
521
534
|
|
|
522
535
|
@normalize_exceptions
|
|
523
536
|
def delete(self) -> None:
|
|
524
537
|
"""Delete the entire artifact collection."""
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
else DELETE_ARTIFACT_PORTFOLIO_GQL
|
|
530
|
-
),
|
|
531
|
-
variable_values={"id": self.id},
|
|
538
|
+
gql_op = gql(
|
|
539
|
+
DELETE_ARTIFACT_SEQUENCE_GQL
|
|
540
|
+
if self.is_sequence()
|
|
541
|
+
else DELETE_ARTIFACT_PORTFOLIO_GQL
|
|
532
542
|
)
|
|
543
|
+
self.client.execute(gql_op, variable_values={"id": self.id})
|
|
533
544
|
|
|
534
545
|
@property
|
|
535
|
-
def description(self) -> str:
|
|
546
|
+
def description(self) -> str | None:
|
|
536
547
|
"""A description of the artifact collection."""
|
|
537
|
-
return self.
|
|
548
|
+
return self._current.description
|
|
538
549
|
|
|
539
550
|
@description.setter
|
|
540
551
|
def description(self, description: str | None) -> None:
|
|
541
552
|
"""Set the description of the artifact collection."""
|
|
542
|
-
self.
|
|
553
|
+
self._current.description = description
|
|
543
554
|
|
|
544
555
|
@property
|
|
545
556
|
def tags(self) -> list[str]:
|
|
546
557
|
"""The tags associated with the artifact collection."""
|
|
547
|
-
return self.
|
|
558
|
+
return self._current.tags
|
|
548
559
|
|
|
549
560
|
@tags.setter
|
|
550
|
-
def tags(self, tags:
|
|
561
|
+
def tags(self, tags: Collection[str]) -> None:
|
|
551
562
|
"""Set the tags associated with the artifact collection."""
|
|
552
|
-
|
|
553
|
-
raise ValueError(
|
|
554
|
-
"Tags must only contain alphanumeric characters or underscores separated by spaces or hyphens"
|
|
555
|
-
)
|
|
556
|
-
self._tags = tags
|
|
563
|
+
self._current.tags = tags
|
|
557
564
|
|
|
558
565
|
@property
|
|
559
566
|
def name(self) -> str:
|
|
560
567
|
"""The name of the artifact collection."""
|
|
561
|
-
return self.
|
|
568
|
+
return self._current.name
|
|
562
569
|
|
|
563
570
|
@name.setter
|
|
564
571
|
def name(self, name: str) -> None:
|
|
565
572
|
"""Set the name of the artifact collection."""
|
|
566
|
-
self.
|
|
573
|
+
self._current.name = name
|
|
567
574
|
|
|
568
575
|
@property
|
|
569
576
|
def type(self):
|
|
570
577
|
"""Returns the type of the artifact collection."""
|
|
571
|
-
return self.
|
|
578
|
+
return self._current.type
|
|
572
579
|
|
|
573
580
|
@type.setter
|
|
574
|
-
def type(self, type:
|
|
581
|
+
def type(self, type: str) -> None:
|
|
575
582
|
"""Set the type of the artifact collection."""
|
|
576
583
|
if not self.is_sequence():
|
|
577
584
|
raise ValueError(
|
|
578
585
|
"Type can only be changed if the artifact collection is a sequence."
|
|
579
586
|
)
|
|
580
|
-
self.
|
|
587
|
+
self._current.type = type
|
|
581
588
|
|
|
582
589
|
def _update_collection(self) -> None:
|
|
583
|
-
self.
|
|
584
|
-
gql(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
590
|
+
if self.is_sequence():
|
|
591
|
+
gql_op = gql(UPDATE_ARTIFACT_SEQUENCE_GQL)
|
|
592
|
+
gql_input = UpdateArtifactSequenceInput(
|
|
593
|
+
artifact_sequence_id=self.id,
|
|
594
|
+
name=self.name,
|
|
595
|
+
description=self.description,
|
|
596
|
+
)
|
|
597
|
+
else:
|
|
598
|
+
gql_op = gql(UPDATE_ARTIFACT_PORTFOLIO_GQL)
|
|
599
|
+
gql_input = UpdateArtifactPortfolioInput(
|
|
600
|
+
artifact_portfolio_id=self.id,
|
|
601
|
+
name=self.name,
|
|
602
|
+
description=self.description,
|
|
603
|
+
)
|
|
604
|
+
self.client.execute(gql_op, variable_values={"input": gql_input.model_dump()})
|
|
605
|
+
self._saved.name = self._current.name
|
|
596
606
|
|
|
597
607
|
def _update_collection_type(self) -> None:
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
"destinationArtifactTypeName": self.type,
|
|
603
|
-
},
|
|
608
|
+
gql_op = gql(UPDATE_ARTIFACT_COLLECTION_TYPE_GQL)
|
|
609
|
+
gql_input = MoveArtifactSequenceInput(
|
|
610
|
+
artifact_sequence_id=self.id,
|
|
611
|
+
destination_artifact_type_name=self.type,
|
|
604
612
|
)
|
|
605
|
-
self.
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
},
|
|
613
|
+
self.client.execute(gql_op, variable_values={"input": gql_input.model_dump()})
|
|
614
|
+
self._saved.type = self._current.type
|
|
615
|
+
|
|
616
|
+
def _add_tags(self, tag_names: Iterable[str]) -> None:
|
|
617
|
+
gql_op = gql(ADD_ARTIFACT_COLLECTION_TAGS_GQL)
|
|
618
|
+
gql_input = CreateArtifactCollectionTagAssignmentsInput(
|
|
619
|
+
entity_name=self.entity,
|
|
620
|
+
project_name=self.project,
|
|
621
|
+
artifact_collection_name=self._saved.name,
|
|
622
|
+
tags=[{"tagName": tag} for tag in tag_names],
|
|
616
623
|
)
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
},
|
|
624
|
+
self.client.execute(gql_op, variable_values={"input": gql_input.model_dump()})
|
|
625
|
+
|
|
626
|
+
def _delete_tags(self, tag_names: Iterable[str]) -> None:
|
|
627
|
+
gql_op = gql(DELETE_ARTIFACT_COLLECTION_TAGS_GQL)
|
|
628
|
+
gql_input = DeleteArtifactCollectionTagAssignmentsInput(
|
|
629
|
+
entity_name=self.entity,
|
|
630
|
+
project_name=self.project,
|
|
631
|
+
artifact_collection_name=self._saved.name,
|
|
632
|
+
tags=[{"tagName": tag} for tag in tag_names],
|
|
627
633
|
)
|
|
634
|
+
self.client.execute(gql_op, variable_values={"input": gql_input.model_dump()})
|
|
628
635
|
|
|
629
636
|
@normalize_exceptions
|
|
630
637
|
def save(self) -> None:
|
|
631
638
|
"""Persist any changes made to the artifact collection."""
|
|
632
|
-
if self.
|
|
639
|
+
if (old_type := self._saved.type) != (new_type := self.type):
|
|
633
640
|
try:
|
|
634
|
-
validate_artifact_type(
|
|
641
|
+
validate_artifact_type(new_type, self.name)
|
|
635
642
|
except ValueError as e:
|
|
636
|
-
|
|
643
|
+
reason = str(e)
|
|
644
|
+
raise ValueError(
|
|
645
|
+
f"Failed to save artifact collection {self.name!r}: {reason}"
|
|
646
|
+
) from e
|
|
637
647
|
try:
|
|
638
|
-
validate_artifact_type(
|
|
648
|
+
validate_artifact_type(old_type, self.name)
|
|
639
649
|
except ValueError as e:
|
|
650
|
+
reason = f"The current type {old_type!r} is an internal type and cannot be changed."
|
|
640
651
|
raise ValueError(
|
|
641
|
-
f"Failed to save artifact collection
|
|
642
|
-
f"The current type '{self._saved_type!r}' is an internal type and cannot be changed."
|
|
652
|
+
f"Failed to save artifact collection {self.name!r}: {reason}"
|
|
643
653
|
) from e
|
|
644
654
|
|
|
655
|
+
# FIXME: Consider consolidating the multiple GQL mutations into a single call.
|
|
645
656
|
self._update_collection()
|
|
646
657
|
|
|
647
|
-
if self.is_sequence() and (
|
|
658
|
+
if self.is_sequence() and (old_type != new_type):
|
|
648
659
|
self._update_collection_type()
|
|
649
660
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
self.
|
|
656
|
-
self._saved_tags = copy(self._tags)
|
|
661
|
+
if (new_tags := set(self._current.tags)) != (old_tags := set(self._saved.tags)):
|
|
662
|
+
if added_tags := (new_tags - old_tags):
|
|
663
|
+
self._add_tags(added_tags)
|
|
664
|
+
if deleted_tags := (old_tags - new_tags):
|
|
665
|
+
self._delete_tags(deleted_tags)
|
|
666
|
+
self._saved.tags = copy(new_tags)
|
|
657
667
|
|
|
658
668
|
def __repr__(self) -> str:
|
|
659
|
-
return f"<ArtifactCollection {self.
|
|
669
|
+
return f"<ArtifactCollection {self.name} ({self.type})>"
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
class _ArtifactEdge(Edge[ArtifactFragment]):
|
|
673
|
+
version: str # Extra field defined only on VersionedArtifactEdge
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
class _ArtifactConnection(ConnectionWithTotal[ArtifactFragment]):
|
|
677
|
+
edges: List[_ArtifactEdge] # noqa: UP006
|
|
660
678
|
|
|
661
679
|
|
|
662
680
|
class Artifacts(SizedPaginator["Artifact"]):
|
|
@@ -679,7 +697,7 @@ class Artifacts(SizedPaginator["Artifact"]):
|
|
|
679
697
|
<!-- lazydoc-ignore-init: internal -->
|
|
680
698
|
"""
|
|
681
699
|
|
|
682
|
-
last_response:
|
|
700
|
+
last_response: _ArtifactConnection | None
|
|
683
701
|
|
|
684
702
|
def __init__(
|
|
685
703
|
self,
|
|
@@ -698,7 +716,7 @@ class Artifacts(SizedPaginator["Artifact"]):
|
|
|
698
716
|
self.type = type
|
|
699
717
|
self.project = project
|
|
700
718
|
self.filters = {"state": "COMMITTED"} if filters is None else filters
|
|
701
|
-
self.tags =
|
|
719
|
+
self.tags = always_list(tags or [])
|
|
702
720
|
self.order = order
|
|
703
721
|
variables = {
|
|
704
722
|
"project": self.project,
|
|
@@ -736,7 +754,7 @@ class Artifacts(SizedPaginator["Artifact"]):
|
|
|
736
754
|
):
|
|
737
755
|
raise ValueError(f"Unable to parse {nameof(type(self))!r} response data")
|
|
738
756
|
|
|
739
|
-
self.last_response =
|
|
757
|
+
self.last_response = _ArtifactConnection.model_validate(conn)
|
|
740
758
|
|
|
741
759
|
@property
|
|
742
760
|
def _length(self) -> int:
|
|
@@ -754,9 +772,7 @@ class Artifacts(SizedPaginator["Artifact"]):
|
|
|
754
772
|
|
|
755
773
|
<!-- lazydoc-ignore: internal -->
|
|
756
774
|
"""
|
|
757
|
-
|
|
758
|
-
return True
|
|
759
|
-
return self.last_response.page_info.has_next_page
|
|
775
|
+
return (conn := self.last_response) is None or conn.has_next
|
|
760
776
|
|
|
761
777
|
@property
|
|
762
778
|
def cursor(self) -> str | None:
|
|
@@ -764,9 +780,7 @@ class Artifacts(SizedPaginator["Artifact"]):
|
|
|
764
780
|
|
|
765
781
|
<!-- lazydoc-ignore: internal -->
|
|
766
782
|
"""
|
|
767
|
-
if self.last_response
|
|
768
|
-
return None
|
|
769
|
-
return self.last_response.edges[-1].cursor
|
|
783
|
+
return conn.next_cursor if (conn := self.last_response) else None
|
|
770
784
|
|
|
771
785
|
def convert_objects(self) -> list[Artifact]:
|
|
772
786
|
"""Convert the raw response data into a list of wandb.Artifact objects.
|
|
@@ -799,14 +813,13 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
799
813
|
<!-- lazydoc-ignore-init: internal -->
|
|
800
814
|
"""
|
|
801
815
|
|
|
802
|
-
last_response:
|
|
803
|
-
RunOutputArtifactConnectionFragment | RunInputArtifactConnectionFragment
|
|
804
|
-
)
|
|
816
|
+
last_response: RunArtifactConnection | None
|
|
805
817
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
818
|
+
_mode2gqlstr: ClassVar[dict[Literal["logged", "used"], str]] = {
|
|
819
|
+
"logged": RUN_OUTPUT_ARTIFACTS_GQL,
|
|
820
|
+
"used": RUN_INPUT_ARTIFACTS_GQL,
|
|
821
|
+
}
|
|
822
|
+
"""Maps the mode ("logged" or "used") to the corresponding GraphQL query string."""
|
|
810
823
|
|
|
811
824
|
def __init__(
|
|
812
825
|
self,
|
|
@@ -817,20 +830,12 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
817
830
|
):
|
|
818
831
|
self.run = run
|
|
819
832
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
RUN_OUTPUT_ARTIFACTS_GQL, omit_fields=omit_artifact_fields(client)
|
|
824
|
-
)
|
|
825
|
-
self._response_cls = RunOutputArtifactConnectionFragment
|
|
826
|
-
elif mode == "used":
|
|
827
|
-
self.run_key = "inputArtifacts"
|
|
828
|
-
self.QUERY = gql_compat(
|
|
829
|
-
RUN_INPUT_ARTIFACTS_GQL, omit_fields=omit_artifact_fields(client)
|
|
830
|
-
)
|
|
831
|
-
self._response_cls = RunInputArtifactConnectionFragment
|
|
832
|
-
else:
|
|
833
|
+
try:
|
|
834
|
+
query_str = self._mode2gqlstr[mode]
|
|
835
|
+
except LookupError:
|
|
833
836
|
raise ValueError("mode must be logged or used")
|
|
837
|
+
else:
|
|
838
|
+
self.QUERY = gql_compat(query_str, omit_fields=omit_artifact_fields(client))
|
|
834
839
|
|
|
835
840
|
variable_values = {
|
|
836
841
|
"entity": run.entity,
|
|
@@ -844,8 +849,8 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
844
849
|
data = self.client.execute(self.QUERY, variable_values=self.variables)
|
|
845
850
|
|
|
846
851
|
# Extract the inner `*Connection` result for faster/easier access.
|
|
847
|
-
inner_data = data["project"]["run"][
|
|
848
|
-
self.last_response =
|
|
852
|
+
inner_data = data["project"]["run"]["artifacts"]
|
|
853
|
+
self.last_response = RunArtifactConnection.model_validate(inner_data)
|
|
849
854
|
|
|
850
855
|
@property
|
|
851
856
|
def _length(self) -> int:
|
|
@@ -863,9 +868,7 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
863
868
|
|
|
864
869
|
<!-- lazydoc-ignore: internal -->
|
|
865
870
|
"""
|
|
866
|
-
|
|
867
|
-
return True
|
|
868
|
-
return self.last_response.page_info.has_next_page
|
|
871
|
+
return (conn := self.last_response) is None or conn.has_next
|
|
869
872
|
|
|
870
873
|
@property
|
|
871
874
|
def cursor(self) -> str | None:
|
|
@@ -873,9 +876,7 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
873
876
|
|
|
874
877
|
<!-- lazydoc-ignore: internal -->
|
|
875
878
|
"""
|
|
876
|
-
if self.last_response
|
|
877
|
-
return None
|
|
878
|
-
return self.last_response.edges[-1].cursor
|
|
879
|
+
return conn.next_cursor if (conn := self.last_response) else None
|
|
879
880
|
|
|
880
881
|
def convert_objects(self) -> list[Artifact]:
|
|
881
882
|
"""Convert the raw response data into a list of wandb.Artifact objects.
|
|
@@ -888,16 +889,15 @@ class RunArtifacts(SizedPaginator["Artifact"]):
|
|
|
888
889
|
return [
|
|
889
890
|
wandb.Artifact._from_attrs(
|
|
890
891
|
path=FullArtifactPath(
|
|
891
|
-
prefix=proj.
|
|
892
|
+
prefix=proj.entity.name,
|
|
892
893
|
project=proj.name,
|
|
893
894
|
name=f"{artifact_seq.name}:v{node.version_index}",
|
|
894
895
|
),
|
|
895
896
|
attrs=node,
|
|
896
897
|
client=self.client,
|
|
897
898
|
)
|
|
898
|
-
for
|
|
899
|
-
if (
|
|
900
|
-
and (artifact_seq := node.artifact_sequence)
|
|
899
|
+
for node in self.last_response.nodes()
|
|
900
|
+
if (artifact_seq := node.artifact_sequence)
|
|
901
901
|
and (proj := artifact_seq.project)
|
|
902
902
|
]
|
|
903
903
|
|
|
@@ -908,7 +908,7 @@ class ArtifactFiles(SizedPaginator["public.File"]):
|
|
|
908
908
|
<!-- lazydoc-ignore-init: internal -->
|
|
909
909
|
"""
|
|
910
910
|
|
|
911
|
-
last_response:
|
|
911
|
+
last_response: ArtifactFileConnection | None
|
|
912
912
|
|
|
913
913
|
def __init__(
|
|
914
914
|
self,
|
|
@@ -965,7 +965,7 @@ class ArtifactFiles(SizedPaginator["public.File"]):
|
|
|
965
965
|
if conn is None:
|
|
966
966
|
raise ValueError(f"Unable to parse {nameof(type(self))!r} response data")
|
|
967
967
|
|
|
968
|
-
self.last_response =
|
|
968
|
+
self.last_response = ArtifactFileConnection.model_validate(conn)
|
|
969
969
|
|
|
970
970
|
@property
|
|
971
971
|
def path(self) -> list[str]:
|
|
@@ -974,12 +974,12 @@ class ArtifactFiles(SizedPaginator["public.File"]):
|
|
|
974
974
|
|
|
975
975
|
@property
|
|
976
976
|
def _length(self) -> int:
|
|
977
|
-
if self.last_response is None:
|
|
978
|
-
self._load_page()
|
|
979
977
|
"""Returns the total number of files in the artifact.
|
|
980
978
|
|
|
981
979
|
<!-- lazydoc-ignore: internal -->
|
|
982
980
|
"""
|
|
981
|
+
if self.last_response is None:
|
|
982
|
+
self._load_page()
|
|
983
983
|
return self.artifact.file_count
|
|
984
984
|
|
|
985
985
|
@property
|
|
@@ -988,9 +988,7 @@ class ArtifactFiles(SizedPaginator["public.File"]):
|
|
|
988
988
|
|
|
989
989
|
<!-- lazydoc-ignore: internal -->
|
|
990
990
|
"""
|
|
991
|
-
|
|
992
|
-
return True
|
|
993
|
-
return self.last_response.page_info.has_next_page
|
|
991
|
+
return (conn := self.last_response) is None or conn.has_next
|
|
994
992
|
|
|
995
993
|
@property
|
|
996
994
|
def cursor(self) -> str | None:
|
|
@@ -998,19 +996,10 @@ class ArtifactFiles(SizedPaginator["public.File"]):
|
|
|
998
996
|
|
|
999
997
|
<!-- lazydoc-ignore: internal -->
|
|
1000
998
|
"""
|
|
1001
|
-
if self.last_response
|
|
1002
|
-
return None
|
|
1003
|
-
return self.last_response.edges[-1].cursor
|
|
1004
|
-
|
|
1005
|
-
def update_variables(self) -> None:
|
|
1006
|
-
"""Update the variables dictionary with the cursor.
|
|
1007
|
-
|
|
1008
|
-
<!-- lazydoc-ignore: internal -->
|
|
1009
|
-
"""
|
|
1010
|
-
self.variables.update({"fileLimit": self.per_page, "fileCursor": self.cursor})
|
|
999
|
+
return conn.next_cursor if (conn := self.last_response) else None
|
|
1011
1000
|
|
|
1012
1001
|
def convert_objects(self) -> list[public.File]:
|
|
1013
|
-
"""Convert the raw response data into a list of
|
|
1002
|
+
"""Convert the raw response data into a list of File objects.
|
|
1014
1003
|
|
|
1015
1004
|
<!-- lazydoc-ignore: internal -->
|
|
1016
1005
|
"""
|