Files
dvc/tests/func/experiments/test_show.py
T

1078 lines
32 KiB
Python

import logging
import os
import random
from datetime import datetime
from unittest.mock import ANY
import pytest
from funcy import first, get_in
from scmrepo.exceptions import SCMError
from dvc.cli import main
from dvc.repo.experiments.executor.base import BaseExecutor, ExecutorInfo, TaskStatus
from dvc.repo.experiments.queue.base import QueueEntry
from dvc.repo.experiments.refs import CELERY_STASH, ExpRefInfo
from dvc.repo.experiments.show import _CachedError
from dvc.repo.experiments.utils import EXEC_PID_DIR, EXEC_TMP_DIR, exp_refs_by_rev
from dvc.utils import relpath
from dvc.utils.serialize import YAMLFileCorruptedError
LOCK_CONTENTS = {
"read": {
"data/MNIST": [{"pid": 54062, "cmd": "dvc exp run"}],
},
"write": {
"data/MNIST": {"pid": 54062, "cmd": "dvc exp run"},
},
}
def make_executor_info(**kwargs):
# set default values for required info fields
for key in (
"git_url",
"baseline_rev",
"location",
"root_dir",
"dvc_dir",
):
if key not in kwargs:
kwargs[key] = ""
return ExecutorInfo(**kwargs)
@pytest.mark.vscode
def test_show_no_commits(tmp_dir):
from dvc.repo import Repo
from dvc.scm import Git
git = Git.init(tmp_dir.fs_path)
assert git.no_commits
assert Repo.init().experiments.show() == {}
@pytest.mark.vscode
def test_show_branch_and_tag_name(tmp_dir, scm, dvc, exp_stage):
with tmp_dir.branch("new/branch", new=True):
tmp_dir.scm_gen("branch", "branch", "commit")
branch_rev = scm.get_rev()
result = dvc.experiments.show(all_branches=True)
assert result[branch_rev]["baseline"]["data"]["name"] == "new/branch"
scm.tag("new/tag")
tag_rev = scm.get_rev()
result = dvc.experiments.show(all_tags=True)
assert result[tag_rev]["baseline"]["data"]["name"] == "new/tag"
@pytest.mark.vscode
def test_show_simple(tmp_dir, scm, dvc, exp_stage):
assert dvc.experiments.show()["workspace"] == {
"baseline": {
"data": {
"rev": "workspace",
"deps": {
"copy.py": {
"hash": ANY,
"size": ANY,
"nfiles": None,
}
},
"metrics": {"metrics.yaml": {"data": {"foo": 1}}},
"outs": {},
"params": {"params.yaml": {"data": {"foo": 1}}},
"status": "Success",
"executor": None,
"timestamp": None,
}
}
}
@pytest.mark.vscode
@pytest.mark.parametrize("workspace", [True, False])
def test_show_experiment(tmp_dir, scm, dvc, exp_stage, workspace):
baseline_rev = scm.get_rev()
timestamp = datetime.fromtimestamp(
scm.gitpython.repo.rev_parse(baseline_rev).committed_date
)
dvc.experiments.run(exp_stage.addressing, params=["foo=2"], tmp_dir=not workspace)
results = dvc.experiments.show()
expected_baseline = {
"data": {
"rev": ANY,
"deps": {
"copy.py": {
"hash": ANY,
"size": ANY,
"nfiles": None,
}
},
"metrics": {"metrics.yaml": {"data": {"foo": 1}}},
"outs": {},
"params": {"params.yaml": {"data": {"foo": 1}}},
"status": "Success",
"executor": None,
"timestamp": timestamp,
"name": "master",
}
}
expected_params = {"data": {"foo": 2}}
assert set(results.keys()) == {"workspace", baseline_rev}
experiments = results[baseline_rev]
assert len(experiments) == 2
for rev, exp in experiments.items():
if rev == "baseline":
assert exp == expected_baseline
else:
assert exp["data"]["metrics"]["metrics.yaml"] == expected_params
assert exp["data"]["params"]["params.yaml"] == expected_params
@pytest.mark.vscode
def test_show_queued(tmp_dir, scm, dvc, exp_stage):
baseline_rev = scm.get_rev()
dvc.experiments.run(
exp_stage.addressing, params=["foo=2"], queue=True, name="test_name"
)
exp_rev = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
results = dvc.experiments.show()[baseline_rev]
assert len(results) == 2
exp = results[exp_rev]["data"]
assert exp["name"] == "test_name"
assert exp["status"] == "Queued"
assert exp["params"]["params.yaml"] == {"data": {"foo": 2}}
# test that only queued experiments for the current baseline are returned
tmp_dir.gen("foo", "foo")
scm.add(["foo"])
scm.commit("new commit")
new_rev = scm.get_rev()
dvc.experiments.run(exp_stage.addressing, params=["foo=3"], queue=True)
exp_rev = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
results = dvc.experiments.show()[new_rev]
assert len(results) == 2
exp = results[exp_rev]["data"]
assert exp["status"] == "Queued"
assert exp["params"]["params.yaml"] == {"data": {"foo": 3}}
@pytest.mark.vscode
def test_show_failed_experiment(tmp_dir, scm, dvc, failed_exp_stage):
baseline_rev = scm.get_rev()
timestamp = datetime.fromtimestamp(
scm.gitpython.repo.rev_parse(baseline_rev).committed_date
)
dvc.experiments.run(failed_exp_stage.addressing, params=["foo=2"], queue=True)
exp_rev = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
dvc.experiments.run(run_all=True)
experiments = dvc.experiments.show()[baseline_rev]
expected_baseline = {
"data": {
"rev": ANY,
"deps": {
"copy.py": {
"hash": ANY,
"size": ANY,
"nfiles": None,
}
},
"metrics": {},
"outs": {},
"params": {"params.yaml": {"data": {"foo": 1}}},
"status": "Success",
"executor": None,
"timestamp": timestamp,
"name": "master",
}
}
expected_failed = {
"data": {
"rev": ANY,
"timestamp": ANY,
"params": {"params.yaml": {"data": {"foo": 2}}},
"deps": {"copy.py": {"hash": None, "size": None, "nfiles": None}},
"metrics": {},
"outs": {},
"status": "Failed",
"executor": None,
"error": {
"msg": "Experiment run failed.",
"type": "",
},
"name": ANY,
}
}
assert len(experiments) == 2
for rev, exp in experiments.items():
if rev == "baseline":
assert exp == expected_baseline
else:
assert rev == exp_rev
assert exp == expected_failed
@pytest.mark.vscode
@pytest.mark.parametrize("workspace", [True, False])
def test_show_checkpoint(tmp_dir, scm, dvc, checkpoint_stage, capsys, workspace):
baseline_rev = scm.get_rev()
results = dvc.experiments.run(
checkpoint_stage.addressing, params=["foo=2"], tmp_dir=not workspace
)
exp_rev = first(results)
results = dvc.experiments.show()[baseline_rev]
# Assert 4 rows: baseline, 2 checkpoints, and final commit
assert len(results) == checkpoint_stage.iterations + 2
checkpoints = []
for rev, exp in results.items():
if rev != "baseline":
checkpoints.append(rev)
assert exp["data"]["checkpoint_tip"] == exp_rev
capsys.readouterr()
assert main(["exp", "show", "--no-pager"]) == 0
cap = capsys.readouterr()
for i, rev in enumerate(checkpoints):
if i == 0:
name = dvc.experiments.get_exact_name([rev])[rev]
name = f"{rev[:7]} [{name}]"
fs = "╓"
elif i == len(checkpoints) - 1:
name = rev[:7]
fs = "╨"
else:
name = rev[:7]
fs = "╟"
assert f"{fs} {name}" in cap.out
@pytest.mark.vscode
@pytest.mark.parametrize("workspace", [True, False])
def test_show_checkpoint_branch(tmp_dir, scm, dvc, checkpoint_stage, capsys, workspace):
results = dvc.experiments.run(
checkpoint_stage.addressing, params=["foo=2"], tmp_dir=not workspace
)
branch_rev = first(results)
if not workspace:
dvc.experiments.apply(branch_rev)
results = dvc.experiments.run(
checkpoint_stage.addressing,
checkpoint_resume=branch_rev,
tmp_dir=not workspace,
)
checkpoint_a = first(results)
dvc.experiments.apply(branch_rev)
results = dvc.experiments.run(
checkpoint_stage.addressing,
checkpoint_resume=branch_rev,
params=["foo=100"],
tmp_dir=not workspace,
)
checkpoint_b = first(results)
capsys.readouterr()
assert main(["exp", "show", "--no-pager"]) == 0
cap = capsys.readouterr()
for rev in (checkpoint_a, checkpoint_b):
ref = dvc.experiments.get_branch_by_rev(rev)
ref_info = ExpRefInfo.from_ref(ref)
name = f"{rev[:7]} [{ref_info.name}]"
assert f"╓ {name}" in cap.out
assert f"({branch_rev[:7]})" in cap.out
def test_show_filter(
tmp_dir,
scm,
dvc,
capsys,
copy_script,
):
capsys.readouterr()
params_file = tmp_dir / "params.yaml"
params_data = {
"foo": 1,
"bar": 1,
"train/foo": 1,
"train/bar": 1,
"nested": {"foo": 1, "bar": 1},
}
(tmp_dir / params_file).dump(params_data)
dvc.run(
cmd="python copy.py params.yaml metrics.yaml",
metrics_no_cache=["metrics.yaml"],
params=["foo"],
name="copy-file",
deps=["copy.py"],
)
scm.add(
[
"dvc.yaml",
"dvc.lock",
"copy.py",
"params.yaml",
"metrics.yaml",
".gitignore",
]
)
scm.commit("init")
capsys.readouterr()
assert main(["exp", "show", "--drop=.*foo"]) == 0
cap = capsys.readouterr()
for filtered in ["foo", "train/foo", "nested.foo"]:
assert f"params.yaml:{filtered}" not in cap.out
assert f"metrics.yaml:{filtered}" not in cap.out
capsys.readouterr()
assert main(["exp", "show", "--drop=.*foo", "--keep=.*train"]) == 0
cap = capsys.readouterr()
for filtered in ["foo", "nested.foo"]:
assert f"params.yaml:{filtered}" not in cap.out
assert f"metrics.yaml:{filtered}" not in cap.out
assert "params.yaml:train/foo" in cap.out
assert "metrics.yaml:train/foo" in cap.out
capsys.readouterr()
assert main(["exp", "show", "--drop=params.yaml:.*foo"]) == 0
cap = capsys.readouterr()
for filtered in ["foo", "train/foo", "nested.foo"]:
assert f"params.yaml:{filtered}" not in cap.out
assert f"metrics.yaml:{filtered}" in cap.out
capsys.readouterr()
assert main(["exp", "show", "--drop=Created"]) == 0
cap = capsys.readouterr()
assert "Created" not in cap.out
capsys.readouterr()
assert main(["exp", "show", "--drop=Created|Experiment"]) == 0
cap = capsys.readouterr()
assert "Created" not in cap.out
assert "Experiment" not in cap.out
@pytest.mark.vscode
def test_show_multiple_commits(tmp_dir, scm, dvc, exp_stage):
init_rev = scm.get_rev()
tmp_dir.scm_gen("file", "file", "commit")
next_rev = scm.get_rev()
dvc.experiments.show(num=-2)
expected = {"workspace", init_rev, next_rev}
results = dvc.experiments.show(num=2)
assert set(results.keys()) == expected
expected = {"workspace"} | set(scm.branch_revs("master"))
results = dvc.experiments.show(all_commits=True)
assert set(results.keys()) == expected
results = dvc.experiments.show(num=100)
assert set(results.keys()) == expected
def test_show_sort(tmp_dir, scm, dvc, exp_stage, caplog):
with caplog.at_level(logging.ERROR):
assert main(["exp", "show", "--no-pager", "--sort-by=bar"]) != 0
assert "Unknown sort column" in caplog.text
with caplog.at_level(logging.ERROR):
assert main(["exp", "show", "--no-pager", "--sort-by=foo"]) != 0
assert "Ambiguous sort column" in caplog.text
assert main(["exp", "show", "--no-pager", "--sort-by=params.yaml:foo"]) == 0
assert main(["exp", "show", "--no-pager", "--sort-by=metrics.yaml:foo"]) == 0
@pytest.mark.vscode
@pytest.mark.parametrize(
"status, pid_exists",
[
(TaskStatus.RUNNING, True),
(TaskStatus.RUNNING, False),
(TaskStatus.FAILED, False),
],
)
def test_show_running_workspace(
tmp_dir, scm, dvc, exp_stage, capsys, caplog, status, pid_exists, mocker
):
from dvc.rwlock import RWLOCK_FILE
pid_dir = os.path.join(dvc.tmp_dir, EXEC_TMP_DIR, EXEC_PID_DIR)
lock_file = relpath(os.path.join(dvc.tmp_dir, RWLOCK_FILE), str(tmp_dir))
info = make_executor_info(location=BaseExecutor.DEFAULT_LOCATION, status=status)
pidfile = os.path.join(
pid_dir,
"workspace",
f"workspace{BaseExecutor.INFOFILE_EXT}",
)
os.makedirs(os.path.dirname(pidfile), exist_ok=True)
(tmp_dir / pidfile).dump_json(info.asdict())
(tmp_dir / lock_file).dump_json(LOCK_CONTENTS)
mocker.patch("psutil.pid_exists", return_value=pid_exists)
assert dvc.experiments.show().get("workspace") == {
"baseline": {
"data": {
"rev": "workspace",
"deps": {
"copy.py": {
"hash": ANY,
"size": ANY,
"nfiles": None,
}
},
"metrics": {"metrics.yaml": {"data": {"foo": 1}}},
"params": {"params.yaml": {"data": {"foo": 1}}},
"outs": {},
"status": (
"Running"
if status == TaskStatus.RUNNING and pid_exists
else "Success"
),
"executor": (
info.location
if status == TaskStatus.RUNNING and pid_exists
else None
),
"timestamp": None,
}
}
}
capsys.readouterr()
assert main(["exp", "show", "--csv"]) == 0
cap = capsys.readouterr()
if status == TaskStatus.RUNNING:
if pid_exists:
assert "Running" in cap.out
assert info.location in cap.out
else:
cmd = LOCK_CONTENTS["read"]["data/MNIST"][0]["cmd"]
pid = LOCK_CONTENTS["read"]["data/MNIST"][0]["pid"]
assert (
f"Process '{cmd}' with (Pid {pid}), in RWLock-file "
f"'{lock_file}' had been killed." in caplog.text
)
assert f"Delete corrupted RWLock-file '{lock_file}'" in caplog.text
def test_show_running_tempdir(tmp_dir, scm, dvc, exp_stage, mocker):
baseline_rev = scm.get_rev()
run_results = dvc.experiments.run(
exp_stage.addressing, params=["foo=2"], tmp_dir=True
)
exp_rev = first(run_results)
exp_ref = first(exp_refs_by_rev(scm, exp_rev))
queue = dvc.experiments.tempdir_queue
stash_rev = "abc123"
entries = [
QueueEntry(
str(tmp_dir / ".dvc" / "tmp" / "foo"),
str(tmp_dir / ".dvc" / "tmp" / "foo"),
str(exp_ref),
stash_rev,
exp_ref.baseline_sha,
None,
exp_ref.name,
None,
)
]
mocker.patch.object(
dvc.experiments.tempdir_queue,
"iter_active",
return_value=entries,
)
info = make_executor_info(location=BaseExecutor.DEFAULT_LOCATION)
pidfile = queue.get_infofile_path(stash_rev)
os.makedirs(os.path.dirname(pidfile), exist_ok=True)
(tmp_dir / pidfile).dump_json(info.asdict())
mock_fetch = mocker.patch.object(
dvc.experiments.tempdir_queue,
"get_running_exps",
return_value={exp_rev: info.asdict()},
)
results = dvc.experiments.show()
mock_fetch.assert_has_calls(
[mocker.call(True)],
)
exp_data = get_in(results, [baseline_rev, exp_rev, "data"])
assert exp_data["status"] == "Running"
assert exp_data["executor"] == info.location
assert results["workspace"]["baseline"]["data"]["status"] == "Success"
def test_show_running_celery(tmp_dir, scm, dvc, exp_stage, mocker):
baseline_rev = scm.get_rev()
dvc.experiments.run(exp_stage.addressing, params=["foo=2"], queue=True)
exp_rev = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
queue = dvc.experiments.celery_queue
entries = list(queue.iter_queued())
mocker.patch.object(
dvc.experiments.celery_queue,
"iter_active",
return_value=entries,
)
info = make_executor_info(location=BaseExecutor.DEFAULT_LOCATION)
pidfile = queue.get_infofile_path(entries[0].stash_rev)
os.makedirs(os.path.dirname(pidfile), exist_ok=True)
(tmp_dir / pidfile).dump_json(info.asdict())
results = dvc.experiments.show()
exp_data = get_in(results, [baseline_rev, exp_rev, "data"])
assert exp_data["status"] == "Running"
assert exp_data["executor"] == info.location
assert results["workspace"]["baseline"]["data"]["status"] == "Success"
def test_show_running_checkpoint(tmp_dir, scm, dvc, checkpoint_stage, mocker):
from dvc.repo.experiments.executor.local import TempDirExecutor
baseline_rev = scm.get_rev()
dvc.experiments.run(checkpoint_stage.addressing, params=["foo=2"], queue=True)
queue = dvc.experiments.celery_queue
entries = list(queue.iter_queued())
run_results = dvc.experiments.run(run_all=True)
checkpoint_rev = first(run_results)
exp_ref = first(exp_refs_by_rev(scm, checkpoint_rev))
mocker.patch.object(
dvc.experiments.celery_queue,
"iter_active",
return_value=entries,
)
mocker.patch.object(
dvc.experiments.celery_queue,
"iter_failed",
return_value=[],
)
pidfile = queue.get_infofile_path(entries[0].stash_rev)
info = make_executor_info(
git_url="foo.git",
baseline_rev=baseline_rev,
location=TempDirExecutor.DEFAULT_LOCATION,
status=TaskStatus.RUNNING,
)
os.makedirs(os.path.dirname(pidfile), exist_ok=True)
(tmp_dir / pidfile).dump_json(info.asdict())
mocker.patch.object(BaseExecutor, "fetch_exps", return_value=[str(exp_ref)])
results = dvc.experiments.show()
checkpoint_res = get_in(results, [baseline_rev, checkpoint_rev, "data"])
assert checkpoint_res["status"] == "Running"
assert checkpoint_res["executor"] == info.location
assert results["workspace"]["baseline"]["data"]["status"] == "Success"
def test_show_with_broken_repo(tmp_dir, scm, dvc, exp_stage, caplog):
baseline_rev = scm.get_rev()
exp1 = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
exp2 = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
with open("dvc.yaml", "a", encoding="utf-8") as fd:
fd.write("breaking the yaml!")
result = dvc.experiments.show()
rev1 = first(exp1)
rev2 = first(exp2)
baseline = result[baseline_rev]
paths = ["data", "params", "params.yaml"]
assert get_in(baseline[rev1], paths) == {"data": {"foo": 2}}
assert get_in(baseline[rev2], paths) == {"data": {"foo": 3}}
paths = ["workspace", "baseline", "error"]
assert isinstance(get_in(result, paths), (_CachedError, YAMLFileCorruptedError))
assert "YAML file structure is corrupted" in str(get_in(result, paths))
def test_show_csv(tmp_dir, scm, dvc, exp_stage, capsys):
import time
baseline_rev = scm.get_rev()
def _get_rev_isotimestamp(rev):
return datetime.fromtimestamp(
scm.gitpython.repo.rev_parse(rev).committed_date
).isoformat()
result1 = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
rev1 = first(result1)
ref_info1 = first(exp_refs_by_rev(scm, rev1))
# at least 1 second gap between these experiments to make sure
# the previous experiment to be regarded as branch_base
time.sleep(1)
result2 = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
rev2 = first(result2)
ref_info2 = first(exp_refs_by_rev(scm, rev2))
capsys.readouterr()
assert main(["exp", "show", "--csv"]) == 0
cap = capsys.readouterr()
data_dep = first(x for x in dvc.index.deps if "copy.py" in x.fspath)
data_hash = data_dep.hash_info.value[:7]
assert "Experiment,rev,typ,Created,parent" in cap.out
assert "metrics.yaml:foo,params.yaml:foo,copy.py" in cap.out
assert f",workspace,baseline,,,3,3,{data_hash}" in cap.out
assert (
"master,{},baseline,{},,1,1,{}".format(
baseline_rev[:7], _get_rev_isotimestamp(baseline_rev), data_hash
)
in cap.out
)
assert (
"{},{},branch_base,{},,2,2,{}".format(
ref_info1.name, rev1[:7], _get_rev_isotimestamp(rev1), data_hash
)
in cap.out
)
assert (
"{},{},branch_commit,{},,3,3,{}".format(
ref_info2.name, rev2[:7], _get_rev_isotimestamp(rev2), data_hash
)
in cap.out
)
def test_show_only_changed(tmp_dir, dvc, scm, capsys, copy_script):
params_file = tmp_dir / "params.yaml"
params_data = {
"foo": 1,
"goobar": 1,
}
(tmp_dir / params_file).dump(params_data)
dvc.run(
cmd="python copy.py params.yaml metrics.yaml",
metrics_no_cache=["metrics.yaml"],
params=["foo", "goobar"],
name="copy-file",
deps=["copy.py"],
)
scm.add(
[
"dvc.yaml",
"dvc.lock",
"copy.py",
"params.yaml",
"metrics.yaml",
".gitignore",
]
)
scm.commit("init")
dvc.experiments.run(params=["foo=2"])
capsys.readouterr()
assert main(["exp", "show"]) == 0
cap = capsys.readouterr()
assert "goobar" in cap.out
capsys.readouterr()
assert main(["exp", "show", "--only-changed"]) == 0
cap = capsys.readouterr()
assert "goobar" not in cap.out
capsys.readouterr()
assert main(["exp", "show", "--only-changed", "--keep=.*bar"]) == 0
cap = capsys.readouterr()
assert "params.yaml:goobar" in cap.out
assert "metrics.yaml:goobar" in cap.out
def test_show_parallel_coordinates(tmp_dir, dvc, scm, mocker, capsys, copy_script):
from dvc.commands.experiments import show
webbroser_open = mocker.patch("webbrowser.open")
show_experiments = mocker.spy(show, "show_experiments")
params_file = tmp_dir / "params.yaml"
params_data = {
"foo": 1,
"bar": 1,
}
(tmp_dir / params_file).dump(params_data)
dvc.run(
cmd="python copy.py params.yaml metrics.yaml",
metrics_no_cache=["metrics.yaml"],
params=["foo", "bar"],
name="copy-file",
deps=["copy.py"],
)
scm.add(
[
"dvc.yaml",
"dvc.lock",
"copy.py",
"params.yaml",
"metrics.yaml",
".gitignore",
]
)
scm.commit("init")
dvc.experiments.run(params=["foo=2"])
assert main(["exp", "show", "--pcp"]) == 0
kwargs = show_experiments.call_args[1]
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
assert all(rev in html_text for rev in ["workspace", "master"])
assert "[exp-" not in html_text
assert '{"label": "metrics.yaml:foo", "values": [2.0, 1.0]}' in html_text
assert '{"label": "params.yaml:foo", "values": [2.0, 1.0]}' in html_text
assert '"line": {"color": [1, 0]' in html_text
assert '"label": "metrics.yaml:bar"' not in html_text
assert '"label": "Created"' not in html_text
assert main(["exp", "show", "--pcp", "--sort-by", "metrics.yaml:foo"]) == 0
kwargs = show_experiments.call_args[1]
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
assert '"line": {"color": [2.0, 1.0]' in html_text
assert main(["exp", "show", "--pcp", "--out", "experiments"]) == 0
kwargs = show_experiments.call_args[1]
assert kwargs["out"] == "experiments"
assert (tmp_dir / "experiments" / "index.html").exists()
assert main(["exp", "show", "--pcp", "--open"]) == 0
webbroser_open.assert_called()
params_data = {"foo": 1, "bar": 1, "foobar": 2}
(tmp_dir / params_file).dump(params_data)
assert main(["exp", "show", "--pcp"]) == 0
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
assert '{"label": "foobar", "values": [2.0, null, null]}' in html_text
assert main(["exp", "show", "--pcp", "--drop", "foobar"]) == 0
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
assert '"label": "Created"' not in html_text
assert '"label": "foobar"' not in html_text
assert main(["exp", "show", "--pcp", "--drop", "Experiment"]) == 0
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
assert '"label": "Experiment"' not in html_text
@pytest.mark.vscode
def test_show_outs(tmp_dir, dvc, scm, erepo_dir, copy_script):
params_file = tmp_dir / "params.yaml"
params_data = {
"foo": 1,
"bar": 1,
}
(tmp_dir / params_file).dump(params_data)
dvc.run(
cmd="python copy.py params.yaml metrics.yaml && echo out > out",
metrics_no_cache=["metrics.yaml"],
params=["foo", "bar"],
name="copy-file",
deps=["copy.py"],
outs=["out"],
)
scm.commit("init")
outs = dvc.experiments.show()["workspace"]["baseline"]["data"]["outs"]
assert outs == {
"out": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": False,
}
}
tmp_dir.dvc_gen("out_add", "foo", commit="dvc add output")
outs = dvc.experiments.show()["workspace"]["baseline"]["data"]["outs"]
assert outs == {
"out": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": False,
},
"out_add": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": True,
},
}
with erepo_dir.chdir():
erepo_dir.dvc_gen("out", "out content", commit="create out")
dvc.imp(os.fspath(erepo_dir), "out", "out_imported")
outs = dvc.experiments.show()["workspace"]["baseline"]["data"]["outs"]
assert outs == {
"out": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": False,
},
"out_add": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": True,
},
"out_imported": {
"hash": ANY,
"size": ANY,
"nfiles": None,
"use_cache": True,
"is_data_source": True,
},
}
def test_metrics_renaming(tmp_dir, dvc, scm, capsys, copy_script):
params_file = tmp_dir / "params.yaml"
params_data = {
"foo": 1,
}
(tmp_dir / params_file).dump(params_data)
dvc.run(
cmd="python copy.py params.yaml metrics.yaml",
metrics_no_cache=["metrics.yaml"],
params=["foo"],
name="copy-file",
deps=["copy.py"],
)
scm.add(
[
"dvc.yaml",
"dvc.lock",
"copy.py",
"params.yaml",
"metrics.yaml",
".gitignore",
]
)
scm.commit("metrics.yaml")
metrics_rev = scm.get_rev()
dvc.run(
cmd="python copy.py params.yaml scores.yaml",
metrics_no_cache=["scores.yaml"],
params=["foo"],
name="copy-file",
deps=["copy.py"],
)
scm.add(
[
"dvc.yaml",
"dvc.lock",
"params.yaml",
"scores.yaml",
]
)
scm.commit("scores.yaml")
scores_rev = scm.get_rev()
capsys.readouterr()
assert main(["exp", "show", "--csv", "-A"]) == 0
cap = capsys.readouterr()
def _get_rev_isotimestamp(rev):
return datetime.fromtimestamp(
scm.gitpython.repo.rev_parse(rev).committed_date
).isoformat()
assert (
"master,{},baseline,{},,1,,1".format(
scores_rev[:7], _get_rev_isotimestamp(scores_rev)
)
in cap.out
)
assert (
",{},baseline,{},,,1,1".format(
metrics_rev[:7], _get_rev_isotimestamp(metrics_rev)
)
in cap.out
)
def test_show_sorted_deps(tmp_dir, dvc, scm, capsys):
tmp_dir.gen("a", "a")
tmp_dir.gen("b", "b")
tmp_dir.gen("c", "c")
tmp_dir.gen("z", "z")
dvc.run(
cmd="echo foo",
name="deps",
deps=["a", "b", "z", "c"],
)
capsys.readouterr()
assert main(["exp", "show", "--csv"]) == 0
cap = capsys.readouterr()
assert "a,b,c,z" in cap.out
@pytest.mark.vscode
def test_show_queued_error(tmp_dir, scm, dvc, exp_stage, mocker):
baseline_rev = scm.get_rev()
dvc.experiments.run(
exp_stage.addressing, params=["foo=2"], queue=True, name="test_name"
)
exp_rev_2 = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
commit_2 = scm.resolve_commit(exp_rev_2)
dvc.experiments.run(exp_stage.addressing, params=["foo=3"], queue=True)
exp_rev_3 = dvc.experiments.scm.resolve_rev(f"{CELERY_STASH}@{{0}}")
def resolve_commit(rev):
if rev == exp_rev_3:
raise SCMError
return commit_2
mocker.patch.object(
scm,
"resolve_commit",
side_effect=mocker.MagicMock(side_effect=resolve_commit),
)
results = dvc.experiments.show()[baseline_rev]
assert len(results) == 2
exp_2 = results[exp_rev_2]["data"]
assert exp_2["status"] == "Queued"
assert exp_2["params"]["params.yaml"] == {"data": {"foo": 2}}
@pytest.mark.vscode
def test_show_completed_error(tmp_dir, scm, dvc, exp_stage, mocker):
baseline_rev = scm.get_rev()
result_2 = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
exp_rev_2 = first(result_2)
commit_2 = scm.resolve_commit(exp_rev_2)
result_3 = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
exp_rev_3 = first(result_3)
def resolve_commit(rev):
if rev == exp_rev_3:
raise SCMError
return commit_2
mocker.patch.object(
scm,
"resolve_commit",
side_effect=mocker.MagicMock(side_effect=resolve_commit),
)
experiments = dvc.experiments.show()[baseline_rev]
assert len(experiments) == 2
assert exp_rev_2 in experiments
@pytest.mark.vscode
def test_show_checkpoint_error(tmp_dir, scm, dvc, checkpoint_stage, mocker):
baseline_rev = scm.get_rev()
results = dvc.experiments.run(checkpoint_stage.addressing, params=["foo=2"])
exp_rev = first(results)
exp_ref = str(first(exp_refs_by_rev(scm, exp_rev)))
results = dvc.experiments.show()[baseline_rev]
# Assert 4 rows: baseline, 2 checkpoints, and final commit
assert len(results) == checkpoint_stage.iterations + 2
checkpoints = {}
for rev in results:
if rev != "baseline":
checkpoints[rev] = scm.resolve_commit(rev)
checkpoints[exp_ref] = scm.resolve_commit(exp_ref)
checkpoints[baseline_rev] = scm.resolve_commit(baseline_rev)
failed_rev = random.choice(list(checkpoints.keys()))
def resolve_commit(rev):
if rev == failed_rev:
raise SCMError
return checkpoints[rev]
mocker.patch.object(
scm,
"resolve_commit",
side_effect=mocker.MagicMock(side_effect=resolve_commit),
)
results = dvc.experiments.show(force=True)[baseline_rev]
assert len(results) == 1
@pytest.mark.vscode
def test_show_baseline_error(tmp_dir, scm, dvc, exp_stage, mocker):
baseline_rev = scm.get_rev()
branch = scm.active_branch()
result_2 = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
exp_rev_2 = first(result_2)
commit_2 = scm.resolve_commit(exp_rev_2)
def resolve_commit(rev):
if rev == baseline_rev:
raise SCMError
return commit_2
mocker.patch.object(
scm,
"resolve_commit",
side_effect=mocker.MagicMock(side_effect=resolve_commit),
)
experiments = dvc.experiments.show()[baseline_rev]
assert len(experiments) == 1
assert experiments["baseline"]["data"] == {"name": branch}
assert isinstance(experiments["baseline"]["error"], (SCMError, _CachedError))