wandb 0.22.1__py3-none-musllinux_1_2_aarch64.whl → 0.22.3__py3-none-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +7 -4
  3. wandb/_pydantic/__init__.py +8 -1
  4. wandb/_pydantic/base.py +54 -18
  5. wandb/_pydantic/field_types.py +8 -3
  6. wandb/_pydantic/pagination.py +46 -0
  7. wandb/_pydantic/utils.py +2 -2
  8. wandb/apis/public/api.py +24 -19
  9. wandb/apis/public/artifacts.py +259 -270
  10. wandb/apis/public/registries/_utils.py +40 -54
  11. wandb/apis/public/registries/registries_search.py +70 -85
  12. wandb/apis/public/registries/registry.py +173 -156
  13. wandb/apis/public/runs.py +27 -6
  14. wandb/apis/public/utils.py +43 -20
  15. wandb/automations/_generated/create_automation.py +2 -2
  16. wandb/automations/_generated/create_generic_webhook_integration.py +4 -4
  17. wandb/automations/_generated/delete_automation.py +2 -2
  18. wandb/automations/_generated/fragments.py +31 -52
  19. wandb/automations/_generated/generic_webhook_integrations_by_entity.py +3 -3
  20. wandb/automations/_generated/get_automations.py +3 -3
  21. wandb/automations/_generated/get_automations_by_entity.py +3 -3
  22. wandb/automations/_generated/input_types.py +9 -9
  23. wandb/automations/_generated/integrations_by_entity.py +3 -3
  24. wandb/automations/_generated/operations.py +6 -6
  25. wandb/automations/_generated/slack_integrations_by_entity.py +3 -3
  26. wandb/automations/_generated/update_automation.py +2 -2
  27. wandb/automations/_utils.py +3 -3
  28. wandb/automations/actions.py +3 -3
  29. wandb/automations/automations.py +6 -5
  30. wandb/bin/gpu_stats +0 -0
  31. wandb/bin/wandb-core +0 -0
  32. wandb/cli/beta.py +23 -3
  33. wandb/cli/beta_leet.py +75 -0
  34. wandb/cli/beta_sync.py +1 -1
  35. wandb/cli/cli.py +34 -7
  36. wandb/errors/term.py +8 -8
  37. wandb/jupyter.py +0 -51
  38. wandb/old/settings.py +6 -6
  39. wandb/proto/v3/wandb_api_pb2.py +86 -0
  40. wandb/proto/v3/wandb_server_pb2.py +38 -37
  41. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  42. wandb/proto/v3/wandb_sync_pb2.py +19 -6
  43. wandb/proto/v4/wandb_api_pb2.py +37 -0
  44. wandb/proto/v4/wandb_server_pb2.py +38 -37
  45. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  46. wandb/proto/v4/wandb_sync_pb2.py +10 -6
  47. wandb/proto/v5/wandb_api_pb2.py +38 -0
  48. wandb/proto/v5/wandb_server_pb2.py +38 -37
  49. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  50. wandb/proto/v5/wandb_sync_pb2.py +10 -6
  51. wandb/proto/v6/wandb_api_pb2.py +48 -0
  52. wandb/proto/v6/wandb_server_pb2.py +38 -37
  53. wandb/proto/v6/wandb_settings_pb2.py +2 -2
  54. wandb/proto/v6/wandb_sync_pb2.py +10 -6
  55. wandb/proto/wandb_api_pb2.py +18 -0
  56. wandb/proto/wandb_generate_proto.py +1 -0
  57. wandb/sdk/artifacts/_generated/__init__.py +96 -40
  58. wandb/sdk/artifacts/_generated/add_aliases.py +3 -3
  59. wandb/sdk/artifacts/_generated/add_artifact_collection_tags.py +26 -0
  60. wandb/sdk/artifacts/_generated/artifact_by_id.py +2 -2
  61. wandb/sdk/artifacts/_generated/artifact_by_name.py +3 -3
  62. wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +27 -8
  63. wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +27 -8
  64. wandb/sdk/artifacts/_generated/artifact_created_by.py +7 -20
  65. wandb/sdk/artifacts/_generated/artifact_file_urls.py +19 -6
  66. wandb/sdk/artifacts/_generated/artifact_membership_by_name.py +26 -0
  67. wandb/sdk/artifacts/_generated/artifact_type.py +5 -5
  68. wandb/sdk/artifacts/_generated/artifact_used_by.py +8 -17
  69. wandb/sdk/artifacts/_generated/artifact_version_files.py +19 -8
  70. wandb/sdk/artifacts/_generated/delete_aliases.py +3 -3
  71. wandb/sdk/artifacts/_generated/delete_artifact.py +4 -4
  72. wandb/sdk/artifacts/_generated/delete_artifact_collection_tags.py +23 -0
  73. wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +4 -4
  74. wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +4 -4
  75. wandb/sdk/artifacts/_generated/delete_registry.py +21 -0
  76. wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +8 -20
  77. wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +13 -35
  78. wandb/sdk/artifacts/_generated/fetch_org_info_from_entity.py +28 -0
  79. wandb/sdk/artifacts/_generated/fetch_registries.py +18 -8
  80. wandb/sdk/{projects → artifacts}/_generated/fetch_registry.py +4 -4
  81. wandb/sdk/artifacts/_generated/fragments.py +183 -333
  82. wandb/sdk/artifacts/_generated/input_types.py +133 -7
  83. wandb/sdk/artifacts/_generated/link_artifact.py +5 -5
  84. wandb/sdk/artifacts/_generated/operations.py +1053 -548
  85. wandb/sdk/artifacts/_generated/project_artifact_collection.py +9 -77
  86. wandb/sdk/artifacts/_generated/project_artifact_collections.py +21 -9
  87. wandb/sdk/artifacts/_generated/project_artifact_type.py +3 -3
  88. wandb/sdk/artifacts/_generated/project_artifact_types.py +19 -6
  89. wandb/sdk/artifacts/_generated/project_artifacts.py +7 -8
  90. wandb/sdk/artifacts/_generated/registry_collections.py +21 -9
  91. wandb/sdk/artifacts/_generated/registry_versions.py +20 -9
  92. wandb/sdk/artifacts/_generated/rename_registry.py +25 -0
  93. wandb/sdk/artifacts/_generated/run_input_artifacts.py +5 -9
  94. wandb/sdk/artifacts/_generated/run_output_artifacts.py +5 -9
  95. wandb/sdk/artifacts/_generated/type_info.py +2 -2
  96. wandb/sdk/artifacts/_generated/unlink_artifact.py +3 -5
  97. wandb/sdk/artifacts/_generated/update_artifact.py +3 -3
  98. wandb/sdk/artifacts/_generated/update_artifact_collection_type.py +28 -0
  99. wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +7 -16
  100. wandb/sdk/artifacts/_generated/update_artifact_sequence.py +7 -16
  101. wandb/sdk/artifacts/_generated/upsert_registry.py +25 -0
  102. wandb/sdk/artifacts/_gqlutils.py +170 -6
  103. wandb/sdk/artifacts/_models/__init__.py +9 -0
  104. wandb/sdk/artifacts/_models/artifact_collection.py +109 -0
  105. wandb/sdk/artifacts/_models/manifest.py +26 -0
  106. wandb/sdk/artifacts/_models/pagination.py +26 -0
  107. wandb/sdk/artifacts/_models/registry.py +100 -0
  108. wandb/sdk/artifacts/_validators.py +45 -27
  109. wandb/sdk/artifacts/artifact.py +249 -244
  110. wandb/sdk/artifacts/artifact_file_cache.py +1 -1
  111. wandb/sdk/artifacts/artifact_manifest.py +37 -32
  112. wandb/sdk/artifacts/artifact_manifest_entry.py +82 -133
  113. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +43 -61
  114. wandb/sdk/artifacts/storage_handler.py +18 -12
  115. wandb/sdk/artifacts/storage_handlers/azure_handler.py +11 -6
  116. wandb/sdk/artifacts/storage_handlers/gcs_handler.py +17 -12
  117. wandb/sdk/artifacts/storage_handlers/http_handler.py +9 -4
  118. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +10 -6
  119. wandb/sdk/artifacts/storage_handlers/multi_handler.py +5 -4
  120. wandb/sdk/artifacts/storage_handlers/s3_handler.py +10 -8
  121. wandb/sdk/artifacts/storage_handlers/tracking_handler.py +6 -4
  122. wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +24 -21
  123. wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +4 -2
  124. wandb/sdk/artifacts/storage_policies/_multipart.py +187 -0
  125. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +61 -242
  126. wandb/sdk/artifacts/storage_policy.py +25 -12
  127. wandb/sdk/data_types/image.py +2 -2
  128. wandb/sdk/data_types/object_3d.py +67 -2
  129. wandb/sdk/interface/interface.py +72 -64
  130. wandb/sdk/interface/interface_queue.py +27 -18
  131. wandb/sdk/interface/interface_shared.py +61 -23
  132. wandb/sdk/interface/interface_sock.py +9 -5
  133. wandb/sdk/internal/_generated/server_features_query.py +4 -4
  134. wandb/sdk/internal/job_builder.py +27 -10
  135. wandb/sdk/internal/sender.py +4 -1
  136. wandb/sdk/launch/create_job.py +2 -1
  137. wandb/sdk/launch/inputs/schema.py +13 -10
  138. wandb/sdk/lib/apikey.py +8 -12
  139. wandb/sdk/lib/asyncio_compat.py +1 -1
  140. wandb/sdk/lib/asyncio_manager.py +5 -5
  141. wandb/sdk/lib/console_capture.py +38 -30
  142. wandb/sdk/lib/progress.py +151 -125
  143. wandb/sdk/lib/retry.py +3 -2
  144. wandb/sdk/lib/service/service_connection.py +2 -2
  145. wandb/sdk/lib/wb_logging.py +2 -1
  146. wandb/sdk/mailbox/mailbox.py +1 -1
  147. wandb/sdk/wandb_init.py +11 -14
  148. wandb/sdk/wandb_run.py +14 -48
  149. wandb/sdk/wandb_settings.py +114 -30
  150. {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/METADATA +2 -1
  151. {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/RECORD +154 -146
  152. wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +0 -26
  153. wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +0 -36
  154. wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +0 -25
  155. wandb/sdk/artifacts/_generated/move_artifact_collection.py +0 -35
  156. wandb/sdk/projects/_generated/__init__.py +0 -26
  157. wandb/sdk/projects/_generated/delete_project.py +0 -22
  158. wandb/sdk/projects/_generated/enums.py +0 -4
  159. wandb/sdk/projects/_generated/fragments.py +0 -41
  160. wandb/sdk/projects/_generated/input_types.py +0 -13
  161. wandb/sdk/projects/_generated/operations.py +0 -88
  162. wandb/sdk/projects/_generated/rename_project.py +0 -27
  163. wandb/sdk/projects/_generated/upsert_registry_project.py +0 -27
  164. {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/WHEEL +0 -0
  165. {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/entry_points.txt +0 -0
  166. {wandb-0.22.1.dist-info → wandb-0.22.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from enum import Enum
4
- from functools import lru_cache
5
- from typing import TYPE_CHECKING, Any, Literal, Mapping, Sequence
4
+ from functools import lru_cache, partial
5
+ from typing import TYPE_CHECKING, Any, Collection
6
6
 
7
7
  from wandb._strutils import ensureprefix
8
- from wandb.sdk.artifacts._validators import (
9
- REGISTRY_PREFIX,
10
- validate_artifact_types_list,
11
- )
8
+ from wandb.sdk.artifacts._validators import REGISTRY_PREFIX, validate_artifact_types
12
9
 
13
10
  if TYPE_CHECKING:
14
11
  from wandb_gql import Client
@@ -24,12 +21,35 @@ class Visibility(str, Enum):
24
21
 
25
22
  @classmethod
26
23
  def _missing_(cls, value: object) -> Any:
27
- return next((e for e in cls if e.name == value), None)
24
+ # Allow instantiation from enum names too (e.g. "organization" or "restricted")
25
+ return cls.__members__.get(value)
28
26
 
27
+ @classmethod
28
+ def from_gql(cls, value: str) -> Visibility:
29
+ """Convert a GraphQL `visibility` value to a Visibility enum."""
30
+ try:
31
+ return cls(value)
32
+ except ValueError:
33
+ expected = ",".join(repr(e.value) for e in cls)
34
+ raise ValueError(
35
+ f"Invalid visibility {value!r} from backend. Expected one of: {expected}"
36
+ ) from None
29
37
 
30
- def format_gql_artifact_types_input(
31
- artifact_types: list[str] | None,
32
- ) -> list[dict[str, str]]:
38
+ @classmethod
39
+ def from_python(cls, name: str) -> Visibility:
40
+ """Convert a visibility string (e.g. user-provided or python default value) to a Visibility enum."""
41
+ try:
42
+ return cls(name)
43
+ except ValueError:
44
+ expected = ",".join(repr(e.name) for e in cls)
45
+ raise ValueError(
46
+ f"Invalid visibility {name!r}. Expected one of: {expected}"
47
+ ) from None
48
+
49
+
50
+ def prepare_artifact_types_input(
51
+ artifact_types: Collection[str] | None,
52
+ ) -> list[dict[str, str]] | None:
33
53
  """Format the artifact types for the GQL input.
34
54
 
35
55
  Args:
@@ -38,39 +58,9 @@ def format_gql_artifact_types_input(
38
58
  Returns:
39
59
  The artifact types for the GQL input.
40
60
  """
41
- if artifact_types is None:
42
- return []
43
- return [{"name": typ} for typ in validate_artifact_types_list(artifact_types)]
44
-
45
-
46
- def gql_to_registry_visibility(
47
- visibility: str,
48
- ) -> Literal["organization", "restricted"]:
49
- """Convert the GQL visibility to the registry visibility.
50
-
51
- Args:
52
- visibility: The GQL visibility.
53
-
54
- Returns:
55
- The registry visibility.
56
- """
57
- try:
58
- return Visibility(visibility).name
59
- except ValueError:
60
- raise ValueError(f"Invalid visibility: {visibility!r} from backend")
61
-
62
-
63
- def registry_visibility_to_gql(
64
- visibility: Literal["organization", "restricted"],
65
- ) -> str:
66
- """Convert the registry visibility to the GQL visibility."""
67
- try:
68
- return Visibility[visibility].value
69
- except LookupError:
70
- allowed_str = ", ".join(map(repr, (e.name for e in Visibility)))
71
- raise ValueError(
72
- f"Invalid visibility: {visibility!r}. Must be one of: {allowed_str}"
73
- )
61
+ if artifact_types:
62
+ return [{"name": typ} for typ in validate_artifact_types(artifact_types)]
63
+ return None
74
64
 
75
65
 
76
66
  def ensure_registry_prefix_on_names(query: Any, in_name: bool = False) -> Any:
@@ -81,25 +71,21 @@ def ensure_registry_prefix_on_names(query: Any, in_name: bool = False) -> Any:
81
71
  EX: {"name": "model"} -> {"name": "wandb-registry-model"}
82
72
  """
83
73
  if isinstance((txt := query), str):
84
- if in_name:
85
- return ensureprefix(txt, REGISTRY_PREFIX)
86
- return txt
87
- if isinstance((dct := query), Mapping):
74
+ return ensureprefix(txt, REGISTRY_PREFIX) if in_name else txt
75
+ if isinstance((dct := query), dict):
88
76
  new_dict = {}
89
77
  for key, obj in dct.items():
90
- if key == "name":
91
- new_dict[key] = ensure_registry_prefix_on_names(obj, in_name=True)
92
- elif key == "$regex":
78
+ if key == "$regex":
93
79
  # For regex operator, we skip transformation of its value.
94
80
  new_dict[key] = obj
81
+ elif key == "name":
82
+ new_dict[key] = ensure_registry_prefix_on_names(obj, in_name=True)
95
83
  else:
96
84
  # For any other key, propagate the in_name and skip_transform flags as-is.
97
85
  new_dict[key] = ensure_registry_prefix_on_names(obj, in_name=in_name)
98
86
  return new_dict
99
- if isinstance((objs := query), Sequence):
100
- return list(
101
- map(lambda x: ensure_registry_prefix_on_names(x, in_name=in_name), objs)
102
- )
87
+ if isinstance((seq := query), (list, tuple)):
88
+ return list(map(partial(ensure_registry_prefix_on_names, in_name=in_name), seq))
103
89
  return query
104
90
 
105
91
 
@@ -5,33 +5,38 @@ from __future__ import annotations
5
5
  import json
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
- from pydantic import ValidationError
8
+ from pydantic import PositiveInt, ValidationError
9
9
  from typing_extensions import override
10
10
  from wandb_gql import gql
11
11
 
12
12
  from wandb._analytics import tracked
13
13
  from wandb.apis.paginator import Paginator
14
+ from wandb.apis.public.artifacts import ArtifactCollection
14
15
  from wandb.apis.public.utils import gql_compat
15
16
  from wandb.sdk.artifacts._generated import (
16
17
  FETCH_REGISTRIES_GQL,
17
18
  REGISTRY_COLLECTIONS_GQL,
18
19
  REGISTRY_VERSIONS_GQL,
19
- ArtifactCollectionType,
20
20
  FetchRegistries,
21
- RegistriesPage,
22
21
  RegistryCollections,
23
- RegistryCollectionsPage,
24
22
  RegistryVersions,
25
- RegistryVersionsPage,
26
23
  )
27
24
  from wandb.sdk.artifacts._gqlutils import omit_artifact_fields
28
- from wandb.sdk.artifacts._validators import FullArtifactPath, remove_registry_prefix
25
+ from wandb.sdk.artifacts._models.pagination import (
26
+ ArtifactMembershipConnection,
27
+ RegistryCollectionConnection,
28
+ RegistryConnection,
29
+ )
30
+ from wandb.sdk.artifacts._validators import (
31
+ SOURCE_COLLECTION_TYPENAME,
32
+ FullArtifactPath,
33
+ remove_registry_prefix,
34
+ )
29
35
 
30
36
  from ._utils import ensure_registry_prefix_on_names
31
37
 
32
38
  if TYPE_CHECKING:
33
- from wandb_gql import Client
34
-
39
+ from wandb.apis.public import RetryingClient
35
40
  from wandb.sdk.artifacts.artifact import Artifact
36
41
 
37
42
 
@@ -40,15 +45,14 @@ class Registries(Paginator):
40
45
 
41
46
  QUERY = gql(FETCH_REGISTRIES_GQL)
42
47
 
43
- last_response: RegistriesPage | None
44
- _last_org_entity: str | None
48
+ last_response: RegistryConnection | None
45
49
 
46
50
  def __init__(
47
51
  self,
48
- client: Client,
52
+ client: RetryingClient,
49
53
  organization: str,
50
54
  filter: dict[str, Any] | None = None,
51
- per_page: int | None = 100,
55
+ per_page: PositiveInt = 100,
52
56
  ):
53
57
  self.client = client
54
58
  self.organization = organization
@@ -57,11 +61,8 @@ class Registries(Paginator):
57
61
  "organization": organization,
58
62
  "filters": json.dumps(self.filter),
59
63
  }
60
-
61
64
  super().__init__(client, variables, per_page)
62
65
 
63
- self._last_org_entity = None
64
-
65
66
  def __next__(self):
66
67
  # Implement custom next since its possible to load empty pages because of auth
67
68
  self.index += 1
@@ -71,22 +72,28 @@ class Registries(Paginator):
71
72
  return self.objects[self.index]
72
73
 
73
74
  @tracked
74
- def collections(self, filter: dict[str, Any] | None = None) -> Collections:
75
+ def collections(
76
+ self, filter: dict[str, Any] | None = None, per_page: PositiveInt = 100
77
+ ) -> Collections:
75
78
  return Collections(
76
79
  client=self.client,
77
80
  organization=self.organization,
78
81
  registry_filter=self.filter,
79
82
  collection_filter=filter,
83
+ per_page=per_page,
80
84
  )
81
85
 
82
86
  @tracked
83
- def versions(self, filter: dict[str, Any] | None = None) -> Versions:
87
+ def versions(
88
+ self, filter: dict[str, Any] | None = None, per_page: PositiveInt = 100
89
+ ) -> Versions:
84
90
  return Versions(
85
91
  client=self.client,
86
92
  organization=self.organization,
87
93
  registry_filter=self.filter,
88
94
  collection_filter=None,
89
95
  artifact_filter=filter,
96
+ per_page=per_page,
90
97
  )
91
98
 
92
99
  @property
@@ -97,15 +104,11 @@ class Registries(Paginator):
97
104
 
98
105
  @property
99
106
  def more(self):
100
- if self.last_response is None:
101
- return True
102
- return self.last_response.page_info.has_next_page
107
+ return (conn := self.last_response) is None or conn.has_next
103
108
 
104
109
  @property
105
110
  def cursor(self):
106
- if self.last_response is None:
107
- return None
108
- return self.last_response.page_info.end_cursor
111
+ return conn.next_cursor if (conn := self.last_response) else None
109
112
 
110
113
  @override
111
114
  def _update_response(self) -> None:
@@ -117,45 +120,43 @@ class Registries(Paginator):
117
120
  )
118
121
 
119
122
  try:
120
- page_data = org_entity.projects
121
- self.last_response = RegistriesPage.model_validate(page_data)
122
- self._last_org_entity = org_entity.name
123
+ conn = org_entity.projects
124
+ self.last_response = RegistryConnection.model_validate(conn)
123
125
  except (LookupError, AttributeError, ValidationError) as e:
124
126
  raise ValueError("Unexpected response data") from e
125
127
 
126
128
  def convert_objects(self):
127
129
  from wandb.apis.public.registries.registry import Registry
128
130
 
129
- if (self.last_response is None) or (self._last_org_entity is None):
131
+ if self.last_response is None:
130
132
  return []
131
133
 
132
- nodes = (e.node for e in self.last_response.edges)
133
134
  return [
134
135
  Registry(
135
136
  client=self.client,
136
137
  organization=self.organization,
137
- entity=self._last_org_entity,
138
+ entity=node.entity.name,
138
139
  name=remove_registry_prefix(node.name),
139
- attrs=node.model_dump(),
140
+ attrs=node,
140
141
  )
141
- for node in nodes
142
+ for node in self.last_response.nodes()
142
143
  ]
143
144
 
144
145
 
145
- class Collections(Paginator["ArtifactCollection"]):
146
+ class Collections(Paginator[ArtifactCollection]):
146
147
  """An lazy iterator of `ArtifactCollection` objects in a Registry."""
147
148
 
148
149
  QUERY = gql(REGISTRY_COLLECTIONS_GQL)
149
150
 
150
- last_response: RegistryCollectionsPage | None
151
+ last_response: RegistryCollectionConnection | None
151
152
 
152
153
  def __init__(
153
154
  self,
154
- client: Client,
155
+ client: RetryingClient,
155
156
  organization: str,
156
157
  registry_filter: dict[str, Any] | None = None,
157
158
  collection_filter: dict[str, Any] | None = None,
158
- per_page: int | None = 100,
159
+ per_page: PositiveInt = 100,
159
160
  ):
160
161
  self.client = client
161
162
  self.organization = organization
@@ -166,7 +167,6 @@ class Collections(Paginator["ArtifactCollection"]):
166
167
  "registryFilter": json.dumps(f) if (f := registry_filter) else None,
167
168
  "collectionFilter": json.dumps(f) if (f := collection_filter) else None,
168
169
  "organization": organization,
169
- "collectionTypes": [ArtifactCollectionType.PORTFOLIO],
170
170
  "perPage": per_page,
171
171
  }
172
172
 
@@ -181,48 +181,42 @@ class Collections(Paginator["ArtifactCollection"]):
181
181
  return self.objects[self.index]
182
182
 
183
183
  @tracked
184
- def versions(self, filter: dict[str, Any] | None = None) -> Versions:
184
+ def versions(
185
+ self, filter: dict[str, Any] | None = None, per_page: PositiveInt = 100
186
+ ) -> Versions:
185
187
  return Versions(
186
188
  client=self.client,
187
189
  organization=self.organization,
188
190
  registry_filter=self.registry_filter,
189
191
  collection_filter=self.collection_filter,
190
192
  artifact_filter=filter,
193
+ per_page=per_page,
191
194
  )
192
195
 
193
196
  @property
194
197
  def length(self):
195
- if self.last_response is None:
196
- return None
197
- return self.last_response.total_count
198
+ return conn.total_count if (conn := self.last_response) else None
198
199
 
199
200
  @property
200
201
  def more(self):
201
- if self.last_response is None:
202
- return True
203
- return self.last_response.page_info.has_next_page
202
+ return (conn := self.last_response) is None or conn.has_next
204
203
 
205
204
  @property
206
205
  def cursor(self):
207
- if self.last_response is None:
208
- return None
209
- return self.last_response.page_info.end_cursor
206
+ return conn.next_cursor if (conn := self.last_response) else None
210
207
 
211
208
  @override
212
209
  def _update_response(self) -> None:
213
210
  data = self.client.execute(self.QUERY, variable_values=self.variables)
214
211
  result = RegistryCollections.model_validate(data)
215
- if not (
216
- (org_data := result.organization)
217
- and (org_entity_data := org_data.org_entity)
218
- ):
212
+ if not ((org := result.organization) and (org_entity := org.org_entity)):
219
213
  raise ValueError(
220
214
  f"Organization {self.organization!r} not found. Please verify the organization name is correct."
221
215
  )
222
216
 
223
217
  try:
224
- page_data = org_entity_data.artifact_collections
225
- self.last_response = RegistryCollectionsPage.model_validate(page_data)
218
+ conn = org_entity.artifact_collections
219
+ self.last_response = RegistryCollectionConnection.model_validate(conn)
226
220
  except (LookupError, AttributeError, ValidationError) as e:
227
221
  raise ValueError("Unexpected response data") from e
228
222
 
@@ -232,36 +226,36 @@ class Collections(Paginator["ArtifactCollection"]):
232
226
  if self.last_response is None:
233
227
  return []
234
228
 
235
- nodes = (e.node for e in self.last_response.edges)
236
229
  return [
237
230
  ArtifactCollection(
238
231
  client=self.client,
239
- entity=project.entity.name,
240
- project=project.name,
232
+ entity=node.project.entity.name,
233
+ project=node.project.name,
241
234
  name=node.name,
242
- type=node.default_artifact_type.name,
235
+ type=node.type.name,
243
236
  organization=self.organization,
244
- attrs=node.model_dump(),
245
- is_sequence=False,
237
+ attrs=node,
246
238
  )
247
- for node in nodes
248
- if (project := node.project)
239
+ for node in self.last_response.nodes()
240
+ # We don't _expect_ any registry collections to be
241
+ # ArtifactSequences, but defensively filter them out anyway.
242
+ if node.project and (node.typename__ != SOURCE_COLLECTION_TYPENAME)
249
243
  ]
250
244
 
251
245
 
252
246
  class Versions(Paginator["Artifact"]):
253
247
  """An lazy iterator of `Artifact` objects in a Registry."""
254
248
 
255
- last_response: RegistryVersionsPage | None
249
+ last_response: ArtifactMembershipConnection | None
256
250
 
257
251
  def __init__(
258
252
  self,
259
- client: Client,
253
+ client: RetryingClient,
260
254
  organization: str,
261
255
  registry_filter: dict[str, Any] | None = None,
262
256
  collection_filter: dict[str, Any] | None = None,
263
257
  artifact_filter: dict[str, Any] | None = None,
264
- per_page: int = 100,
258
+ per_page: PositiveInt = 100,
265
259
  ):
266
260
  self.client = client
267
261
  self.organization = organization
@@ -298,31 +292,24 @@ class Versions(Paginator["Artifact"]):
298
292
 
299
293
  @property
300
294
  def more(self) -> bool:
301
- if self.last_response is None:
302
- return True
303
- return self.last_response.page_info.has_next_page
295
+ return (conn := self.last_response) is None or conn.has_next
304
296
 
305
297
  @property
306
298
  def cursor(self) -> str | None:
307
- if self.last_response is None:
308
- return None
309
- return self.last_response.page_info.end_cursor
299
+ return conn.next_cursor if (conn := self.last_response) else None
310
300
 
311
301
  @override
312
302
  def _update_response(self) -> None:
313
303
  data = self.client.execute(self.QUERY, variable_values=self.variables)
314
304
  result = RegistryVersions.model_validate(data)
315
- if not (
316
- (org_data := result.organization)
317
- and (org_entity_data := org_data.org_entity)
318
- ):
305
+ if not ((org := result.organization) and (org_entity := org.org_entity)):
319
306
  raise ValueError(
320
307
  f"Organization {self.organization!r} not found. Please verify the organization name is correct."
321
308
  )
322
309
 
323
310
  try:
324
- page_data = org_entity_data.artifact_memberships
325
- self.last_response = RegistryVersionsPage.model_validate(page_data)
311
+ conn = org_entity.artifact_memberships
312
+ self.last_response = ArtifactMembershipConnection.model_validate(conn)
326
313
  except (LookupError, AttributeError, ValidationError) as e:
327
314
  raise ValueError("Unexpected response data") from e
328
315
 
@@ -331,23 +318,21 @@ class Versions(Paginator["Artifact"]):
331
318
 
332
319
  if self.last_response is None:
333
320
  return []
334
-
335
- nodes = (e.node for e in self.last_response.edges)
336
321
  return [
337
- Artifact._from_attrs(
338
- path=FullArtifactPath(
322
+ Artifact._from_membership(
323
+ membership=membership,
324
+ target=FullArtifactPath(
339
325
  prefix=project.entity.name,
340
326
  project=project.name,
341
- name=f"{collection.name}:v{node.version_index}",
327
+ name=f"{collection.name}:v{version_idx}",
342
328
  ),
343
- attrs=artifact,
344
329
  client=self.client,
345
- aliases=[alias.alias for alias in node.aliases],
346
330
  )
347
- for node in nodes
331
+ for membership in self.last_response.nodes()
348
332
  if (
349
- (collection := node.artifact_collection)
333
+ (collection := membership.artifact_collection)
350
334
  and (project := collection.project)
351
- and (artifact := node.artifact)
335
+ and membership.artifact
336
+ and (version_idx := membership.version_index) is not None
352
337
  )
353
338
  ]