From b60b593b059aadb1e3d9b560474913cf729bbcd0 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Mon, 31 Oct 2022 22:41:04 -0700 Subject: [PATCH 01/15] add update_aliases method and relevant gql mutations --- tests/unit_tests/test_public_api.py | 5 ++ wandb/apis/public.py | 114 +++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index f691051ed7e..47e36a231f2 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -212,6 +212,11 @@ def test_artifact_download_logger(): termlog.assert_not_called() +def test_update_aliases_on_artifact(user, relay_server): + _project = "test" + # TODO: Test api.artifact(...) and use_artifact(...) + + @pytest.mark.parametrize("sweep_config", VALID_SWEEP_CONFIGS_MINIMAL) def test_sweep_api(user, relay_server, sweep_config): _project = "test" diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 0c89fd11c98..8cb65fe9563 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4189,12 +4189,15 @@ def __init__(self, client, entity, project, name, attrs=None): self._description = self._attrs.get("description", None) self._sequence_name = self._attrs["artifactSequence"]["name"] self._version_index = self._attrs.get("versionIndex", None) + # We will only show aliases under the Collection this artifact version is fetched from self._aliases = [ a["alias"] for a in self._attrs["aliases"] if not re.match(r"^v\d+$", a["alias"]) - and a["artifactCollectionName"] == self._sequence_name + and a["artifactCollectionName"] == self._artifact_name ] + self._aliases_to_add = [] + self._aliases_to_remove = [] self._manifest = None self._is_downloaded = False self._dependent_artifacts = [] @@ -4354,6 +4357,20 @@ def _use_as(self, use_as): self._attrs["_use_as"] = use_as return use_as + @normalize_exceptions + def update_aliases(self, add: List[str], remove: List[str]): + # edit aliases locally + # .save() will persist these changes to backend + # TODO: Print out a message in the SDK on finish if the user has + # unfinished changes in their artifact (such as updating the aliases). + for alias in add: + if alias not in self._aliases_to_add: + self._aliases_to_add.append(alias) + + for alias in remove: + if alias not in self._aliases_to_remove: + self._aliases_to_remove.append(alias) + @normalize_exceptions def link(self, target_path: str, aliases=None): if ":" in target_path: @@ -4715,11 +4732,106 @@ def save(self): ], }, ) + # Save locally modified aliases + self._save_alias_changes() return True def wait(self): return self + @normalize_exceptions + def _save_alias_changes(self): + """ + Convenience function called by artifact.save() to persist alias changes + on this artifact to the wandb backend. + """ + + # Introspect + introspect_query = gql( + """ + query ProbeServerAddAliasesInput { + AddAliasesInputInfoType: __type(name: "AddAliasesInput") { + name + inputFields { + name + } + } + } + """ + ) + res = self.client.execute(introspect_query) + valid = res.get("AddAliasesInputInfoType") or None + if not valid: + return + + if len(self._aliases_to_add) > 0: + add_mutation = gql( + """ + mutation addAliases( + $artifactID: ID!, + $aliases: [ArtifactCollectionAliasInput!]!, + ) { + addAliases( + input: { + artifactID: $artifactID, + aliases: $aliases, + } + ) + } + """ + ) + self.client.execute( + add_mutation, + variable_values={ + "artifactID": self.id, + "aliases": [ + { + "artifactCollectionName": self._artifact_name, + "alias": alias, + "entityName": self._entity, + "projectName": self._project + } + for alias in self._aliases_to_add + ], + }, + ) + + if len(self._aliases_to_remove) > 0: + delete_mutation = gql( + """ + mutation deleteAliases( + $artifactID: ID!, + $aliases: [ArtifactCollectionAliasInput!]!, + ) { + deleteAliases( + input: { + artifactID: $artifactID, + aliases: $aliases, + } + ) + } + """ + ) + self.client.execute( + delete_mutation, + variable_values={ + "artifactID": self.id, + "aliases": [ + { + "artifactCollectionName": self._artifact_name, + "alias": alias, + "entityName": self._entity, + "projectName": self._project + } + for alias in self._aliases_to_remove + ], + }, + ) + # reset local state + self._aliases_to_add = [] + self._aliases_to_remove = [] + return True + # TODO: not yet public, but we probably want something like this. def _list(self): manifest = self._load_manifest() From bea6ea049594c0096e45f349fdd818cf3b137dd2 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Tue, 1 Nov 2022 23:12:01 -0700 Subject: [PATCH 02/15] wip test --- tests/unit_tests/test_public_api.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 47e36a231f2..0526925b4a5 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -212,8 +212,28 @@ def test_artifact_download_logger(): termlog.assert_not_called() -def test_update_aliases_on_artifact(user, relay_server): +def test_update_aliases_on_artifact(user, relay_server, wandb_init): _project = "test" + + with relay_server(): + run = wandb_init(entity=user, project=_project) + artifact = wandb.Artifact("test-artifact", "test-type") + with open("boom.txt", "w") as f: + f.write("testing") + artifact.add_file("boom.txt", "test-name") + run.log_artifact(artifact, aliases=["best"]) + artifact.wait() + run.finish() + + artifact = Api().artifact(f"{_project}/test-artifact:v0") + artifact.update_aliases(add=["staging"], remove=["best"]) + artifact.save() + + artifact = Api().artifact("test-artifact:v0") + aliases = artifact.aliases + assert "staging" in aliases + assert "best" not in aliases + # TODO: Test api.artifact(...) and use_artifact(...) From af2fc9a46edd8b4e005e22aad274f36191cc377c Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 2 Nov 2022 10:22:56 -0700 Subject: [PATCH 03/15] added test for public api --- tests/unit_tests/test_public_api.py | 14 ++++++++------ wandb/apis/public.py | 25 +++++++++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 0526925b4a5..538dbe50d61 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -213,10 +213,10 @@ def test_artifact_download_logger(): def test_update_aliases_on_artifact(user, relay_server, wandb_init): - _project = "test" + project = "test" with relay_server(): - run = wandb_init(entity=user, project=_project) + run = wandb_init(entity=user, project=project) artifact = wandb.Artifact("test-artifact", "test-type") with open("boom.txt", "w") as f: f.write("testing") @@ -225,17 +225,19 @@ def test_update_aliases_on_artifact(user, relay_server, wandb_init): artifact.wait() run.finish() - artifact = Api().artifact(f"{_project}/test-artifact:v0") + artifact = Api().artifact( + name=f"{user}/{project}/test-artifact:v0", type="test-type" + ) artifact.update_aliases(add=["staging"], remove=["best"]) artifact.save() - artifact = Api().artifact("test-artifact:v0") + artifact = Api().artifact( + name=f"{user}/{project}/test-artifact:v0", type="test-type" + ) aliases = artifact.aliases assert "staging" in aliases assert "best" not in aliases - # TODO: Test api.artifact(...) and use_artifact(...) - @pytest.mark.parametrize("sweep_config", VALID_SWEEP_CONFIGS_MINIMAL) def test_sweep_api(user, relay_server, sweep_config): diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 8cb65fe9563..5ec7128cc34 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4182,6 +4182,7 @@ def __init__(self, client, entity, project, name, attrs=None): self._entity = entity self._project = project self._artifact_name = name + self._artifact_collection_name = name.split(":")[0] if ":" in name else name self._attrs = attrs if self._attrs is None: self._load() @@ -4194,7 +4195,7 @@ def __init__(self, client, entity, project, name, attrs=None): a["alias"] for a in self._attrs["aliases"] if not re.match(r"^v\d+$", a["alias"]) - and a["artifactCollectionName"] == self._artifact_name + and a["artifactCollectionName"] == self._artifact_collection_name ] self._aliases_to_add = [] self._aliases_to_remove = [] @@ -4361,7 +4362,7 @@ def _use_as(self, use_as): def update_aliases(self, add: List[str], remove: List[str]): # edit aliases locally # .save() will persist these changes to backend - # TODO: Print out a message in the SDK on finish if the user has + # TODO: Print out a message in the SDK on finish if the user has # unfinished changes in their artifact (such as updating the aliases). for alias in add: if alias not in self._aliases_to_add: @@ -4742,13 +4743,13 @@ def wait(self): @normalize_exceptions def _save_alias_changes(self): """ - Convenience function called by artifact.save() to persist alias changes + Convenience function called by artifact.save() to persist alias changes on this artifact to the wandb backend. """ # Introspect introspect_query = gql( - """ + """ query ProbeServerAddAliasesInput { AddAliasesInputInfoType: __type(name: "AddAliasesInput") { name @@ -4776,7 +4777,9 @@ def _save_alias_changes(self): artifactID: $artifactID, aliases: $aliases, } - ) + ) { + success + } } """ ) @@ -4786,10 +4789,10 @@ def _save_alias_changes(self): "artifactID": self.id, "aliases": [ { - "artifactCollectionName": self._artifact_name, + "artifactCollectionName": self._artifact_collection_name, "alias": alias, "entityName": self._entity, - "projectName": self._project + "projectName": self._project, } for alias in self._aliases_to_add ], @@ -4808,7 +4811,9 @@ def _save_alias_changes(self): artifactID: $artifactID, aliases: $aliases, } - ) + ) { + success + } } """ ) @@ -4818,10 +4823,10 @@ def _save_alias_changes(self): "artifactID": self.id, "aliases": [ { - "artifactCollectionName": self._artifact_name, + "artifactCollectionName": self._artifact_collection_name, "alias": alias, "entityName": self._entity, - "projectName": self._project + "projectName": self._project, } for alias in self._aliases_to_remove ], From 32d5323ba4c584e09a7bb6649794b164466ca784 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 2 Nov 2022 12:13:15 -0700 Subject: [PATCH 04/15] removed relay call --- tests/unit_tests/test_public_api.py | 42 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 538dbe50d61..1f61bef72d6 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -214,29 +214,27 @@ def test_artifact_download_logger(): def test_update_aliases_on_artifact(user, relay_server, wandb_init): project = "test" + run = wandb_init(entity=user, project=project) + artifact = wandb.Artifact("test-artifact", "test-type") + with open("boom.txt", "w") as f: + f.write("testing") + artifact.add_file("boom.txt", "test-name") + run.log_artifact(artifact, aliases=["best"]) + artifact.wait() + run.finish() + + artifact = Api().artifact( + name=f"{user}/{project}/test-artifact:v0", type="test-type" + ) + artifact.update_aliases(add=["staging"], remove=["best"]) + artifact.save() - with relay_server(): - run = wandb_init(entity=user, project=project) - artifact = wandb.Artifact("test-artifact", "test-type") - with open("boom.txt", "w") as f: - f.write("testing") - artifact.add_file("boom.txt", "test-name") - run.log_artifact(artifact, aliases=["best"]) - artifact.wait() - run.finish() - - artifact = Api().artifact( - name=f"{user}/{project}/test-artifact:v0", type="test-type" - ) - artifact.update_aliases(add=["staging"], remove=["best"]) - artifact.save() - - artifact = Api().artifact( - name=f"{user}/{project}/test-artifact:v0", type="test-type" - ) - aliases = artifact.aliases - assert "staging" in aliases - assert "best" not in aliases + artifact = Api().artifact( + name=f"{user}/{project}/test-artifact:v0", type="test-type" + ) + aliases = artifact.aliases + assert "staging" in aliases + assert "best" not in aliases @pytest.mark.parametrize("sweep_config", VALID_SWEEP_CONFIGS_MINIMAL) From b09561078f6c94536320f795cb882dd4d7f31178 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 2 Nov 2022 14:57:15 -0700 Subject: [PATCH 05/15] code nit --- wandb/apis/public.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 5ec7128cc34..176eeb67cd3 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4182,7 +4182,7 @@ def __init__(self, client, entity, project, name, attrs=None): self._entity = entity self._project = project self._artifact_name = name - self._artifact_collection_name = name.split(":")[0] if ":" in name else name + self._artifact_collection_name = name.split(":")[0] self._attrs = attrs if self._attrs is None: self._load() From d9abd5525db72ba7d0df470c66b5ac6dddf9b259 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 2 Nov 2022 16:07:12 -0700 Subject: [PATCH 06/15] changed to alias updates in pythonic list form rather than separate method --- tests/unit_tests/test_public_api.py | 2 +- wandb/apis/public.py | 40 ++++++++--------------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 1f61bef72d6..86afbb08799 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -226,7 +226,7 @@ def test_update_aliases_on_artifact(user, relay_server, wandb_init): artifact = Api().artifact( name=f"{user}/{project}/test-artifact:v0", type="test-type" ) - artifact.update_aliases(add=["staging"], remove=["best"]) + artifact.aliases = ["staging"] artifact.save() artifact = Api().artifact( diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 176eeb67cd3..f552ff4b38c 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4191,14 +4191,14 @@ def __init__(self, client, entity, project, name, attrs=None): self._sequence_name = self._attrs["artifactSequence"]["name"] self._version_index = self._attrs.get("versionIndex", None) # We will only show aliases under the Collection this artifact version is fetched from + # _aliases will be a mutable copy on which the user can append or remove aliases self._aliases = [ a["alias"] for a in self._attrs["aliases"] if not re.match(r"^v\d+$", a["alias"]) and a["artifactCollectionName"] == self._artifact_collection_name ] - self._aliases_to_add = [] - self._aliases_to_remove = [] + self._frozen_aliases = [a for a in self._aliases] self._manifest = None self._is_downloaded = False self._dependent_artifacts = [] @@ -4358,20 +4358,6 @@ def _use_as(self, use_as): self._attrs["_use_as"] = use_as return use_as - @normalize_exceptions - def update_aliases(self, add: List[str], remove: List[str]): - # edit aliases locally - # .save() will persist these changes to backend - # TODO: Print out a message in the SDK on finish if the user has - # unfinished changes in their artifact (such as updating the aliases). - for alias in add: - if alias not in self._aliases_to_add: - self._aliases_to_add.append(alias) - - for alias in remove: - if alias not in self._aliases_to_remove: - self._aliases_to_remove.append(alias) - @normalize_exceptions def link(self, target_path: str, aliases=None): if ":" in target_path: @@ -4724,13 +4710,6 @@ def save(self): "artifactID": self.id, "description": self.description, "metadata": json.dumps(util.make_safe_for_json(self.metadata)), - "aliases": [ - { - "artifactCollectionName": self._sequence_name, - "alias": alias, - } - for alias in self._aliases - ], }, ) # Save locally modified aliases @@ -4747,6 +4726,9 @@ def _save_alias_changes(self): on this artifact to the wandb backend. """ + aliases_to_add = set(self._aliases) - set(self._frozen_aliases) + aliases_to_remove = set(self._frozen_aliases) - set(self._aliases) + # Introspect introspect_query = gql( """ @@ -4765,7 +4747,7 @@ def _save_alias_changes(self): if not valid: return - if len(self._aliases_to_add) > 0: + if len(aliases_to_add) > 0: add_mutation = gql( """ mutation addAliases( @@ -4794,12 +4776,12 @@ def _save_alias_changes(self): "entityName": self._entity, "projectName": self._project, } - for alias in self._aliases_to_add + for alias in aliases_to_add ], }, ) - if len(self._aliases_to_remove) > 0: + if len(aliases_to_remove) > 0: delete_mutation = gql( """ mutation deleteAliases( @@ -4828,13 +4810,13 @@ def _save_alias_changes(self): "entityName": self._entity, "projectName": self._project, } - for alias in self._aliases_to_remove + for alias in aliases_to_remove ], }, ) + # reset local state - self._aliases_to_add = [] - self._aliases_to_remove = [] + self._frozen_aliases = self._aliases return True # TODO: not yet public, but we probably want something like this. From 52265d0373e151b205fcf91267dc1391949be32f Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 2 Nov 2022 16:21:01 -0700 Subject: [PATCH 07/15] modify test --- tests/unit_tests/test_public_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 86afbb08799..0ce4a679c7e 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -227,12 +227,14 @@ def test_update_aliases_on_artifact(user, relay_server, wandb_init): name=f"{user}/{project}/test-artifact:v0", type="test-type" ) artifact.aliases = ["staging"] + artifact.aliases.append("boom") artifact.save() artifact = Api().artifact( name=f"{user}/{project}/test-artifact:v0", type="test-type" ) aliases = artifact.aliases + assert "boom" in aliases assert "staging" in aliases assert "best" not in aliases From 7eee4e56d6458dcb381f859cbb56e50ba3311303 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Thu, 3 Nov 2022 10:20:52 -0700 Subject: [PATCH 08/15] backwards compatibility --- wandb/apis/public.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index f552ff4b38c..fae2564034c 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4704,12 +4704,39 @@ def save(self): } """ ) + introspect_query = gql( + """ + query ProbeServerAddAliasesInput { + AddAliasesInputInfoType: __type(name: "AddAliasesInput") { + name + inputFields { + name + } + } + } + """ + ) + res = self.client.execute(introspect_query) + valid = res.get("AddAliasesInputInfoType") or None + aliases = None + if not valid: + # Note: dangerous for artifacts linked to multiple collections + # This risk only exists for clients with old wandb backends. + aliases = [ + { + "artifactCollectionName": self._sequence_name, + "alias": alias, + } + for alias in self._aliases + ] + self.client.execute( mutation, variable_values={ "artifactID": self.id, "description": self.description, "metadata": json.dumps(util.make_safe_for_json(self.metadata)), + "aliases": aliases }, ) # Save locally modified aliases From d6db49f55ec0b48f60db5559804c2a72e8f9b0d4 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Thu, 3 Nov 2022 14:16:09 -0700 Subject: [PATCH 09/15] format --- wandb/apis/public.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index fae2564034c..5764f05e7ae 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4727,7 +4727,7 @@ def save(self): "artifactCollectionName": self._sequence_name, "alias": alias, } - for alias in self._aliases + for alias in self._aliases ] self.client.execute( @@ -4736,7 +4736,7 @@ def save(self): "artifactID": self.id, "description": self.description, "metadata": json.dumps(util.make_safe_for_json(self.metadata)), - "aliases": aliases + "aliases": aliases, }, ) # Save locally modified aliases From dfe5945fb62419703990e94bc3384f265b1bd8ad Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Mon, 7 Nov 2022 13:37:58 -0800 Subject: [PATCH 10/15] collection name --- wandb/apis/public.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 5764f05e7ae..919ea99b2db 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4724,7 +4724,7 @@ def save(self): # This risk only exists for clients with old wandb backends. aliases = [ { - "artifactCollectionName": self._sequence_name, + "artifactCollectionName": self._artifact_collection_name, "alias": alias, } for alias in self._aliases From 211b290e2269362270f4d6726acd629d54cabd8b Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Mon, 7 Nov 2022 13:38:23 -0800 Subject: [PATCH 11/15] format --- tests/unit_tests/test_public_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 0f659c8c1bb..2b08cc331d7 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -202,8 +202,8 @@ def test_artifact_download_logger(): assert termlog.call_args == call else: termlog.assert_not_called() - - + + def test_update_aliases_on_artifact(user, relay_server, wandb_init): project = "test" run = wandb_init(entity=user, project=project) From 3f7d938228286dc850f80d4205c634a5390638c5 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Tue, 8 Nov 2022 13:43:40 -0800 Subject: [PATCH 12/15] modified test to ensure collection-specific alias changes occurred --- tests/unit_tests/test_public_api.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/test_public_api.py b/tests/unit_tests/test_public_api.py index 2b08cc331d7..5e58fd90e5c 100644 --- a/tests/unit_tests/test_public_api.py +++ b/tests/unit_tests/test_public_api.py @@ -211,21 +211,33 @@ def test_update_aliases_on_artifact(user, relay_server, wandb_init): with open("boom.txt", "w") as f: f.write("testing") artifact.add_file("boom.txt", "test-name") - run.log_artifact(artifact, aliases=["best"]) + art = run.log_artifact(artifact, aliases=["sequence"]) + run.link_artifact(art, f"{user}/{project}/my-sample-portfolio") artifact.wait() run.finish() + # fetch artifact under original parent sequence artifact = Api().artifact( name=f"{user}/{project}/test-artifact:v0", type="test-type" ) - artifact.aliases = ["staging"] + aliases = artifact.aliases + assert "sequence" in aliases + + # fetch artifact under portfolio + # and change aliases under portfolio only + artifact = Api().artifact( + name=f"{user}/{project}/my-sample-portfolio:v0", type="test-type" + ) + aliases = artifact.aliases + assert "sequence" not in aliases + artifact.aliases = ["portfolio"] artifact.aliases.append("boom") artifact.save() artifact = Api().artifact( - name=f"{user}/{project}/test-artifact:v0", type="test-type" + name=f"{user}/{project}/my-sample-portfolio:v0", type="test-type" ) aliases = artifact.aliases + assert "portfolio" in aliases assert "boom" in aliases - assert "staging" in aliases - assert "best" not in aliases + assert "sequence" not in aliases From d64c08649a9f9afa10ac791ee24c6537369897ae Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Tue, 8 Nov 2022 13:45:27 -0800 Subject: [PATCH 13/15] nit --- wandb/apis/public.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index 58738608f31..c9414fdd381 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4763,7 +4763,7 @@ def _save_alias_changes(self): """ ) res = self.client.execute(introspect_query) - valid = res.get("AddAliasesInputInfoType") or None + valid = res.get("AddAliasesInputInfoType") if not valid: return From 7108412923ec0818b38397e7fabcb0c9d46f6441 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 9 Nov 2022 10:59:03 -0800 Subject: [PATCH 14/15] commented on updateArtifact vs new alias endpoints risk --- wandb/apis/public.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index c9414fdd381..c3406a91005 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4710,11 +4710,15 @@ def save(self): """ ) res = self.client.execute(introspect_query) - valid = res.get("AddAliasesInputInfoType") or None + valid = res.get("AddAliasesInputInfoType") aliases = None if not valid: - # Note: dangerous for artifacts linked to multiple collections - # This risk only exists for clients with old wandb backends. + # If valid, wandb backend version >= 0.13.0. + # This means we can safely remove aliases from this updateArtifact request since we'll be calling + # the alias endpoints below in _save_alias_changes. + # If not valid, wandb backend version < 0.13.0. This requires aliases to be sent in updateArtifact. + # Doing so is necessary but can be dangerous since this means the user can blow away all existing + # aliases under multiple artifact collections (i.e if the artifact belongs to more than 1 collection). aliases = [ { "artifactCollectionName": self._artifact_collection_name, From 4217298ddde2b146c30e9c411b97a445189f58d6 Mon Sep 17 00:00:00 2001 From: Vish Rajiv Date: Wed, 9 Nov 2022 11:10:04 -0800 Subject: [PATCH 15/15] this risk doesn't exist because the ability to create portfolios came after the alias endpoints --- wandb/apis/public.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wandb/apis/public.py b/wandb/apis/public.py index c3406a91005..3d86566555f 100644 --- a/wandb/apis/public.py +++ b/wandb/apis/public.py @@ -4717,8 +4717,6 @@ def save(self): # This means we can safely remove aliases from this updateArtifact request since we'll be calling # the alias endpoints below in _save_alias_changes. # If not valid, wandb backend version < 0.13.0. This requires aliases to be sent in updateArtifact. - # Doing so is necessary but can be dangerous since this means the user can blow away all existing - # aliases under multiple artifact collections (i.e if the artifact belongs to more than 1 collection). aliases = [ { "artifactCollectionName": self._artifact_collection_name,