Compare commits

...
9 Commits
Author SHA1 Message Date
Ruslan Kuprieiev 13d1276a12 dvc: bump to 2.6.4 2021-08-20 20:00:55 +03:00
Ruslan Kuprieiev 7da20a7c74 setup: upgrade s3fs
Fixes https://github.com/iterative/dvc/issues/6466
2021-08-20 19:43:14 +03:00
Paweł Redzyński a7175501aa ruamel.yaml: adjust to new DuplicatedKeyError (#6464) 2021-08-19 20:02:16 +03:00
GaoandPeter Rowlands 57899ee86d Remove all of the experiments in the workspace (#6394)
* Remove all of the experiments in the workspace

fix #5676
1. remove all the experiments in workspace
2. add tests for it

* Only remove baseline ones

* Add a new arguments --all

* Update dvc/repo/experiments/remove.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

* Rename and shortcut for these commands

* Solve the issue from removing a changing list.

* Update dvc/repo/experiments/remove.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

* Update dvc/repo/experiments/remove.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

* delete workspace option

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-08-19 10:18:51 +09:00
Saugat Pachhai 4b9a4114fe exp run: pass recursive args to checkout (#6458) 2021-08-18 15:17:33 +05:45
Ruslan Kuprieiev 82e559a51a dvc: bump to 2.6.3 2021-08-17 15:17:48 +03:00
Ruslan Kuprieiev 33c6a9b19b analytics: introduce DVC_NO_ANALYTICS (#6453)
Replacing DVC_ANALYTICS. This is following our other env conventions and
will not have effect if set to false/no/etc, and will only work if set
to true/yes/y/etc. Will disable analytics even if it is explicitly
enabled in our configs.

Kudos @skshetry
2021-08-17 15:16:40 +03:00
Saugat Pachhai 14cc748139 Increase coverage target to 90%. 2021-08-17 12:23:35 +05:45
David de la Iglesia Castro 40abf34c8c Update dvc exp branch help (#6439) 2021-08-17 11:14:44 +09:00
15 changed files with 114 additions and 44 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ coverage:
status:
project:
default:
target: 85
target: 90
threshold: 10%
patch:
default:
+3 -3
View File
@@ -2,7 +2,7 @@ import json
import logging
import os
from .env import DVC_ANALYTICS
from .env import DVC_NO_ANALYTICS
logger = logging.getLogger(__name__)
@@ -44,8 +44,8 @@ def is_enabled():
if env2bool("DVC_TEST"):
return False
enabled = env2bool(DVC_ANALYTICS, None)
if enabled is None:
enabled = not os.getenv(DVC_NO_ANALYTICS)
if enabled:
enabled = to_bool(
Config(validate=False).get("core", {}).get("analytics", "true")
)
+11 -2
View File
@@ -747,7 +747,9 @@ class CmdExperimentsRemove(CmdBase):
def run(self):
self.repo.experiments.remove(
exp_names=self.args.experiment, queue=self.args.queue
exp_names=self.args.experiment,
queue=self.args.queue,
clear_all=self.args.all,
)
return 0
@@ -1237,9 +1239,16 @@ def add_parser(subparsers, parent_parser):
help=EXPERIMENTS_REMOVE_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
experiments_remove_parser.add_argument(
remove_group = experiments_remove_parser.add_mutually_exclusive_group()
remove_group.add_argument(
"--queue", action="store_true", help="Remove all queued experiments."
)
remove_group.add_argument(
"-A",
"--all",
action="store_true",
help="Remove all committed experiments.",
)
experiments_remove_parser.add_argument(
"experiment",
nargs="*",
+1 -1
View File
@@ -9,4 +9,4 @@ DVCLIVE_RESUME = "DVCLIVE_RESUME"
DVC_IGNORE_ISATTY = "DVC_IGNORE_ISATTY"
DVC_EXP_GIT_REMOTE = "DVC_EXP_GIT_REMOTE"
DVC_EXP_AUTO_PUSH = "DVC_EXP_AUTO_PUSH"
DVC_ANALYTICS = "DVC_ANALYTICS"
DVC_NO_ANALYTICS = "DVC_NO_ANALYTICS"
+1 -1
View File
@@ -455,7 +455,7 @@ class Experiments:
)
logger.info(
"\nTo promote an experiment to a Git branch run:\n\n"
"\tdvc exp branch <exp>\n"
"\tdvc exp branch <exp> <branch>\n"
)
@scm_locked
+1
View File
@@ -348,6 +348,7 @@ class BaseExecutor(ABC):
quiet=True,
allow_missing=True,
checkpoint_reset=checkpoint_reset,
recursive=kwargs.get("recursive", False),
)
checkpoint_func = partial(
+28 -8
View File
@@ -7,21 +7,29 @@ from dvc.repo.scm_context import scm_context
from dvc.scm.base import RevError
from .base import EXPS_NAMESPACE, ExpRefInfo
from .utils import exp_refs_by_name, remove_exp_refs
from .utils import exp_refs, exp_refs_by_name, remove_exp_refs
logger = logging.getLogger(__name__)
@locked
@scm_context
def remove(repo, exp_names=None, queue=False, **kwargs):
if not exp_names and not queue:
def remove(
repo,
exp_names=None,
queue=False,
clear_all=False,
**kwargs,
):
if not any([exp_names, queue, clear_all]):
return 0
removed = 0
if queue:
removed += len(repo.experiments.stash)
repo.experiments.stash.clear()
removed += _clear_stash(repo)
if clear_all:
removed += _clear_all(repo)
if exp_names:
remained = _remove_commited_exps(repo, exp_names)
remained = _remove_queued_exps(repo, remained)
@@ -33,6 +41,18 @@ def remove(repo, exp_names=None, queue=False, **kwargs):
return removed
def _clear_stash(repo):
removed = len(repo.experiments.stash)
repo.experiments.stash.clear()
return removed
def _clear_all(repo):
ref_infos = list(exp_refs(repo.scm))
remove_exp_refs(repo.scm, ref_infos)
return len(ref_infos)
def _get_exp_stash_index(repo, ref_or_rev: str) -> Optional[int]:
stash_revs = repo.experiments.stash_revs
for _, ref_info in stash_revs.items():
@@ -53,9 +73,9 @@ def _get_exp_ref(repo, exp_name: str) -> Optional[ExpRefInfo]:
if repo.scm.get_ref(exp_name):
return ExpRefInfo.from_ref(exp_name)
else:
exp_refs = list(exp_refs_by_name(repo.scm, exp_name))
if exp_refs:
return _get_ref(exp_refs, exp_name, cur_rev)
exp_ref_list = list(exp_refs_by_name(repo.scm, exp_name))
if exp_ref_list:
return _get_ref(exp_ref_list, exp_name, cur_rev)
return None
+2 -9
View File
@@ -23,17 +23,10 @@ def load_yaml(path, fs=None):
def parse_yaml(text, path, typ="safe"):
from ruamel.yaml import YAML
from ruamel.yaml import YAMLError as _YAMLError
from ruamel.yaml.constructor import DuplicateKeyError
yaml = YAML(typ=typ)
try:
with reraise(_YAMLError, YAMLFileCorruptedError(path)):
return yaml.load(text) or {}
except DuplicateKeyError as exc:
# NOTE: unfortunately this one doesn't inherit from YAMLError, so we
# have to catch it by-hand. See
# https://yaml.readthedocs.io/en/latest/api.html#duplicate-keys
raise YAMLError(path, exc.problem)
with reraise(_YAMLError, YAMLFileCorruptedError(path)):
return yaml.load(text) or {}
def parse_yaml_for_update(text, path):
+1 -1
View File
@@ -6,7 +6,7 @@
import os
import subprocess
_BASE_VERSION = "2.6.2"
_BASE_VERSION = "2.6.4"
def _generate_version(base_version):
+2 -2
View File
@@ -61,7 +61,7 @@ install_requires = [
"grandalf==0.6",
"distro>=1.3.0",
"appdirs>=1.4.3",
"ruamel.yaml>=0.16.1",
"ruamel.yaml>=0.17.11",
"toml>=0.10.1",
"funcy>=1.14",
"pathspec>=0.6.0,<0.9.0",
@@ -97,7 +97,7 @@ install_requires = [
gs = ["gcsfs==2021.7.0"]
gdrive = ["pydrive2[fsspec]>=1.9.1"]
s3 = ["s3fs==2021.7.0", "aiobotocore[boto3]>1.0.1"]
s3 = ["s3fs==2021.8.0", "aiobotocore[boto3]>1.0.1"]
azure = ["adlfs==2021.7.1", "azure-identity>=1.4.0", "knack"]
oss = ["ossfs==2021.7.5"]
ssh = ["sshfs>=2021.7.1"]
+10 -1
View File
@@ -8,7 +8,7 @@ from funcy import first
from dvc.dvcfile import PIPELINE_FILE
from dvc.repo.experiments.utils import exp_refs_by_rev
from dvc.utils.serialize import PythonFileCorruptedError
from dvc.utils.serialize import PythonFileCorruptedError, load_yaml
from tests.func.test_repro_multistage import COPY_SCRIPT
@@ -657,3 +657,12 @@ def test_modified_data_dep(tmp_dir, scm, dvc, workspace, params, target):
if workspace:
assert (tmp_dir / "metrics.yaml").read_text().strip() == params
assert (tmp_dir / "data").read_text().strip() == "modified"
def test_exp_run_recursive(tmp_dir, scm, dvc, run_copy_metrics):
tmp_dir.dvc_gen("metric_t.json", "foo: 1")
run_copy_metrics(
"metric_t.json", "metric.json", metrics=["metric.json"], no_exec=True
)
assert dvc.experiments.run(".", recursive=True)
assert load_yaml(tmp_dir / "metric.json") == {"foo": 1}
+18
View File
@@ -72,3 +72,21 @@ def test_remove_special_queued_experiments(tmp_dir, scm, dvc, exp_stage):
assert rev3 in dvc.experiments.stash_revs
assert scm.get_ref(str(ref_info1)) is None
assert scm.get_ref(str(ref_info2)) is not None
def test_remove_all(tmp_dir, scm, dvc, exp_stage):
results = dvc.experiments.run(exp_stage.addressing, params=["foo=1"])
ref_info1 = first(exp_refs_by_rev(scm, first(results)))
dvc.experiments.run(exp_stage.addressing, params=["foo=2"], queue=True)
scm.add(["dvc.yaml", "dvc.lock", "copy.py", "params.yaml", "metrics.yaml"])
scm.commit("update baseline")
results = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
ref_info2 = first(exp_refs_by_rev(scm, first(results)))
dvc.experiments.run(exp_stage.addressing, params=["foo=4"], queue=True)
removed = dvc.experiments.remove(clear_all=True)
assert removed == 2
assert len(dvc.experiments.stash) == 2
assert scm.get_ref(str(ref_info2)) is None
assert scm.get_ref(str(ref_info1)) is None
+16 -4
View File
@@ -252,13 +252,25 @@ def test_experiments_pull(dvc, scm, mocker):
)
def test_experiments_remove(dvc, scm, mocker):
cli_args = parse_args(["experiments", "remove", "--queue"])
@pytest.mark.parametrize(
"queue,clear_all",
[(True, False), (False, True)],
)
def test_experiments_remove(dvc, scm, mocker, queue, clear_all):
if queue:
args = "--queue"
if clear_all:
args = "--all"
cli_args = parse_args(["experiments", "remove", args])
assert cli_args.func == CmdExperimentsRemove
cmd = cli_args.func(cli_args)
m = mocker.patch("dvc.repo.experiments.remove.remove", return_value={})
assert cmd.run() == 0
m.assert_called_once_with(cmd.repo, exp_names=[], queue=True)
m.assert_called_once_with(
cmd.repo,
exp_names=[],
queue=queue,
clear_all=clear_all,
)
+12 -9
View File
@@ -87,7 +87,7 @@ def test_is_enabled(dvc, config, result, monkeypatch, tmp_global_dir):
# reset DVC_TEST env var, which affects `is_enabled()`
monkeypatch.delenv("DVC_TEST")
monkeypatch.delenv("DVC_ANALYTICS", raising=False)
monkeypatch.delenv("DVC_NO_ANALYTICS", raising=False)
assert result == analytics.is_enabled()
@@ -96,20 +96,23 @@ def test_is_enabled(dvc, config, result, monkeypatch, tmp_global_dir):
"config, env, result",
[
(None, None, True),
(None, "true", True),
(None, "false", False),
(None, "true", False),
(None, "false", False), # only checking if env is set
("false", None, False),
("false", "true", True),
("false", "true", False),
("false", "false", False),
("true", None, True),
("true", "true", True),
("true", "false", False),
("true", "true", False),
("true", "false", False), # we checking if env is set
],
)
def test_is_enabled_env(dvc, config, env, result, monkeypatch, tmp_global_dir):
def test_is_enabled_env_neg(
dvc, config, env, result, monkeypatch, tmp_global_dir
):
# reset DVC_TEST env var, which affects `is_enabled()`
monkeypatch.delenv("DVC_TEST")
monkeypatch.delenv("DVC_ANALYTICS", raising=False)
monkeypatch.delenv("DVC_NO_ANALYTICS", raising=False)
with dvc.config.edit() as conf:
conf["core"] = {}
@@ -120,7 +123,7 @@ def test_is_enabled_env(dvc, config, env, result, monkeypatch, tmp_global_dir):
conf["core"] = {"analytics": config}
if env is not None:
monkeypatch.setenv("DVC_ANALYTICS", env)
monkeypatch.setenv("DVC_NO_ANALYTICS", env)
assert result == analytics.is_enabled()
+7 -2
View File
@@ -1,6 +1,11 @@
import pytest
from dvc.utils.serialize import EncodingError, YAMLError, load_yaml, parse_yaml
from dvc.utils.serialize import (
EncodingError,
YAMLFileCorruptedError,
load_yaml,
parse_yaml,
)
def test_parse_yaml_duplicate_key_error():
@@ -10,7 +15,7 @@ def test_parse_yaml_duplicate_key_error():
mykey:
- bar
"""
with pytest.raises(YAMLError, match='found duplicate key "mykey"'):
with pytest.raises(YAMLFileCorruptedError):
parse_yaml(text, "mypath")