Compare commits

...
Author SHA1 Message Date
David de la Iglesia Castro 6a5fa998ad deps: bumps dvc-azure>=2.21.2
Closes #9506 via https://github.com/iterative/dvc-azure/pull/48

(cherry picked from commit a1b891ce01)
2023-05-31 14:41:41 +02:00
daavoo cbc8911863 repro: Skip pulling run cache if no-run-cache was passed.
(cherry picked from commit 5a6f834088)
2023-05-29 19:47:19 +02:00
daavoo 4a5307603f repro: Continue on RunCacheNotSupported
Closes #9470

(cherry picked from commit e98bf38d4c)
2023-05-29 19:47:19 +02:00
Peter Rowlands 6d783b8d97 exp: always log running experiment name at the start of a run
(cherry picked from commit 89109fafcf)
2023-05-23 16:14:47 +09:00
Peter Rowlands 0e0299e39d exp: support switching to live workspace exp name
(cherry picked from commit aa19e24670)
2023-05-23 16:14:47 +09:00
Peter Rowlands 1652e6af7b exp: drop deprecated get_running_exps()
- replaced with queue.collect_active()

(cherry picked from commit 629223cbc2)
2023-05-23 16:14:47 +09:00
Peter Rowlands e655844712 plots diff: use exp switch_repo context instead of brancher
(cherry picked from commit 96449f157c)
2023-05-23 16:14:47 +09:00
Peter Rowlands 98cd479876 exp: add contextmanager for switching to active tempdir repo
(cherry picked from commit 8cb2646023)
2023-05-23 16:14:47 +09:00
mergify[bot]anddaavoo 45c9b2c9f6 get_random_exp_name: Use custom random generator. (backport #9490) (#9491)
get_random_exp_name: Use custom random generator.

See https://github.com/iterative/dvclive/pull/576

Co-authored-by: Ivan Shcheklein <[email protected]>
(cherry picked from commit eb45cda5d0)

Co-authored-by: daavoo <[email protected]>
2023-05-22 16:04:14 +05:45
mergify[bot]andskshetry a9ae450a64 stage add: introduce --run option (backport #9480) (#9487)
stage add: introduce --run option (#9480)

* stage add: introduce --run option

Part of #5846.

* fix ui message

(cherry picked from commit 1989c0fe98)

Co-authored-by: skshetry <[email protected]>
2023-05-22 12:48:15 +05:45
daavoo 60e161004d exp: Fix --rev args for ls pull and remove.
Closes #9471

(cherry picked from commit 3ddd4b87fc)
2023-05-21 16:32:31 +02:00
23 changed files with 290 additions and 122 deletions
+30 -1
View File
@@ -1,5 +1,6 @@
import argparse import argparse
import logging import logging
from contextlib import contextmanager
from itertools import chain, filterfalse from itertools import chain, filterfalse
from typing import TYPE_CHECKING, Dict, Iterable, List from typing import TYPE_CHECKING, Dict, Iterable, List
@@ -119,8 +120,22 @@ def parse_cmd(commands: List[str]) -> str:
return " ".join(map(quote_argument, commands)) return " ".join(map(quote_argument, commands))
@contextmanager
def _disable_logging(highest_level=logging.CRITICAL):
previous_level = logging.root.manager.disable
logging.disable(highest_level)
try:
yield
finally:
logging.disable(previous_level)
class CmdStageAdd(CmdBase): class CmdStageAdd(CmdBase):
def run(self): def run(self):
from dvc.repo import lock_repo
kwargs = vars(self.args) kwargs = vars(self.args)
kwargs.update( kwargs.update(
{ {
@@ -128,7 +143,15 @@ class CmdStageAdd(CmdBase):
"params": parse_params(self.args.params), "params": parse_params(self.args.params),
} }
) )
self.repo.stage.add(**kwargs)
with self.repo.scm_context, lock_repo(self.repo):
with _disable_logging(logging.INFO):
stage = self.repo.stage.add(**kwargs)
logger.info("Added stage %r in %r", stage.addressing, stage.relpath)
if self.args.run:
stage.run()
stage.dump(update_pipeline=False)
return 0 return 0
@@ -257,6 +280,12 @@ def _add_common_args(parser):
"This doesn't affect any DVC operations." "This doesn't affect any DVC operations."
), ),
) )
parser.add_argument(
"--run",
action="store_true",
default=False,
help="Execute the stage after generating it.",
)
parser.add_argument( parser.add_argument(
"command", "command",
nargs=argparse.REMAINDER, nargs=argparse.REMAINDER,
-11
View File
@@ -449,17 +449,6 @@ class Experiments:
result[rev] = name result[rev] = name
return result return result
def get_running_exps(self, fetch_refs: bool = True) -> Dict[str, Dict]:
"""Return info for running experiments."""
result = {}
for queue in (
self.workspace_queue,
self.tempdir_queue,
self.celery_queue,
):
result.update(queue.get_running_exps(fetch_refs))
return result
def apply(self, *args, **kwargs): def apply(self, *args, **kwargs):
from dvc.repo.experiments.apply import apply from dvc.repo.experiments.apply import apply
+47
View File
@@ -0,0 +1,47 @@
from contextlib import ExitStack, contextmanager
from typing import TYPE_CHECKING, Iterator, Tuple
from dvc.repo.experiments.exceptions import InvalidExpRevError
from dvc.scm import RevError
if TYPE_CHECKING:
from dvc.repo import Repo
@contextmanager
def switch_repo(
repo: "Repo",
rev: str,
) -> Iterator[Tuple["Repo", str]]:
"""Return a repo instance (brancher) switched to rev.
If rev is the name of a running experiment, the returned instance will be
the live repo wherever the experiment is running.
NOTE: This will not resolve git SHA's that only exist in queued exp workspaces
(it will only match queued exp names).
"""
try:
with repo.switch(rev):
yield repo, rev
return
except RevError as exc:
orig_exc = exc
exps = repo.experiments
if rev == exps.workspace_queue.get_running_exp():
yield repo, "workspace"
return
for queue in (exps.tempdir_queue, exps.celery_queue):
try:
active_repo = queue.active_repo(rev)
except InvalidExpRevError:
continue
stack = ExitStack()
stack.enter_context(active_repo)
stack.enter_context(active_repo.switch("workspace"))
with stack:
yield active_repo, rev
return
raise orig_exc
+7
View File
@@ -114,3 +114,10 @@ class UnresolvedRunningExpNamesError(UnresolvedExpNamesError):
class ExpQueueEmptyError(DvcException): class ExpQueueEmptyError(DvcException):
pass pass
class ExpNotStartedError(DvcException):
def __init__(self, name: str):
super().__init__(
f"Queued experiment '{name}' exists but has not started running yet"
)
+3
View File
@@ -497,6 +497,9 @@ class BaseExecutor(ABC):
exp_ref: Optional["ExpRefInfo"] = None exp_ref: Optional["ExpRefInfo"] = None
repro_force: bool = False repro_force: bool = False
if info.name:
ui.write(f"Reproducing experiment '{info.name}'")
with cls._repro_dvc( with cls._repro_dvc(
info, info,
infofile, infofile,
+6 -3
View File
@@ -1,6 +1,6 @@
import logging import logging
from collections import defaultdict from collections import defaultdict
from typing import Optional from typing import List, Optional, Union
from dvc.repo import locked from dvc.repo import locked
from dvc.repo.scm_context import scm_context from dvc.repo.scm_context import scm_context
@@ -15,14 +15,17 @@ logger = logging.getLogger(__name__)
@scm_context @scm_context
def ls( def ls(
repo, repo,
rev: Optional[str] = None, rev: Optional[Union[List[str], str]] = None,
all_commits: bool = False, all_commits: bool = False,
num: int = 1, num: int = 1,
git_remote: Optional[str] = None, git_remote: Optional[str] = None,
): ):
rev_set = None rev_set = None
if not all_commits: if not all_commits:
revs = iter_revs(repo.scm, [rev or "HEAD"], num) rev = rev or "HEAD"
if isinstance(rev, str):
rev = [rev]
revs = iter_revs(repo.scm, rev, num)
rev_set = set(revs.keys()) rev_set = set(revs.keys())
ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote) ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote)
+4 -2
View File
@@ -23,7 +23,7 @@ def pull( # noqa: C901
git_remote: str, git_remote: str,
exp_names: Union[Iterable[str], str], exp_names: Union[Iterable[str], str],
all_commits=False, all_commits=False,
rev: Optional[str] = None, rev: Optional[Union[List[str], str]] = None,
num=1, num=1,
force: bool = False, force: bool = False,
pull_cache: bool = False, pull_cache: bool = False,
@@ -49,7 +49,9 @@ def pull( # noqa: C901
raise UnresolvedExpNamesError(unresolved_exp_names) raise UnresolvedExpNamesError(unresolved_exp_names)
if rev: if rev:
rev_dict = iter_revs(repo.scm, [rev], num) if isinstance(rev, str):
rev = [rev]
rev_dict = iter_revs(repo.scm, rev, num)
rev_set = set(rev_dict.keys()) rev_set = set(rev_dict.keys())
ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote) ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote)
for _, ref_info_list in ref_info_dict.items(): for _, ref_info_list in ref_info_dict.items():
+23 -8
View File
@@ -729,14 +729,6 @@ class BaseStashQueue(ABC):
message=f"commit: {msg}", message=f"commit: {msg}",
) )
@abstractmethod
def get_running_exps(self, fetch_refs: bool = True) -> Dict[str, Dict]:
"""Get the execution info of the currently running experiments
Args:
fetch_ref (bool): fetch completed checkpoints or not.
"""
@abstractmethod @abstractmethod
def collect_active_data( def collect_active_data(
self, self,
@@ -790,3 +782,26 @@ class BaseStashQueue(ABC):
Returns: Returns:
Dict mapping baseline revision to list of queued experiments. Dict mapping baseline revision to list of queued experiments.
""" """
def active_repo(self, name: str) -> "Repo":
"""Return a Repo for the specified active experiment if it exists."""
from dvc.repo import Repo
from dvc.repo.experiments.exceptions import (
ExpNotStartedError,
InvalidExpRevError,
)
from dvc.repo.experiments.executor.base import ExecutorInfo, TaskStatus
for entry in self.iter_active():
if entry.name != name:
continue
infofile = self.get_infofile_path(entry.stash_rev)
executor_info = ExecutorInfo.load_json(infofile)
if executor_info.status < TaskStatus.RUNNING:
raise ExpNotStartedError(name)
dvc_root = os.path.join(executor_info.root_dir, executor_info.dvc_dir)
try:
return Repo(dvc_root)
except (FileNotFoundError, DvcException) as exc:
raise InvalidExpRevError(name) from exc
raise InvalidExpRevError(name)
-13
View File
@@ -489,19 +489,6 @@ class LocalCeleryQueue(BaseStashQueue):
return celery_remove(self, *args, **kwargs) return celery_remove(self, *args, **kwargs)
def get_running_exps(self, fetch_refs: bool = True) -> Dict[str, Dict]:
"""Get the execution info of the currently running experiments
Args:
fetch_ref (bool): fetch completed checkpoints or not.
"""
result: Dict[str, Dict] = {}
for entry in self.iter_active():
result.update(
fetch_running_exp_from_temp_dir(self, entry.stash_rev, fetch_refs)
)
return result
def get_ref_and_entry_by_names( def get_ref_and_entry_by_names(
self, self,
exp_names: Union[str, List[str]], exp_names: Union[str, List[str]],
-8
View File
@@ -144,14 +144,6 @@ class TempDirQueue(WorkspaceQueue):
) -> Dict[str, str]: ) -> Dict[str, str]:
return BaseStashQueue.collect_executor(exp, executor, exec_result) return BaseStashQueue.collect_executor(exp, executor, exec_result)
def get_running_exps(self, fetch_refs: bool = True) -> Dict[str, Dict]:
result: Dict[str, Dict] = {}
for entry in self.iter_active():
result.update(
fetch_running_exp_from_temp_dir(self, entry.stash_rev, fetch_refs)
)
return result
def collect_active_data( def collect_active_data(
self, self,
baseline_revs: Optional[Collection[str]], baseline_revs: Optional[Collection[str]],
+5 -20
View File
@@ -191,33 +191,18 @@ class WorkspaceQueue(BaseStashQueue):
): ):
raise NotImplementedError raise NotImplementedError
def get_running_exps( def get_running_exp(self) -> Optional[str]:
self, """Return the name of the exp running in workspace (if it exists)."""
fetch_refs: bool = True, # noqa: ARG002
) -> Dict[str, Dict]:
assert self._EXEC_NAME assert self._EXEC_NAME
result: Dict[str, Dict] = {}
if self._active_pid is None: if self._active_pid is None:
return result return None
infofile = self.get_infofile_path(self._EXEC_NAME) infofile = self.get_infofile_path(self._EXEC_NAME)
try: try:
info = ExecutorInfo.from_dict(load_json(infofile)) info = ExecutorInfo.from_dict(load_json(infofile))
except OSError: except OSError:
return result return None
return info.name
if info.status < TaskStatus.FAILED:
# If we are appending to a checkpoint branch in a workspace
# run, show the latest checkpoint as running.
if info.status == TaskStatus.SUCCESS:
return result
last_rev = self.scm.get_ref(EXEC_BRANCH)
if last_rev:
result[last_rev] = info.asdict()
else:
result[self._EXEC_NAME] = info.asdict()
return result
def collect_active_data( def collect_active_data(
self, self,
+5 -3
View File
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
def remove( # noqa: C901, PLR0912 def remove( # noqa: C901, PLR0912
repo: "Repo", repo: "Repo",
exp_names: Union[None, str, List[str]] = None, exp_names: Union[None, str, List[str]] = None,
rev: Optional[str] = None, rev: Optional[Union[List[str], str]] = None,
all_commits: bool = False, all_commits: bool = False,
num: int = 1, num: int = 1,
queue: bool = False, queue: bool = False,
@@ -60,6 +60,8 @@ def remove( # noqa: C901, PLR0912
if remained: if remained:
raise UnresolvedExpNamesError(remained, git_remote=git_remote) raise UnresolvedExpNamesError(remained, git_remote=git_remote)
elif rev: elif rev:
if isinstance(rev, str):
rev = [rev]
exp_ref_dict = _resolve_exp_by_baseline(repo, rev, num, git_remote) exp_ref_dict = _resolve_exp_by_baseline(repo, rev, num, git_remote)
removed.extend(exp_ref_dict.keys()) removed.extend(exp_ref_dict.keys())
exp_ref_list.extend(exp_ref_dict.values()) exp_ref_list.extend(exp_ref_dict.values())
@@ -85,14 +87,14 @@ def remove( # noqa: C901, PLR0912
def _resolve_exp_by_baseline( def _resolve_exp_by_baseline(
repo: "Repo", repo: "Repo",
rev: str, rev: List[str],
num: int, num: int,
git_remote: Optional[str] = None, git_remote: Optional[str] = None,
) -> Dict[str, "ExpRefInfo"]: ) -> Dict[str, "ExpRefInfo"]:
assert isinstance(repo.scm, Git) assert isinstance(repo.scm, Git)
commit_ref_dict: Dict[str, "ExpRefInfo"] = {} commit_ref_dict: Dict[str, "ExpRefInfo"] = {}
rev_dict = iter_revs(repo.scm, [rev], num) rev_dict = iter_revs(repo.scm, rev, num)
rev_set = set(rev_dict.keys()) rev_set = set(rev_dict.keys())
ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote) ref_info_dict = exp_refs_by_baseline(repo.scm, rev_set, git_remote)
for _, ref_info_list in ref_info_dict.items(): for _, ref_info_list in ref_info_dict.items():
File diff suppressed because one or more lines are too long
+16 -9
View File
@@ -121,20 +121,25 @@ class Plots:
} }
""" """
from dvc.repo.experiments.brancher import switch_repo
from dvc.utils.collections import ensure_list from dvc.utils.collections import ensure_list
targets = ensure_list(targets) targets = ensure_list(targets)
targets = [self.repo.dvcfs.from_os_path(target) for target in targets] targets = [self.repo.dvcfs.from_os_path(target) for target in targets]
for rev in self.repo.brancher(revs=revs): if revs is None:
# .brancher() adds unwanted workspace revs = ["workspace"]
if revs is not None and rev not in revs: else:
continue revs = list(revs)
rev = rev or "workspace" if "workspace" in revs:
# reorder revs to match repo.brancher ordering
revs.remove("workspace")
revs = ["workspace"] + revs
for rev in revs:
with switch_repo(self.repo, rev) as (repo, _):
res: Dict = {} res: Dict = {}
definitions = _collect_definitions( definitions = _collect_definitions(
self.repo, repo,
targets=targets, targets=targets,
revision=rev, revision=rev,
onerror=onerror, onerror=onerror,
@@ -146,6 +151,7 @@ class Plots:
data_targets = _get_data_targets(definitions) data_targets = _get_data_targets(definitions)
res[rev]["sources"] = self._collect_data_sources( res[rev]["sources"] = self._collect_data_sources(
repo,
targets=data_targets, targets=data_targets,
recursive=recursive, recursive=recursive,
props=props, props=props,
@@ -156,16 +162,17 @@ class Plots:
@error_handler @error_handler
def _collect_data_sources( def _collect_data_sources(
self, self,
repo: "Repo",
targets: Optional[List[str]] = None, targets: Optional[List[str]] = None,
recursive: bool = False, recursive: bool = False,
props: Optional[Dict] = None, props: Optional[Dict] = None,
onerror: Optional[Callable] = None, onerror: Optional[Callable] = None,
): ):
fs = self.repo.dvcfs fs = repo.dvcfs
props = props or {} props = props or {}
plots = _collect_plots(self.repo, targets, recursive) plots = _collect_plots(repo, targets, recursive)
res: Dict[str, Any] = {} res: Dict[str, Any] = {}
for fs_path, rev_props in plots.items(): for fs_path, rev_props in plots.items():
joined_props = {**rev_props, **props} joined_props = {**rev_props, **props}
+5 -1
View File
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Iterator, List
from dvc.exceptions import DvcException, ReproductionError from dvc.exceptions import DvcException, ReproductionError
from dvc.repo.scm_context import scm_context from dvc.repo.scm_context import scm_context
from dvc.stage.cache import RunCacheNotSupported
from dvc.stage.exceptions import CheckpointKilledError from dvc.stage.exceptions import CheckpointKilledError
from . import locked from . import locked
@@ -125,9 +126,12 @@ def reproduce( # noqa: C901, PLR0912
) )
) )
if kwargs.get("pull", False): if kwargs.get("pull", False) and kwargs.get("run_cache", True):
logger.debug("Pulling run cache") logger.debug("Pulling run cache")
try:
self.stage_cache.pull(None) self.stage_cache.pull(None)
except RunCacheNotSupported as e:
logger.warning("Failed to pull run cache: %s", e)
return _reproduce_stages(self.index.graph, list(stages), **kwargs) return _reproduce_stages(self.index.graph, list(stages), **kwargs)
-1
View File
@@ -122,7 +122,6 @@ class StageLoad:
force=force, force=force,
**stage_data, **stage_data,
) )
with self.repo.scm_context:
stage.dump(update_lock=update_lock) stage.dump(update_lock=update_lock)
try: try:
stage.ignore_outs() stage.ignore_outs()
+2 -2
View File
@@ -67,7 +67,7 @@ dependencies = [
] ]
[project.optional-dependencies] [project.optional-dependencies]
all = ["dvc[azure,gdrive,gs,hdfs,oss,s3,ssh,webdav,webhdfs]"] all = ["dvc[azure,gdrive,gs,hdfs,oss,s3,ssh,webdav,webhdfs]"]
azure = ["dvc-azure==2.21.1"] azure = ["dvc-azure>=2.21.2"]
dev = ["dvc[azure,gdrive,gs,hdfs,lint,oss,s3,ssh,terraform,tests,webdav,webhdfs]"] dev = ["dvc[azure,gdrive,gs,hdfs,lint,oss,s3,ssh,terraform,tests,webdav,webhdfs]"]
gdrive = ["dvc-gdrive==2.19.2"] gdrive = ["dvc-gdrive==2.19.2"]
gs = ["dvc-gs==2.22.0"] gs = ["dvc-gs==2.22.0"]
@@ -303,5 +303,5 @@ ignore-names = ["M", "SCM"]
max-args = 10 max-args = 10
[tool.bandit] [tool.bandit]
exclude_dirs = ["tests", "scripts"] exclude_dirs = ["tests", "scripts", "dvc/repo/experiments/utils.py"]
skips = ["B101"] skips = ["B101"]
@@ -369,6 +369,12 @@ def test_list(tmp_dir, scm, dvc, exp_stage):
baseline_a[:7]: {ref_info_a.name, ref_info_b.name} baseline_a[:7]: {ref_info_a.name, ref_info_b.name}
} }
exp_list = dvc.experiments.ls(rev=[baseline_a, scm.get_rev()])
assert {key: set(val) for key, val in exp_list.items()} == {
baseline_a[:7]: {ref_info_a.name, ref_info_b.name},
"master": {ref_info_c.name},
}
exp_list = dvc.experiments.ls(all_commits=True) exp_list = dvc.experiments.ls(all_commits=True)
assert {key: set(val) for key, val in exp_list.items()} == { assert {key: set(val) for key, val in exp_list.items()} == {
baseline_a[:7]: {ref_info_a.name, ref_info_b.name}, baseline_a[:7]: {ref_info_a.name, ref_info_b.name},
+24
View File
@@ -251,6 +251,30 @@ def test_pull_args(tmp_dir, scm, dvc, git_downstream, exp_stage, all_, rev, resu
assert git_downstream.tmp_dir.scm.get_ref(str(ref_info3)) == result3 assert git_downstream.tmp_dir.scm.get_ref(str(ref_info3)) == result3
def test_pull_multi_rev(tmp_dir, scm, dvc, git_downstream, exp_stage):
baseline = scm.get_rev()
results = dvc.experiments.run(exp_stage.addressing, params=["foo=1"])
exp1 = first(results)
ref_info1 = first(exp_refs_by_rev(scm, exp1))
results = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
exp2 = first(results)
ref_info2 = first(exp_refs_by_rev(scm, exp2))
scm.commit("new_baseline")
results = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
exp3 = first(results)
ref_info3 = first(exp_refs_by_rev(scm, exp3))
downstream_exp = git_downstream.tmp_dir.dvc.experiments
git_downstream.tmp_dir.scm.fetch_refspecs(str(tmp_dir), ["master:master"])
downstream_exp.pull(git_downstream.remote, [], rev=[baseline, scm.get_rev()])
assert git_downstream.tmp_dir.scm.get_ref(str(ref_info1)) == exp1
assert git_downstream.tmp_dir.scm.get_ref(str(ref_info2)) == exp2
assert git_downstream.tmp_dir.scm.get_ref(str(ref_info3)) == exp3
def test_pull_diverged(tmp_dir, scm, dvc, git_downstream, exp_stage): def test_pull_diverged(tmp_dir, scm, dvc, git_downstream, exp_stage):
git_downstream.tmp_dir.scm_gen("foo", "foo", commit="init") git_downstream.tmp_dir.scm_gen("foo", "foo", commit="init")
remote_rev = git_downstream.tmp_dir.scm.get_rev() remote_rev = git_downstream.tmp_dir.scm.get_rev()
+23
View File
@@ -156,3 +156,26 @@ def test_remove_experiments_by_rev(tmp_dir, scm, dvc, exp_stage):
assert "queue2" in queue_revs assert "queue2" in queue_revs
assert scm.get_ref(new_exp_ref) is not None assert scm.get_ref(new_exp_ref) is not None
assert "queue4" in queue_revs assert "queue4" in queue_revs
def test_remove_multi_rev(tmp_dir, scm, dvc, exp_stage):
baseline = scm.get_rev()
results = dvc.experiments.run(exp_stage.addressing, params=["foo=1"])
baseline_exp_ref = first(exp_refs_by_rev(scm, first(results)))
dvc.experiments.run(
exp_stage.addressing, params=["foo=2"], queue=True, name="queue2"
)
scm.commit("new_baseline")
results = dvc.experiments.run(exp_stage.addressing, params=["foo=3"])
new_exp_ref = first(exp_refs_by_rev(scm, first(results)))
assert set(dvc.experiments.remove(rev=[baseline, scm.get_rev()])) == {
baseline_exp_ref.name,
new_exp_ref.name,
}
assert scm.get_ref(str(baseline_exp_ref)) is None
assert scm.get_ref(str(new_exp_ref)) is None
+12 -11
View File
@@ -2,18 +2,19 @@ from funcy import first
def test_generate_random_exp_name(tmp_dir, dvc, scm, exp_stage, mocker): def test_generate_random_exp_name(tmp_dir, dvc, scm, exp_stage, mocker):
mocked_generator = mocker.MagicMock()
mocked_generator.choice.side_effect = [
0,
0,
0,
0,
1,
1,
0,
0,
]
mocker.patch( mocker.patch(
"dvc.repo.experiments.utils.random.choice", "dvc.repo.experiments.utils.random.Random", return_value=mocked_generator
side_effect=[
0,
0,
0,
0,
1,
1,
0,
0,
],
) )
ref = first(dvc.experiments.run(exp_stage.addressing, params=["foo=1"])) ref = first(dvc.experiments.run(exp_stage.addressing, params=["foo=1"]))
+28
View File
@@ -9,6 +9,7 @@ from dvc.cli import main
from dvc.dvcfile import LOCK_FILE, PROJECT_FILE from dvc.dvcfile import LOCK_FILE, PROJECT_FILE
from dvc.exceptions import CyclicGraphError, ReproductionError from dvc.exceptions import CyclicGraphError, ReproductionError
from dvc.stage import PipelineStage from dvc.stage import PipelineStage
from dvc.stage.cache import RunCacheNotSupported
from dvc.stage.exceptions import StageNotFound from dvc.stage.exceptions import StageNotFound
from dvc.utils.fs import remove from dvc.utils.fs import remove
@@ -457,3 +458,30 @@ def test_repro_allow_missing_and_pull(tmp_dir, dvc, mocker, local_remote):
ret = dvc.reproduce(pull=True, allow_missing=True) ret = dvc.reproduce(pull=True, allow_missing=True)
# create-foo is skipped ; copy-foo pulls missing dep # create-foo is skipped ; copy-foo pulls missing dep
assert len(ret) == 1 assert len(ret) == 1
def test_repro_pulls_continue_without_run_cache(tmp_dir, dvc, mocker, local_remote):
(foo,) = tmp_dir.dvc_gen("foo", "foo")
dvc.push()
mocker.patch.object(
dvc.stage_cache, "pull", side_effect=RunCacheNotSupported("foo")
)
dvc.stage.add(name="copy-foo", cmd="cp foo bar", deps=["foo"], outs=["bar"])
remove("foo")
remove(foo.outs[0].cache_path)
assert dvc.reproduce(pull=True)
def test_repro_skip_pull_if_no_run_cache_is_passed(tmp_dir, dvc, mocker, local_remote):
(foo,) = tmp_dir.dvc_gen("foo", "foo")
dvc.push()
spy_pull = mocker.spy(dvc.stage_cache, "pull")
dvc.stage.add(name="copy-foo", cmd="cp foo bar", deps=["foo"], outs=["bar"])
remove("foo")
remove(foo.outs[0].cache_path)
assert dvc.reproduce(pull=True, run_cache=False)
assert not spy_pull.called
+12
View File
@@ -86,3 +86,15 @@ def test_stage_add(mocker, dvc, command, parsed_command):
cmd=parsed_command, cmd=parsed_command,
force=True, force=True,
) )
def test_stage_add_and_run(mocker, dvc):
cli_args = parse_args(["stage", "add", "--run", "-n", "foo", "-o", "foo", "cmd"])
cmd = cli_args.func(cli_args)
add_mock = mocker.patch.object(cmd.repo.stage, "add")
assert cmd.run() == 0
assert called_once_with_subset(add_mock, name="foo", outs=["foo"], cmd="cmd")
add_mock.return_value.run.assert_called_once()
add_mock.return_value.dump.assert_called_once_with(update_pipeline=False)