diff --git a/dvc/stage.py b/dvc/stage.py index b0856c06e..e612e69fd 100644 --- a/dvc/stage.py +++ b/dvc/stage.py @@ -152,6 +152,23 @@ class Stage(object): len(self.deps) == 1 and \ len(self.outs) == 1 + def _changed_deps(self, print_info, log): + if self.is_callback: + msg = "Dvc file '{}' is a 'callback' stage (has a command and " \ + "no dependencies) and thus always considered as changed." + self.project.logger.warn(msg.format(self.relpath)) + return True + + for dep in self.deps: + if not dep.changed(): + continue + if print_info: + msg = "Dependency '{}' of '{}' changed." + log(msg.format(dep, self.relpath)) + return True + + return False + def changed(self, print_info=False): ret = False @@ -160,20 +177,8 @@ class Stage(object): else: log = self.project.logger.debug - if self.is_callback: - msg = "Dvc file '{}' is a 'callback' stage (has a command and " \ - "no dependencies) and thus always considered as changed." - self.project.logger.warn(msg.format(self.relpath)) - ret = True - if not self.locked: - for dep in self.deps: - if not dep.changed(): - continue - if print_info: - msg = "Dependency '{}' of '{}' changed." - log(msg.format(dep, self.relpath)) - ret = True + ret = self._changed_deps(print_info, log) for out in self.outs: if not out.changed(): diff --git a/tests/test_repro.py b/tests/test_repro.py index b26cbf51f..b2c4726a9 100644 --- a/tests/test_repro.py +++ b/tests/test_repro.py @@ -343,6 +343,34 @@ class TestReproLocked(TestReproChangedData): self.assertNotEqual(ret, 0) +class TestReproLockedCallback(TestDvc): + def test(self): + file1 = 'file1' + file1_stage = file1 + '.dvc' + # NOTE: purposefully not specifying dependencies + # to create a callbacs stage. + stage = self.dvc.run(fname=file1_stage, + outs=[file1], + cmd='python {} {} {}'.format(self.CODE, + self.FOO, + file1)) + self.assertTrue(stage is not None) + self.assertEqual(stage.relpath, file1_stage) + + stages = self.dvc.reproduce(file1_stage) + self.assertEqual(len(stages), 1) + + self.dvc.lock_stage(file1_stage) + + stages = self.dvc.reproduce(file1_stage) + self.assertEqual(len(stages), 0) + + self.dvc.lock_stage(file1_stage, unlock=True) + + stages = self.dvc.reproduce(file1_stage) + self.assertEqual(len(stages), 1) + + class TestReproPhony(TestReproChangedData): def test(self): stage = self.dvc.run(deps=[self.file1])