diff --git a/dvc/stage/__init__.py b/dvc/stage/__init__.py index a3c0ee4f7d..0388a302aa 100644 --- a/dvc/stage/__init__.py +++ b/dvc/stage/__init__.py @@ -228,7 +228,7 @@ def is_callback(self): A callback stage is always considered as changed, so it runs on every `dvc repro` call. """ - return not self.is_data_source and len(self.deps) == 0 + return self.cmd and not any((self.deps, self.outs)) @property def is_import(self): @@ -265,13 +265,7 @@ def changed_deps(self): if self.frozen: return False - if self.is_callback: - logger.debug( - "%s has a command but no dependencies", self.addressing - ) - return True - - if self.always_changed or self.is_checkpoint: + if self.is_callback or self.always_changed or self.is_checkpoint: return True return self._changed_deps() diff --git a/dvc/stage/run.py b/dvc/stage/run.py index 45cb6caa8b..d747409e17 100644 --- a/dvc/stage/run.py +++ b/dvc/stage/run.py @@ -113,11 +113,7 @@ def _run(stage, executable, cmd, checkpoint_func, **kwargs): def cmd_run(stage, dry=False, checkpoint_func=None): - logger.info( - "Running %s" "stage '%s':", - "callback " if stage.is_callback else "", - stage.addressing, - ) + logger.info("Running stage '%s':", stage.addressing) commands = _enforce_cmd_list(stage.cmd) kwargs = prepare_kwargs(stage, checkpoint_func=checkpoint_func) executable = get_executable() diff --git a/tests/func/test_lockfile.py b/tests/func/test_lockfile.py index c55ebdafbe..c7d9eb6ddd 100644 --- a/tests/func/test_lockfile.py +++ b/tests/func/test_lockfile.py @@ -189,10 +189,7 @@ def v1_repo_lock(tmp_dir, dvc): def test_can_read_v1_lockfile(tmp_dir, dvc, v1_repo_lock): assert dvc.status() == { - "bar": [ - {"changed outs": {"bar.txt": "not in cache"}}, - "always changed", - ], + "bar": [{"changed outs": {"bar.txt": "not in cache"}}], "foo": ["always changed"], } diff --git a/tests/func/test_repro.py b/tests/func/test_repro.py index 5f1a7f2801..ba410f5890 100644 --- a/tests/func/test_repro.py +++ b/tests/func/test_repro.py @@ -266,28 +266,6 @@ def test(self): self.assertEqual(len(stages), 2) -class TestReproNoDeps(TestRepro): - def test(self): - out = "out" - code_file = "out.py" - stage_file = "out.dvc" - code = ( - 'import uuid\nwith open("{}", "w+") as fd:\n' - "\tfd.write(str(uuid.uuid4()))\n".format(out) - ) - with open(code_file, "w+") as fd: - fd.write(code) - stage = self._run( - fname=stage_file, - outs=[out], - cmd=f"python {code_file}", - name="uuid", - ) - - stages = self.dvc.reproduce(self._get_stage_target(stage)) - self.assertEqual(len(stages), 1) - - class TestReproForce(TestRepro): def test(self): stages = self.dvc.reproduce( @@ -572,11 +550,10 @@ class TestReproFrozenCallback(SingleStageRun, TestDvc): def test(self): file1 = "file1" file1_stage = file1 + ".dvc" - # NOTE: purposefully not specifying dependencies + # NOTE: purposefully not specifying deps or outs # to create a callback stage. stage = self._run( fname=file1_stage, - outs=[file1], cmd=f"python {self.CODE} {self.FOO} {file1}", name="copy-FOO-file1", ) @@ -864,6 +841,7 @@ class TestReproAlreadyCached(TestRepro): def test(self): stage = self._run( fname="datetime.dvc", + always_changed=True, deps=[], outs=["datetime.txt"], cmd='python -c "import time; print(time.time())" > datetime.txt', diff --git a/tests/func/test_repro_multistage.py b/tests/func/test_repro_multistage.py index 8c74a364b1..cd5d65c4f8 100644 --- a/tests/func/test_repro_multistage.py +++ b/tests/func/test_repro_multistage.py @@ -56,10 +56,6 @@ class TestReproDepDirWithOutputsUnderItMultiStage( pass -class TestReproNoDepsMultiStage(MultiStageRun, test_repro.TestReproNoDeps): - pass - - class TestReproForceMultiStage(MultiStageRun, test_repro.TestReproForce): pass diff --git a/tests/func/test_run_single_stage.py b/tests/func/test_run_single_stage.py index 3db7b0f3eb..d806ec4c87 100644 --- a/tests/func/test_run_single_stage.py +++ b/tests/func/test_run_single_stage.py @@ -679,11 +679,7 @@ def test_rerun_deterministic_ignore_cache(tmp_dir, run_copy): def test_rerun_callback(dvc): def run_callback(force=False): return dvc.run( - cmd="echo content > out", - outs=["out"], - deps=[], - force=force, - single_stage=True, + cmd="echo content > out", force=force, single_stage=True, ) assert run_callback() is not None @@ -768,6 +764,7 @@ def run_command(self, file, file_content): [ "run", "--single-stage", + "--always-changed", self.outs_command, file, f"echo {file_content} >> {file}", diff --git a/tests/unit/stage/test_run.py b/tests/unit/stage/test_run.py index 7128aab2a1..972d38b20d 100644 --- a/tests/unit/stage/test_run.py +++ b/tests/unit/stage/test_run.py @@ -19,6 +19,6 @@ def test_run_stage_dry(caplog, cmd, expected): run_stage(stage, dry=True) expected.insert( - 0, "Running callback stage 'stage.dvc':", + 0, "Running stage 'stage.dvc':", ) assert caplog.messages == expected