From 0e38525d8df405ff86f689eb70a2b5bcf9945e91 Mon Sep 17 00:00:00 2001 From: skshetry <18718008+skshetry@users.noreply.github.com> Date: Thu, 25 May 2023 16:27:14 +0545 Subject: [PATCH] cli: remove dvc run (#9508) Note that `Repo.run()` API still exists, which will be removed in successive PRs. A lot of tests depend on it that it'll take time before removing it. --- dvc/cli/parser.py | 2 - dvc/commands/run.py | 95 -------- tests/func/test_add.py | 13 +- tests/func/test_cli.py | 49 ---- tests/func/test_data_cloud.py | 2 - tests/func/test_repro.py | 78 +++--- tests/func/test_repro_multistage.py | 100 +++++--- tests/func/test_run_single_stage.py | 354 +--------------------------- tests/unit/command/test_run.py | 144 ----------- 9 files changed, 116 insertions(+), 721 deletions(-) delete mode 100644 dvc/commands/run.py delete mode 100644 tests/unit/command/test_run.py diff --git a/dvc/cli/parser.py b/dvc/cli/parser.py index 06981133f..fb6b7fe9d 100644 --- a/dvc/cli/parser.py +++ b/dvc/cli/parser.py @@ -41,7 +41,6 @@ from dvc.commands import ( remove, repro, root, - run, stage, unprotect, update, @@ -62,7 +61,6 @@ COMMANDS = [ remove, move, unprotect, - run, repro, data_sync, gc, diff --git a/dvc/commands/run.py b/dvc/commands/run.py deleted file mode 100644 index a08b91e9e..000000000 --- a/dvc/commands/run.py +++ /dev/null @@ -1,95 +0,0 @@ -import argparse -import logging - -from dvc.cli.command import CmdBase -from dvc.cli.utils import append_doc_link -from dvc.commands.stage import parse_cmd -from dvc.exceptions import DvcException - -logger = logging.getLogger(__name__) - - -class CmdRun(CmdBase): - def run(self): - if not any( - [ - self.args.deps, - self.args.outs, - self.args.outs_no_cache, - self.args.metrics, - self.args.metrics_no_cache, - self.args.plots, - self.args.plots_no_cache, - self.args.outs_persist, - self.args.outs_persist_no_cache, - self.args.params, - self.args.command, - ] - ): # pragma: no cover - logger.error( - "too few arguments. Specify at least one: `-d`, `-o`, `-O`, " - "`-m`, `-M`, `-p`, `--plots`, `--plots-no-cache`, " - "`--outs-persist`, `--outs-persist-no-cache`, `command`." - ) - return 1 - - kwargs = vars(self.args) - kwargs.update( - { - "cmd": parse_cmd(self.args.command), - "fname": kwargs.pop("file"), - "no_exec": self.args.no_exec, - "run_cache": not kwargs.pop("no_run_cache"), - } - ) - try: - self.repo.run(**kwargs) - except DvcException: - logger.exception("") - return 1 - - return 0 - - -def add_parser(subparsers, parent_parser): - from dvc.commands.stage import _add_common_args - - RUN_HELP = "Generate a dvc.yaml file from a command and execute the command." - run_parser = subparsers.add_parser( - "run", - parents=[parent_parser], - description=append_doc_link(RUN_HELP, "run"), - help=RUN_HELP, - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - run_parser.add_argument("-n", "--name", help="Stage name.") - run_parser.add_argument("--file", metavar="", help=argparse.SUPPRESS) - run_parser.add_argument( - "--single-stage", - action="store_true", - default=False, - help=argparse.SUPPRESS, - ) - _add_common_args(run_parser) - run_parser.add_argument( - "--no-exec", - action="store_true", - default=False, - help="Only create dvc.yaml without actually running it.", - ) - run_parser.add_argument( - "--no-commit", - action="store_true", - default=False, - help="Don't put files/directories into cache.", - ) - run_parser.add_argument( - "--no-run-cache", - action="store_true", - default=False, - help=( - "Execute the command even if this stage has already been run " - "with the same command/dependencies/outputs/etc before." - ), - ) - run_parser.set_defaults(func=CmdRun) diff --git a/tests/func/test_add.py b/tests/func/test_add.py index ac9a62f94..c72ec8bde 100644 --- a/tests/func/test_add.py +++ b/tests/func/test_add.py @@ -362,10 +362,6 @@ def test_should_update_state_entry_for_file_after_add(mocker, dvc, tmp_dir): assert ret == 0 assert file_md5_counter.mock.call_count == 1 - ret = main(["run", "--single-stage", "-d", "foo", "echo foo"]) - assert ret == 0 - assert file_md5_counter.mock.call_count == 1 - os.rename("foo", "foo.back") ret = main(["checkout"]) assert ret == 0 @@ -392,19 +388,14 @@ def test_should_update_state_entry_for_directory_after_add(mocker, dvc, tmp_dir) assert ret == 0 assert file_md5_counter.mock.call_count == 5 - ls = "dir" if os.name == "nt" else "ls" - ret = main(["run", "--single-stage", "-d", "data", "{} {}".format(ls, "data")]) - assert ret == 0 - assert file_md5_counter.mock.call_count == 7 - os.rename("data", "data.back") ret = main(["checkout"]) assert ret == 0 - assert file_md5_counter.mock.call_count == 7 + assert file_md5_counter.mock.call_count == 5 ret = main(["status"]) assert ret == 0 - assert file_md5_counter.mock.call_count == 9 + assert file_md5_counter.mock.call_count == 6 def test_add_commit(tmp_dir, dvc): diff --git a/tests/func/test_cli.py b/tests/func/test_cli.py index 612e1e91e..9f4cc1ba9 100644 --- a/tests/func/test_cli.py +++ b/tests/func/test_cli.py @@ -11,7 +11,6 @@ from dvc.commands.data_sync import CmdDataPull, CmdDataPush from dvc.commands.init import CmdInit from dvc.commands.remove import CmdRemove from dvc.commands.repro import CmdRepro -from dvc.commands.run import CmdRun from dvc.commands.status import CmdDataStatus from dvc.exceptions import NotDvcRepoError @@ -21,54 +20,6 @@ def test_argparse(dvc): assert isinstance(args.func(args), CmdInit) -def test_run(dvc): - dep1 = "dep1" - dep2 = "dep2" - - out1 = "out1" - out2 = "out2" - - out_no_cache1 = "out_no_cache1" - out_no_cache2 = "out_no_cache2" - - fname = "dvc.dvc" - cmd = "cmd" - arg1 = "arg1" - arg2 = "arg2" - - args = parse_args( - [ - "run", - "-d", - dep1, - "--deps", - dep2, - "-o", - out1, - "--outs", - out2, - "-O", - out_no_cache1, - "--outs-no-cache", - out_no_cache2, - "--file", - fname, - cmd, - arg1, - arg2, - ] - ) - cmd_cls = args.func(args) - assert isinstance(cmd_cls, CmdRun) - assert args.deps == [dep1, dep2] - assert args.outs == [out1, out2] - assert args.outs_no_cache == [out_no_cache1, out_no_cache2] - assert args.file == fname - assert args.command == [cmd, arg1, arg2] - - cmd_cls.repo.close() - - def test_pull(dvc): args = parse_args(["pull"]) cmd = args.func(args) diff --git a/tests/func/test_data_cloud.py b/tests/func/test_data_cloud.py index 1204a9425..88db91d1e 100644 --- a/tests/func/test_data_cloud.py +++ b/tests/func/test_data_cloud.py @@ -147,8 +147,6 @@ def test_hash_recalculation(mocker, dvc, tmp_dir, local_remote): assert ret == 0 ret = main(["push"]) assert ret == 0 - ret = main(["run", "--single-stage", "-d", "foo", "echo foo"]) - assert ret == 0 assert test_file_md5.mock.call_count == 1 diff --git a/tests/func/test_repro.py b/tests/func/test_repro.py index d157124ac..f27836bea 100644 --- a/tests/func/test_repro.py +++ b/tests/func/test_repro.py @@ -335,9 +335,10 @@ def test_repro_dry_no_exec(tmp_dir, dvc): ret = main( [ - "run", - "--no-exec", - "--single-stage", + "stage", + "add", + "-n", + f"copy-{idir}-{odir}", "-d", idir, "-o", @@ -351,18 +352,17 @@ def test_repro_dry_no_exec(tmp_dir, dvc): ret = main( [ - "run", - "--no-exec", - "--single-stage", - "--file", - DVC_FILE, + "stage", + "add", + "-n", + "ls", *deps, "ls {}".format(" ".join(dep for i, dep in enumerate(deps) if i % 2)), ] ) assert ret == 0 - ret = main(["repro", "--dry", DVC_FILE]) + ret = main(["repro", "--dry", "ls"]) assert ret == 0 @@ -1114,14 +1114,27 @@ def test_downstream(dvc): # \ / # A # - assert main(["run", "--single-stage", "-o", "A", "echo A>A"]) == 0 - assert main(["run", "--single-stage", "-d", "A", "-o", "B", "echo B>B"]) == 0 - assert main(["run", "--single-stage", "-d", "A", "-o", "C", "echo C>C"]) == 0 + assert main(["stage", "add", "-n", "stage-A", "--run", "-o", "A", "echo A>A"]) == 0 + assert ( + main( + ["stage", "add", "-n", "stage-B", "--run", "-d", "A", "-o", "B", "echo B>B"] + ) + == 0 + ) + assert ( + main( + ["stage", "add", "-n", "stage-C", "--run", "-d", "A", "-o", "C", "echo C>C"] + ) + == 0 + ) assert ( main( [ - "run", - "--single-stage", + "stage", + "add", + "-n", + "stage-D", + "--run", "-d", "B", "-d", @@ -1133,13 +1146,21 @@ def test_downstream(dvc): ) == 0 ) - assert main(["run", "--single-stage", "-o", "G", "echo G>G"]) == 0 - assert main(["run", "--single-stage", "-d", "G", "-o", "F", "echo F>F"]) == 0 + assert main(["stage", "add", "-n", "stage-G", "--run", "-o", "G", "echo G>G"]) == 0 + assert ( + main( + ["stage", "add", "-n", "stage-F", "--run", "-d", "G", "-o", "F", "echo F>F"] + ) + == 0 + ) assert ( main( [ - "run", - "--single-stage", + "stage", + "add", + "-n", + "stage-E", + "--run", "-d", "D", "-d", @@ -1160,22 +1181,25 @@ def test_downstream(dvc): # / # B # - evaluation = dvc.reproduce("B.dvc", downstream=True, force=True) + evaluation = dvc.reproduce("stage-B", downstream=True, force=True) assert len(evaluation) == 3 - assert evaluation[0].relpath == "B.dvc" - assert evaluation[1].relpath == "D.dvc" - assert evaluation[2].relpath == "E.dvc" + assert evaluation[0].addressing == "stage-B" + assert evaluation[1].addressing == "stage-D" + assert evaluation[2].addressing == "stage-E" # B, C should be run (in any order) before D # See https://github.com/iterative/dvc/issues/3602 - evaluation = dvc.reproduce("A.dvc", downstream=True, force=True) + evaluation = dvc.reproduce("stage-A", downstream=True, force=True) assert len(evaluation) == 5 - assert evaluation[0].relpath == "A.dvc" - assert {evaluation[1].relpath, evaluation[2].relpath} == {"B.dvc", "C.dvc"} - assert evaluation[3].relpath == "D.dvc" - assert evaluation[4].relpath == "E.dvc" + assert evaluation[0].addressing == "stage-A" + assert {evaluation[1].addressing, evaluation[2].addressing} == { + "stage-B", + "stage-C", + } + assert evaluation[3].addressing == "stage-D" + assert evaluation[4].addressing == "stage-E" def test_repro_when_cmd_changes(tmp_dir, dvc, run_copy, mocker): diff --git a/tests/func/test_repro_multistage.py b/tests/func/test_repro_multistage.py index f4709839c..677096a59 100644 --- a/tests/func/test_repro_multistage.py +++ b/tests/func/test_repro_multistage.py @@ -36,7 +36,7 @@ def test_repro_frozen(tmp_dir, dvc, run_copy): assert stages == [data_stage, stage0] -def test_downstream(tmp_dir, dvc): +def test_downstream(M, tmp_dir, dvc): # The dependency graph should look like this: # # E @@ -47,19 +47,60 @@ def test_downstream(tmp_dir, dvc): # \ / # A # - assert main(["run", "-n", "A-gen", "-o", "A", "echo A>A"]) == 0 - assert main(["run", "-n", "B-gen", "-d", "A", "-o", "B", "echo B>B"]) == 0 - assert main(["run", "--single-stage", "-d", "A", "-o", "C", "echo C>C"]) == 0 + assert main(["stage", "add", "--run", "-n", "A-gen", "-o", "A", "echo A>A"]) == 0 assert ( - main(["run", "-n", "D-gen", "-d", "B", "-d", "C", "-o", "D", "echo D>D"]) == 0 + main(["stage", "add", "--run", "-n", "B-gen", "-d", "A", "-o", "B", "echo B>B"]) + == 0 ) - assert main(["run", "--single-stage", "-o", "G", "echo G>G"]) == 0 - assert main(["run", "-n", "F-gen", "-d", "G", "-o", "F", "echo F>F"]) == 0 assert ( main( [ - "run", - "--single-stage", + "stage", + "add", + "--run", + "-n", + "C-gen", + "-d", + "A", + "-o", + "C", + "echo C>C", + ] + ) + == 0 + ) + assert ( + main( + [ + "stage", + "add", + "--run", + "-n", + "D-gen", + "-d", + "B", + "-d", + "C", + "-o", + "D", + "echo D>D", + ] + ) + == 0 + ) + assert main(["stage", "add", "--run", "-n", "G-gen", "-o", "G", "echo G>G"]) == 0 + assert ( + main(["stage", "add", "--run", "-n", "F-gen", "-d", "G", "-o", "F", "echo F>F"]) + == 0 + ) + assert ( + main( + [ + "stage", + "add", + "--run", + "-n", + "E-gen", "-d", "D", "-d", @@ -83,41 +124,24 @@ def test_downstream(tmp_dir, dvc): evaluation = dvc.reproduce(PROJECT_FILE + ":B-gen", downstream=True, force=True) assert len(evaluation) == 3 - assert isinstance(evaluation[0], PipelineStage) - assert evaluation[0].relpath == PROJECT_FILE - assert evaluation[0].name == "B-gen" - - assert isinstance(evaluation[1], PipelineStage) - assert evaluation[1].relpath == PROJECT_FILE - assert evaluation[1].name == "D-gen" - - assert not isinstance(evaluation[2], PipelineStage) - assert evaluation[2].relpath == "E.dvc" + assert all(isinstance(stage, PipelineStage) for stage in evaluation) + assert all(stage.relpath == PROJECT_FILE for stage in evaluation) + assert [stage.name for stage in evaluation] == ["B-gen", "D-gen", "E-gen"] # B, C should be run (in any order) before D # See https://github.com/iterative/dvc/issues/3602 evaluation = dvc.reproduce(PROJECT_FILE + ":A-gen", downstream=True, force=True) assert len(evaluation) == 5 - assert isinstance(evaluation[0], PipelineStage) - assert evaluation[0].relpath == PROJECT_FILE - assert evaluation[0].name == "A-gen" - - names = set() - for stage in evaluation[1:3]: - if isinstance(stage, PipelineStage): - assert stage.relpath == PROJECT_FILE - names.add(stage.name) - else: - names.add(stage.relpath) - assert names == {"B-gen", "C.dvc"} - - assert isinstance(evaluation[3], PipelineStage) - assert evaluation[3].relpath == PROJECT_FILE - assert evaluation[3].name == "D-gen" - - assert not isinstance(evaluation[4], PipelineStage) - assert evaluation[4].relpath == "E.dvc" + assert all(isinstance(stage, PipelineStage) for stage in evaluation) + assert all(stage.relpath == PROJECT_FILE for stage in evaluation) + assert [stage.name for stage in evaluation] == [ + "A-gen", + M.any_of("B-gen", "C-gen"), + M.any_of("B-gen", "C-gen"), + "D-gen", + "E-gen", + ] def test_repro_when_cmd_changes(tmp_dir, dvc, run_copy, mocker): diff --git a/tests/func/test_run_single_stage.py b/tests/func/test_run_single_stage.py index a75dd7db3..4d9b9e0f8 100644 --- a/tests/func/test_run_single_stage.py +++ b/tests/func/test_run_single_stage.py @@ -18,8 +18,7 @@ from dvc.exceptions import ( OverlappingOutputPathsError, StagePathAsOutputError, ) -from dvc.fs import system -from dvc.output import Output, OutputIsStageFileError +from dvc.output import OutputIsStageFileError from dvc.stage import Stage from dvc.stage.exceptions import ( StageFileAlreadyExistsError, @@ -307,262 +306,6 @@ def test_run_remove_outs(tmp_dir, dvc, append_foo_script): ) -def test_run_unprotect_outs_copy(tmp_dir, dvc, append_foo_script): - ret = main(["config", "cache.type", "copy"]) - assert ret == 0 - - ret = main( - [ - "run", - "-d", - "append_foo.py", - "-o", - "foo", - "--single-stage", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - assert os.access("foo", os.W_OK) - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - ret = main( - [ - "run", - "--force", - "--no-run-cache", - "--single-stage", - "-d", - "append_foo.py", - "-o", - "foo", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - assert os.access("foo", os.W_OK) - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - -def test_run_unprotect_outs_symlink(tmp_dir, dvc, append_foo_script): - ret = main(["config", "cache.type", "symlink"]) - assert ret == 0 - - assert ret == 0 - ret = main( - [ - "run", - "-d", - "append_foo.py", - "-o", - "foo", - "--single-stage", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - - if os.name == "nt": - # NOTE: Windows symlink perms don't propagate to the target - assert os.access("foo", os.W_OK) - else: - assert not os.access("foo", os.W_OK) - - assert system.is_symlink("foo") - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - ret = main( - [ - "run", - "--force", - "--no-run-cache", - "--single-stage", - "-d", - "append_foo.py", - "-o", - "foo", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - - if os.name == "nt": - # NOTE: Windows symlink perms don't propagate to the target - assert os.access("foo", os.W_OK) - else: - assert not os.access("foo", os.W_OK) - - assert system.is_symlink("foo") - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - -def test_run_unprotect_outs_hardlink(tmp_dir, dvc, append_foo_script): - ret = main(["config", "cache.type", "hardlink"]) - assert ret == 0 - - assert ret == 0 - ret = main( - [ - "run", - "-d", - "append_foo.py", - "-o", - "foo", - "--single-stage", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - assert not os.access("foo", os.W_OK) - assert system.is_hardlink("foo") - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - ret = main( - [ - "run", - "--force", - "--no-run-cache", - "--single-stage", - "-d", - "append_foo.py", - "-o", - "foo", - "python", - "append_foo.py", - "foo", - ] - ) - assert ret == 0 - assert not os.access("foo", os.W_OK) - assert system.is_hardlink("foo") - with open("foo", encoding="utf-8") as fd: - assert fd.read() == "foo" - - -def test_cmd_run_overwrite(tmp_dir, dvc, copy_script): - # NOTE: using sleep() is a workaround for filesystems - # with low mtime resolution. We have to use mtime since - # comparing mtime's is the only way to check that the stage - # file didn't change(size and inode in the first test down - # below don't change). - tmp_dir.gen({"foo": "foo", "bar": "bar"}) - import time - - ret = main( - [ - "run", - "-d", - "foo", - "-d", - "copy.py", - "-o", - "out", - "--file", - "out.dvc", - "--single-stage", - "python", - "copy.py", - "foo", - "out", - ] - ) - assert ret == 0 - - stage_mtime = os.path.getmtime("out.dvc") - - time.sleep(1) - - ret = main( - [ - "run", - "-d", - "foo", - "-d", - "copy.py", - "--force", - "--no-run-cache", - "--single-stage", - "-o", - "out", - "--file", - "out.dvc", - "python", - "copy.py", - "foo", - "out", - ] - ) - assert ret == 0 - - # NOTE: check that dvcfile was overwritten - assert stage_mtime != os.path.getmtime("out.dvc") - stage_mtime = os.path.getmtime("out.dvc") - - time.sleep(1) - - ret = main( - [ - "run", - "--force", - "--single-stage", - "--file", - "out.dvc", - "-d", - "bar", - "cat bar", - ] - ) - assert ret == 0 - - # NOTE: check that dvcfile was overwritten - assert stage_mtime != os.path.getmtime("out.dvc") - - -class TestCmdRunCliMetrics: - def test_cached(self, dvc): - ret = main( - [ - "run", - "-m", - "metrics.txt", - "--single-stage", - "echo test > metrics.txt", - ] - ) - assert ret == 0 - with open("metrics.txt", encoding="utf-8") as fd: - assert fd.read().rstrip() == "test" - - def test_not_cached(self, dvc): - ret = main( - [ - "run", - "-M", - "metrics.txt", - "--single-stage", - "echo test > metrics.txt", - ] - ) - assert ret == 0 - with open("metrics.txt", encoding="utf-8") as fd: - assert fd.read().rstrip() == "test" - - class TestCmdRunWorkingDirectory: def test_default_wdir_is_not_written(self, tmp_dir, dvc): stage = dvc.run( @@ -665,62 +408,6 @@ def test_rerun_changed_out(tmp_dir, run_copy): run_copy("foo", "out", force=False, single_stage=True) -def test_run_commit(dvc): - fname = "test" - ret = main( - [ - "run", - "-o", - fname, - "--no-commit", - "--single-stage", - "echo", - "test", - ">", - fname, - ] - ) - assert ret == 0 - assert os.path.isfile(fname) - assert not os.path.exists(dvc.cache.local.path) - - ret = main(["commit", fname + ".dvc"]) - assert ret == 0 - assert os.path.isfile(fname) - assert len(list(dvc.cache.local.all())) == 1 - - -@pytest.mark.parametrize( - "outs_command", - [ - "--outs-persist", - "--outs-persist-no-cache", - ], -) -def test_run_persist(tmp_dir, dvc, outs_command): - assert ( - main( - [ - "run", - "--single-stage", - "--always-changed", - outs_command, - "file", - "echo content>>file", - ] - ) - == 0 - ) - d = (tmp_dir / "file.dvc").load_yaml() - assert d["outs"][0][Output.PARAM_PERSIST] - - assert main(["repro", "file.dvc"]) == 0 - assert (tmp_dir / "file").read_text() == "content\ncontent\n" - - assert main(["remove", "file.dvc", "--outs"]) == 0 - assert not (tmp_dir / "file").exists() - - def test_should_raise_on_overlapping_output_paths(tmp_dir, dvc, append_foo_script): tmp_dir.gen("data", {"foo": "foo", "bar": "bar"}) ret = main(["add", "data"]) @@ -746,45 +433,6 @@ def test_should_raise_on_overlapping_output_paths(tmp_dir, dvc, append_foo_scrip ) -@pytest.mark.parametrize( - "outs_command, first_expected, second_expected", - [ - ("--outs", "foo\n", "bar\n"), - ("--outs-persist", "foo\n", "foo\nbar\n"), - ], -) -def test_rerun_with_same_outputs( - tmp_dir, dvc, outs_command, first_expected, second_expected -): - assert ( - main( - [ - "run", - "--single-stage", - "--outs", - "foo", - "echo foo>foo", - ] - ) - == 0 - ) - assert (tmp_dir / "foo").read_text() == first_expected - assert ( - main( - [ - "run", - outs_command, - "foo", - "--force", - "--single-stage", - "echo bar>>foo", - ] - ) - == 0 - ) - assert (tmp_dir / "foo").read_text() == second_expected - - def test_should_not_checkout_upon_corrupted_local_hardlink_cache( mocker, tmp_dir, dvc, copy_script ): diff --git a/tests/unit/command/test_run.py b/tests/unit/command/test_run.py deleted file mode 100644 index b9fea4bd2..000000000 --- a/tests/unit/command/test_run.py +++ /dev/null @@ -1,144 +0,0 @@ -from dvc.cli import parse_args -from dvc.commands.run import CmdRun -from tests.utils.asserts import called_once_with_subset - - -def test_run(mocker, dvc): - cli_args = parse_args( - [ - "run", - "--name", - "name", - "--deps", - "deps", - "--outs", - "outs", - "--outs-no-cache", - "outs-no-cache", - "--metrics", - "metrics", - "--metrics-no-cache", - "metrics-no-cache", - "--plots", - "plots", - "--plots-no-cache", - "plots-no-cache", - "--file", - "file", - "--wdir", - "wdir", - "--no-exec", - "--force", - "--no-run-cache", - "--no-commit", - "--outs-persist", - "outs-persist", - "--outs-persist-no-cache", - "outs-persist-no-cache", - "--always-changed", - "--params", - "file:param1,param2", - "--params", - "param3", - "--external", - "--desc", - "description", - "command", - ] - ) - assert cli_args.func == CmdRun - - cmd = cli_args.func(cli_args) - m = mocker.patch.object(cmd.repo, "run", autospec=True) - - assert cmd.run() == 0 - - assert called_once_with_subset( - m, - deps=["deps"], - outs=["outs"], - outs_no_cache=["outs-no-cache"], - metrics=["metrics"], - metrics_no_cache=["metrics-no-cache"], - plots=["plots"], - plots_no_cache=["plots-no-cache"], - outs_persist=["outs-persist"], - outs_persist_no_cache=["outs-persist-no-cache"], - params=["file:param1,param2", "param3"], - fname="file", - wdir="wdir", - no_exec=True, - force=True, - run_cache=False, - no_commit=True, - always_changed=True, - cmd="command", - name="name", - single_stage=False, - external=True, - desc="description", - ) - - -def test_run_args_from_cli(mocker, dvc): - args = parse_args(["run", "echo", "foo"]) - cmd = args.func(args) - m = mocker.patch.object(cmd.repo, "run", autospec=True) - assert cmd.run() == 0 - assert called_once_with_subset( - m, - deps=[], - outs=[], - outs_no_cache=[], - metrics=[], - metrics_no_cache=[], - plots=[], - plots_no_cache=[], - outs_persist=[], - outs_persist_no_cache=[], - params=[], - fname=None, - wdir=None, - no_exec=False, - force=False, - run_cache=True, - no_commit=False, - always_changed=False, - cmd="echo foo", - name=None, - single_stage=False, - external=False, - desc=None, - ) - - -def test_run_args_with_spaces(mocker, dvc): - args = parse_args(["run", "echo", "foo bar"]) - cmd = args.func(args) - m = mocker.patch.object(cmd.repo, "run", autospec=True) - assert cmd.run() == 0 - assert called_once_with_subset( - m, - deps=[], - outs=[], - outs_no_cache=[], - metrics=[], - metrics_no_cache=[], - plots=[], - plots_no_cache=[], - outs_persist=[], - outs_persist_no_cache=[], - params=[], - fname=None, - wdir=None, - no_exec=False, - force=False, - run_cache=True, - no_commit=False, - always_changed=False, - cmd='echo "foo bar"', - name=None, - single_stage=False, - external=False, - desc=None, - )