From 9c7b1428832a013040b63b903b7e8a886cb93ad8 Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Fri, 5 Jan 2018 11:38:57 +0200 Subject: [PATCH 1/2] test: add test for phony targets Fixes #342 Signed-off-by: Ruslan Kuprieiev --- tests/test_repro.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_repro.py b/tests/test_repro.py index df7b025bc..e7b543a35 100644 --- a/tests/test_repro.py +++ b/tests/test_repro.py @@ -59,3 +59,14 @@ class TestReproChangedDeepData(TestReproChangedData): self.assertTrue(filecmp.cmp(self.file1, self.BAR)) self.assertTrue(filecmp.cmp(file2, self.BAR)) + + +class TestReproPhony(TestReproChangedData): + def test(self): + stage = self.dvc.run(deps=[self.file1]) + + self.swap_foo_with_bar() + + self.dvc.reproduce([stage.path]) + + self.assertTrue(filecmp.cmp(self.file1, self.BAR)) From bc74afb4cf743e6a04899f50f4836991d9039042 Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Fri, 5 Jan 2018 11:55:16 +0200 Subject: [PATCH 2/2] dvc: accept only single target through project.reproduce Accepting multiple targets doesn't really make sence in both usage and return values. Signed-off-by: Ruslan Kuprieiev --- dvc/command/repro.py | 14 +++++++++++--- dvc/project.py | 29 +++++++++++++++-------------- tests/test_repro.py | 8 ++++---- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/dvc/command/repro.py b/dvc/command/repro.py index c3b6a5e31..c8bb524a9 100644 --- a/dvc/command/repro.py +++ b/dvc/command/repro.py @@ -1,8 +1,16 @@ +from dvc.exceptions import DvcException from dvc.command.common.base import CmdBase class CmdRepro(CmdBase): def run(self): recursive = not self.args.single_item - self.project.reproduce(self.args.targets, - recursive=recursive, - force=self.args.force) + for target in self.args.targets: + try: + self.project.reproduce(target, + recursive=recursive, + force=self.args.force) + except DvcException as ex: + msg = 'Failed to reproduce {}: {}'.format(target, str(ex)) + self.project.logger.error(msg) + return 1 + return 0 diff --git a/dvc/project.py b/dvc/project.py index 62f620f25..e77541af2 100644 --- a/dvc/project.py +++ b/dvc/project.py @@ -22,7 +22,9 @@ class StageNotInPipelineError(PipelineError): class StageNotFoundError(DvcException): - pass + def __init__(self, path): + msg = 'Stage file {} does not exist'.format(path) + super(StageNotFoundError, self).__init__(msg) class Pipeline(object): @@ -161,23 +163,22 @@ class Project(object): stage.dump() return stage - def reproduce(self, targets, recursive=True, force=False): + def reproduce(self, target, recursive=True, force=False): reproduced = [] stages = nx.get_node_attributes(self.graph(), 'stage') - for target in targets: - node = os.path.relpath(os.path.abspath(target), self.root_dir) - if node not in stages: - raise StageNotFoundError(target) + node = os.path.relpath(os.path.abspath(target), self.root_dir) + if node not in stages: + raise StageNotFoundError(target) - if recursive: - for n in nx.dfs_postorder_nodes(self.graph(), node): - stages[n].reproduce(force=force) - stages[n].dump() - reproduced.append(stages[n]) + if recursive: + for n in nx.dfs_postorder_nodes(self.graph(), node): + stages[n].reproduce(force=force) + stages[n].dump() + reproduced.append(stages[n]) - stages[node].reproduce(force=force) - stages[node].dump() - reproduced.append(stages[node]) + stages[node].reproduce(force=force) + stages[node].dump() + reproduced.append(stages[node]) return reproduced diff --git a/tests/test_repro.py b/tests/test_repro.py index e7b543a35..43dbd3fe0 100644 --- a/tests/test_repro.py +++ b/tests/test_repro.py @@ -25,7 +25,7 @@ class TestReproChangedCode(TestRepro): with open(self.CODE, 'a') as code: code.write("\nshutil.copyfile('{}', sys.argv[2])\n".format(self.BAR)) - self.dvc.reproduce([self.file1_stage]) + self.dvc.reproduce(self.file1_stage) self.assertTrue(filecmp.cmp(self.file1, self.BAR)) @@ -34,7 +34,7 @@ class TestReproChangedData(TestRepro): def test(self): self.swap_foo_with_bar() - self.dvc.reproduce([self.file1_stage]) + self.dvc.reproduce(self.file1_stage) self.assertTrue(filecmp.cmp(self.file1, self.BAR)) @@ -55,7 +55,7 @@ class TestReproChangedDeepData(TestReproChangedData): self.swap_foo_with_bar() - self.dvc.reproduce([file2_stage]) + self.dvc.reproduce(file2_stage) self.assertTrue(filecmp.cmp(self.file1, self.BAR)) self.assertTrue(filecmp.cmp(file2, self.BAR)) @@ -67,6 +67,6 @@ class TestReproPhony(TestReproChangedData): self.swap_foo_with_bar() - self.dvc.reproduce([stage.path]) + self.dvc.reproduce(stage.path) self.assertTrue(filecmp.cmp(self.file1, self.BAR))