From 71afff6db340cadd758af59baa68f7cf5f6fc8ef Mon Sep 17 00:00:00 2001 From: dberenbaum Date: Thu, 18 Jan 2024 15:27:34 -0500 Subject: [PATCH] exp save: add recursive arg --- dvc/commands/experiments/save.py | 17 +++++++++-------- dvc/repo/experiments/executor/base.py | 9 +++++++-- dvc/repo/experiments/save.py | 2 ++ tests/unit/command/test_experiments.py | 6 +++++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dvc/commands/experiments/save.py b/dvc/commands/experiments/save.py index cc75d1e50..918f1f63d 100644 --- a/dvc/commands/experiments/save.py +++ b/dvc/commands/experiments/save.py @@ -16,6 +16,7 @@ class CmdExperimentsSave(CmdBase): ref = self.repo.experiments.save( targets=self.args.targets, name=self.args.name, + recursive=self.args.recursive, force=self.args.force, include_untracked=self.args.include_untracked, message=self.args.message, @@ -45,15 +46,15 @@ def add_parser(experiments_subparsers, parent_parser): save_parser.add_argument( "targets", nargs="*", - help="""\ -Stages to save. 'dvc.yaml' by default. -The targets can be path to a dvc.yaml file or `.dvc` file, -or a stage name from dvc.yaml file from -current working directory. To save a stage from dvc.yaml -from other directories, the target must be a path followed by colon `:` -and then the stage name name. -""", + help=("Limit DVC caching to these .dvc files and stage names."), ).complete = completion.DVCFILES_AND_STAGE + save_parser.add_argument( + "-R", + "--recursive", + action="store_true", + default=False, + help="Cache subdirectories of the specified directory.", + ) save_parser.add_argument( "-f", "--force", diff --git a/dvc/repo/experiments/executor/base.py b/dvc/repo/experiments/executor/base.py index 3a07f7fd2..9d2a1299c 100644 --- a/dvc/repo/experiments/executor/base.py +++ b/dvc/repo/experiments/executor/base.py @@ -269,6 +269,7 @@ class BaseExecutor(ABC): cls, info: "ExecutorInfo", targets: Optional[Iterable[str]] = None, + recursive: bool = False, force: bool = False, include_untracked: Optional[List[str]] = None, message: Optional[str] = None, @@ -297,9 +298,13 @@ class BaseExecutor(ABC): stages = [] if targets: for target in targets: - stages.append(dvc.commit(target, force=True, relink=False)) + stages.append( + dvc.commit( + target, recursive=recursive, force=True, relink=False + ) + ) else: - stages = dvc.commit([], force=True, relink=False) + stages = dvc.commit([], recursive=recursive, force=True, relink=False) exp_hash = cls.hash_exp(stages) if include_untracked: dvc.scm.add(include_untracked, force=True) # type: ignore[call-arg] diff --git a/dvc/repo/experiments/save.py b/dvc/repo/experiments/save.py index ed067d134..1ae4e1594 100644 --- a/dvc/repo/experiments/save.py +++ b/dvc/repo/experiments/save.py @@ -16,6 +16,7 @@ def save( repo: "Repo", targets: Optional[Iterable[str]] = None, name: Optional[str] = None, + recursive: bool = False, force: bool = False, include_untracked: Optional[List[str]] = None, message: Optional[str] = None, @@ -34,6 +35,7 @@ def save( save_result = executor.save( executor.info, targets=targets, + recursive=recursive, force=force, include_untracked=include_untracked, message=message, diff --git a/tests/unit/command/test_experiments.py b/tests/unit/command/test_experiments.py index 0dbb00a7c..13d2d055c 100644 --- a/tests/unit/command/test_experiments.py +++ b/tests/unit/command/test_experiments.py @@ -475,7 +475,9 @@ def test_experiments_rename_invalid(dvc, scm, mocker, capsys, caplog): def test_experiments_save(dvc, scm, mocker): - cli_args = parse_args(["exp", "save", "target", "--name", "exp-name", "--force"]) + cli_args = parse_args( + ["exp", "save", "target", "--name", "exp-name", "--recursive", "--force"] + ) assert cli_args.func == CmdExperimentsSave cmd = cli_args.func(cli_args) @@ -487,6 +489,7 @@ def test_experiments_save(dvc, scm, mocker): cmd.repo, targets=["target"], name="exp-name", + recursive=True, force=True, include_untracked=[], message=None, @@ -507,6 +510,7 @@ def test_experiments_save_message(dvc, scm, mocker, flag): cmd.repo, targets=[], name=None, + recursive=False, force=False, include_untracked=[], message="custom commit message",