diff --git a/dvc/command/run.py b/dvc/command/run.py index 343ff782b..079894061 100644 --- a/dvc/command/run.py +++ b/dvc/command/run.py @@ -33,36 +33,17 @@ class CmdRun(CmdBase): ) return 1 + kwargs = vars(self.args) + kwargs.update( + { + "cmd": parse_cmd(self.args.cmd), + "fname": kwargs.pop("file"), + "no_exec": (self.args.no_exec or bool(self.args.checkpoints)), + "run_cache": not kwargs.pop("no_run_cache"), + } + ) try: - self.repo.run( - cmd=parse_cmd(self.args.cmd), - outs=self.args.outs, - outs_no_cache=self.args.outs_no_cache, - metrics=self.args.metrics, - metrics_no_cache=self.args.metrics_no_cache, - plots=self.args.plots, - plots_no_cache=self.args.plots_no_cache, - live=self.args.live, - live_no_cache=self.args.live_no_cache, - live_no_summary=self.args.live_no_summary, - live_no_html=self.args.live_no_html, - deps=self.args.deps, - params=self.args.params, - fname=self.args.file, - wdir=self.args.wdir, - no_exec=(self.args.no_exec or bool(self.args.checkpoints)), - force=self.args.force, - run_cache=not self.args.no_run_cache, - no_commit=self.args.no_commit, - outs_persist=self.args.outs_persist, - outs_persist_no_cache=self.args.outs_persist_no_cache, - checkpoints=self.args.checkpoints, - always_changed=self.args.always_changed, - name=self.args.name, - single_stage=self.args.single_stage, - external=self.args.external, - desc=self.args.desc, - ) + self.repo.run(**kwargs) except DvcException: logger.exception("") return 1 diff --git a/tests/unit/command/test_run.py b/tests/unit/command/test_run.py index 906477c06..0a4d11a12 100644 --- a/tests/unit/command/test_run.py +++ b/tests/unit/command/test_run.py @@ -1,5 +1,6 @@ from dvc.cli import parse_args from dvc.command.run import CmdRun +from tests.utils.asserts import called_once_with_subset def test_run(mocker, dvc): @@ -60,7 +61,8 @@ def test_run(mocker, dvc): assert cmd.run() == 0 - m.assert_called_once_with( + assert called_once_with_subset( + m, deps=["deps"], outs=["outs"], outs_no_cache=["outs-no-cache"], @@ -96,7 +98,8 @@ def test_run_args_from_cli(mocker, dvc): cmd = args.func(args) m = mocker.patch.object(cmd.repo, "run", autospec=True) assert cmd.run() == 0 - m.assert_called_once_with( + assert called_once_with_subset( + m, deps=[], outs=[], outs_no_cache=[], @@ -132,7 +135,8 @@ def test_run_args_with_spaces(mocker, dvc): cmd = args.func(args) m = mocker.patch.object(cmd.repo, "run", autospec=True) assert cmd.run() == 0 - m.assert_called_once_with( + assert called_once_with_subset( + m, deps=[], outs=[], outs_no_cache=[], diff --git a/tests/unit/command/test_stage.py b/tests/unit/command/test_stage.py index 237cd89f7..e3335aa13 100644 --- a/tests/unit/command/test_stage.py +++ b/tests/unit/command/test_stage.py @@ -2,6 +2,7 @@ import pytest from dvc.cli import parse_args from dvc.command.stage import CmdStageAdd +from tests.utils.asserts import called_once_with_subset @pytest.mark.parametrize( @@ -65,36 +66,30 @@ def test_stage_add(mocker, dvc, command, parsed_command): m = mocker.patch.object(cmd.repo.stage, "add") assert cmd.run() == 0 - expected = { - "name": "name", - "deps": ["deps"], - "outs": ["outs"], - "outs_no_cache": ["outs-no-cache"], - "params": [ + assert called_once_with_subset( + m, + name="name", + deps=["deps"], + outs=["outs"], + outs_no_cache=["outs-no-cache"], + params=[ {"file": ["param1", "param2"]}, {"params.yaml": ["param3"]}, ], - "metrics": ["metrics"], - "metrics_no_cache": ["metrics-no-cache"], - "plots": ["plots"], - "plots_no_cache": ["plots-no-cache"], - "live": "live", - "live_no_summary": True, - "live_no_html": True, - "wdir": "wdir", - "outs_persist": ["outs-persist"], - "outs_persist_no_cache": ["outs-persist-no-cache"], - "checkpoints": ["checkpoints"], - "always_changed": True, - "external": True, - "desc": "description", - "cmd": parsed_command, - "force": True, - } - args, kwargs = m.call_args - assert not args - - for key, val in expected.items(): - # expected values should be a subset of what's in the call args list - assert key in kwargs - assert kwargs[key] == val, f"in key '{key}'" + metrics=["metrics"], + metrics_no_cache=["metrics-no-cache"], + plots=["plots"], + plots_no_cache=["plots-no-cache"], + live="live", + live_no_summary=True, + live_no_html=True, + wdir="wdir", + outs_persist=["outs-persist"], + outs_persist_no_cache=["outs-persist-no-cache"], + checkpoints=["checkpoints"], + always_changed=True, + external=True, + desc="description", + cmd=parsed_command, + force=True, + ) diff --git a/tests/utils/asserts.py b/tests/utils/asserts.py new file mode 100644 index 000000000..e5ad5d9da --- /dev/null +++ b/tests/utils/asserts.py @@ -0,0 +1,13 @@ +from typing import Any +from unittest.mock import ANY, Mock + + +def called_once_with_subset(m: Mock, *args: Any, **kwargs: Any) -> bool: + m.assert_called_once() + m_args, m_kwargs = m.call_args + + expected_args = m_args + (ANY,) * (len(m_args) - len(args)) + expected_kwargs = {k: kwargs.get(k, ANY) for k in m_kwargs} + m.assert_called_with(*expected_args, **expected_kwargs) + + return True