Check whether outputs in graphs do not overlap

This commit is contained in:
pared
2019-03-25 14:19:15 +01:00
parent e94bc88b26
commit 2b3107f72c
4 changed files with 61 additions and 11 deletions
+14
View File
@@ -227,3 +227,17 @@ class RecursiveAddingWhileUsingFilename(DvcException):
super(RecursiveAddingWhileUsingFilename, self).__init__(
"using fname with recursive is not allowed."
)
class OverlappingOutputPathsError(DvcException):
def __init__(self, out_1, out_2):
super(OverlappingOutputPathsError, self).__init__(
"Paths for outs:\n'{}'('{}')\n'{}'('{}')\noverlap. To avoid "
"unpredictable behaviour, rerun command with non overlapping outs "
"paths.".format(
str(out_1),
out_1.stage.relpath,
str(out_2),
out_2.stage.relpath,
)
)
+10 -1
View File
@@ -283,6 +283,7 @@ class Repo(object):
from dvc.exceptions import (
OutputDuplicationError,
StagePathAsOutputError,
OverlappingOutputPathsError,
)
G = nx.DiGraph()
@@ -293,7 +294,15 @@ class Repo(object):
for stage in stages:
for out in stage.outs:
existing = [o.stage for o in outs if o.path == out.path]
existing = []
for o in outs:
if o.path == out.path:
existing.append(o.stage)
in_o_dir = out.path.startswith(o.path + o.sep)
in_out_dir = o.path.startswith(out.path + out.sep)
if in_o_dir or in_out_dir:
raise OverlappingOutputPathsError(o, out)
if existing:
stages = [stage.relpath, existing[0].relpath]
+6 -4
View File
@@ -115,9 +115,10 @@ class TestReproWorkingDirectoryAsOutput(TestDvc):
faulty_stage_path = os.path.join("dir2", "something.dvc")
with open(faulty_stage_path, "w") as fd:
output = os.path.join("..", "something")
stage_dump = {
"cmd": "echo something > something",
"outs": [{"path": "something"}],
"cmd": "echo something > {}".format(output),
"outs": [{"path": output}],
}
yaml.safe_dump(stage_dump, fd, default_flow_style=False)
@@ -156,9 +157,10 @@ class TestReproWorkingDirectoryAsOutput(TestDvc):
error_stage_path = os.path.join(nested_dir, "dir", "error.dvc")
with open(error_stage_path, "w") as fd:
output = os.path.join("..", "..", "something")
stage_dump = {
"cmd": "echo something > something",
"outs": [{"path": "something"}],
"cmd": "echo something > {}".format(output),
"outs": [{"path": output}],
}
yaml.safe_dump(stage_dump, fd, default_flow_style=False)
+31 -6
View File
@@ -20,6 +20,7 @@ from dvc.exceptions import (
CyclicGraphError,
ArgumentDuplicationError,
StagePathAsOutputError,
OverlappingOutputPathsError,
)
from tests.basic_env import TestDvc
@@ -198,12 +199,7 @@ class TestRunStageInsideOutput(TestDvc):
self.dvc.run(cmd="", deps=[], outs=[self.DATA_DIR])
with self.assertRaises(StagePathAsOutputError):
self.dvc.run(
cmd="",
cwd=self.DATA_DIR,
outs=[self.FOO],
fname="inside-cwd.dvc",
)
self.dvc.run(cmd="", cwd=self.DATA_DIR, fname="inside-cwd.dvc")
def test_file_name(self):
self.dvc.run(cmd="", deps=[], outs=[self.DATA_DIR])
@@ -820,3 +816,32 @@ class TestRunPersistOutsNoCache(TestRunPersist):
def test(self):
self._test()
class TestShouldRaiseOnOverlappingOutputPaths(TestDvc):
def test(self):
ret = main(["add", self.DATA_DIR])
self.assertEqual(0, ret)
with self.assertRaises(OverlappingOutputPathsError) as err:
self.dvc.run(
outs=[self.DATA], cmd="echo data >> {}".format(self.DATA)
)
error_output = str(err.exception)
data_dir_stage = self.DATA_DIR + Stage.STAGE_FILE_SUFFIX
data_stage = os.path.basename(self.DATA) + Stage.STAGE_FILE_SUFFIX
self.assertIn("Paths for outs:\n", error_output)
self.assertIn(
"\n'{}'('{}')\n".format(self.DATA_DIR, data_dir_stage),
error_output,
)
self.assertIn(
"\n'{}'('{}')\n".format(self.DATA, data_stage), error_output
)
self.assertIn(
"\noverlap. To avoid unpredictable behaviour, rerun "
"command with non overlapping outs paths.",
error_output,
)