Add API to add artifacts (#9345)

* api call to add/write artifact

* return dvcyaml path on add

* create dir for dvc.yaml if needed

* create dir for dvc.yaml if needed - fix

* allow nested artifact names

* make dvc get download artifacts

* Apply suggestions from code review

Co-authored-by: Oded Messer <[email protected]>

* use feedback

* remove 'dvc get' from this PR, keep only adding artifacts

* allow first char of artifact name to be a number

* add note about constants

* fix parsing artifact name

* remove 'parse_name' and write tests for 'add'

* Apply suggestions from code review

Co-authored-by: Oded Messer <[email protected]>

* updates from comments

* fix for studio

* fix again

* making check_name_format to raise an exception

* Update dvc/repo/artifacts.py

---------

Co-authored-by: Oded Messer <[email protected]>
This commit is contained in:
Alexander Guschin
2023-05-15 07:55:43 +00:00
committed by GitHub
co-authored by Oded Messer
parent 915dd5062e
commit 45181504ea
4 changed files with 120 additions and 55 deletions
+56 -22
View File
@@ -3,6 +3,7 @@ import os
import pytest
from dvc.annotations import Artifact
from dvc.exceptions import InvalidArgumentError
from dvc.repo.artifacts import name_is_compatible
from dvc.utils.strictyaml import YAMLSyntaxError, YAMLValidationError
@@ -21,7 +22,7 @@ dvcyaml = {
}
def test_reading_artifacts_subdir(tmp_dir, dvc):
def test_artifacts_read_subdir(tmp_dir, dvc):
(tmp_dir / "dvc.yaml").dump(dvcyaml)
subdir = tmp_dir / "subdir"
@@ -38,6 +39,55 @@ def test_reading_artifacts_subdir(tmp_dir, dvc):
}
def test_artifacts_add_subdir(tmp_dir, dvc):
subdir = tmp_dir / "subdir"
subdir.mkdir()
(subdir / "dvc.yaml").dump(dvcyaml)
new_art = Artifact(path="path")
tmp_dir.dvc.artifacts.add("new", new_art, dvcfile="subdir/dvc.yaml")
artifacts = {
name: Artifact(**values) for name, values in dvcyaml["artifacts"].items()
}
artifacts["new"] = new_art
assert tmp_dir.dvc.artifacts.read() == {
f"subdir{os.path.sep}dvc.yaml": artifacts,
}
def test_artifacts_add_abspath(tmp_dir, dvc):
subdir = tmp_dir / "subdir"
subdir.mkdir()
new_art = Artifact(path="path")
tmp_dir.dvc.artifacts.add(
"new", new_art, dvcfile=os.path.abspath("subdir/dvc.yaml")
)
assert tmp_dir.dvc.artifacts.read() == {
f"subdir{os.path.sep}dvc.yaml": {"new": new_art},
}
def test_artifacts_add_fails_on_dvc_subrepo(tmp_dir, dvc):
# adding artifact to the DVC subrepo from the parent DVC repo
# shouldn't work
subdir = tmp_dir / "subdir"
(subdir / ".dvc").mkdir(parents=True)
with pytest.raises(InvalidArgumentError):
tmp_dir.dvc.artifacts.add(
"failing", Artifact(path="path"), dvcfile="subdir/dvc.yaml"
)
with pytest.raises(InvalidArgumentError):
tmp_dir.dvc.artifacts.add(
"failing", Artifact(path="path"), dvcfile="subdir/dvclive/dvc.yaml"
)
bad_dvcyaml_extra_field = {
"artifacts": {
"lol": {"kek": "cheburek", "path": "lol"},
@@ -71,7 +121,7 @@ artifacts:
"""
def test_broken_dvcyaml_id_duplication(tmp_dir, dvc):
def test_artifacts_read_fails_on_id_duplication(tmp_dir, dvc):
with open(tmp_dir / "dvc.yaml", "w") as f:
f.write(bad_dvcyaml_id_duplication)
@@ -79,32 +129,19 @@ def test_broken_dvcyaml_id_duplication(tmp_dir, dvc):
tmp_dir.dvc.artifacts.read()
dvcyaml_redirecting = {"artifacts": "artifacts.yaml"}
def test_read_artifacts_yaml(tmp_dir, dvc):
(tmp_dir / "dvc.yaml").dump(dvcyaml_redirecting)
(tmp_dir / "artifacts.yaml").dump(dvcyaml["artifacts"])
artifacts = {
name: Artifact(**values) for name, values in dvcyaml["artifacts"].items()
}
assert tmp_dir.dvc.artifacts.read() == {
"dvc.yaml": artifacts,
}
@pytest.mark.parametrize(
"name",
[
"1",
"m",
"nn",
"m1",
"1nn",
"model-prod",
"model-prod-v1",
],
)
def test_check_name_is_valid(name):
def test_name_is_compatible(name):
assert name_is_compatible(name)
@@ -112,10 +149,8 @@ def test_check_name_is_valid(name):
"name",
[
"",
"1",
"m/",
"/m",
"1nn",
"###",
"@@@",
"a model",
@@ -125,8 +160,7 @@ def test_check_name_is_valid(name):
"model@1",
"model#1",
"@namespace/model",
"namespace/model",
],
)
def test_check_name_is_invalid(name):
def test_name_is_compatible_fails(name):
assert not name_is_compatible(name)