Skip to content

Commit

Permalink
update msg for multiple overlapping outputs (#9862)
Browse files Browse the repository at this point in the history
* update msg for multiple overlapping outputs

* update msg for multiple overlapping outputs

* fix type checking

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* use sets of stages to fix mypy errors

* fix tests/func/test_add.py::test_try_adding_multiple_overlaps

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
dberenbaum and pre-commit-ci[bot] authored Aug 23, 2023
1 parent fc955c1 commit 428c094
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 16 deletions.
28 changes: 16 additions & 12 deletions dvc/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Exceptions raised by the dvc."""
import errno
from typing import Dict, List
from typing import TYPE_CHECKING, Dict, List, Set

from dvc.utils import format_link

if TYPE_CHECKING:
from dvc.stage import Stage


class DvcException(Exception):
"""Base class for all dvc exceptions."""
Expand Down Expand Up @@ -31,24 +34,25 @@ class OutputDuplicationError(DvcException):
stages (list): list of paths to stages.
"""

def __init__(self, output, stages):
def __init__(self, output: str, stages: Set["Stage"]):
from funcy import first

assert isinstance(output, str)
assert all(hasattr(stage, "relpath") for stage in stages)
msg = ""
stage_names = [s.addressing for s in stages]
stages_str = " ".join(stage_names)
if len(stages) == 1:
stage_name = first(stages)
msg = f"output '{output}' is already specified in {stage_name}."
stage = first(stages)
msg = (
f"output '{output}' is already specified in {stage}."
f"\nUse `dvc remove {stage.addressing}` to stop tracking the "
"overlapping output."
)
else:
msg = "output '{}' is already specified in stages:\n{}".format(
output, "\n".join(f"\t- {s}" for s in stage_names)
stage_names = "\n".join(["\t- " + s.addressing for s in stages])
msg = (
f"output '{output}' is specified in:\n{stage_names}"
"\nUse `dvc remove` with any of the above targets to stop tracking the "
"overlapping output."
)
msg += (
f"\nUse `dvc remove {stages_str}` to stop tracking the overlapping output."
)
super().__init__(msg)
self.stages = stages
self.output = output
Expand Down
2 changes: 1 addition & 1 deletion dvc/repo/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def translate_graph_error(stages: List["Stage"]) -> Iterator[None]:
)
except OutputDuplicationError as exc:
raise OutputDuplicationError( # noqa: B904
exc.output, list(set(exc.stages) - set(stages))
exc.output, set(exc.stages) - set(stages)
)


Expand Down
2 changes: 1 addition & 1 deletion dvc/repo/trie.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def build_outs_trie(stages):
# Check for dup outs
if out_key in outs:
dup_stages = [stage, outs[out_key].stage]
raise OutputDuplicationError(str(out), dup_stages)
raise OutputDuplicationError(str(out), set(dup_stages))

# Check for overlapping outs
if outs.has_subtrie(out_key):
Expand Down
27 changes: 25 additions & 2 deletions tests/func/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
from dvc.cli import main
from dvc.config import ConfigError
from dvc.dvcfile import DVC_FILE_SUFFIX
from dvc.exceptions import DvcException, OverlappingOutputPathsError
from dvc.exceptions import (
DvcException,
OutputDuplicationError,
OverlappingOutputPathsError,
)
from dvc.fs import LocalFileSystem, system
from dvc.output import (
OutputAlreadyTrackedError,
Expand All @@ -22,7 +26,7 @@
from dvc.stage import Stage
from dvc.stage.exceptions import StageExternalOutputsError, StagePathNotFoundError
from dvc.utils.fs import path_isin
from dvc.utils.serialize import YAMLFileCorruptedError
from dvc.utils.serialize import YAMLFileCorruptedError, dump_yaml
from dvc_data.hashfile.hash import file_md5
from dvc_data.hashfile.hash_info import HashInfo
from tests.utils import get_gitignore_content
Expand Down Expand Up @@ -656,6 +660,25 @@ def test_try_adding_pipeline_tracked_output(tmp_dir, dvc, run_copy):
dvc.add("bar")


def test_try_adding_multiple_overlaps(tmp_dir, dvc):
tmp_dir.dvc_gen("foo", "foo")
dvcyaml_content = {
"stages": {
"echo-foo": {
"cmd": "echo foo > foo",
"outs": ["foo"],
}
}
}
dump_yaml("dvc.yaml", dvcyaml_content)
msg = (
"\nUse `dvc remove` with any of the above targets to stop tracking the "
"overlapping output."
)
with pytest.raises(OutputDuplicationError, match=msg):
dvc.add("foo")


def test_add_pipeline_file(tmp_dir, dvc, run_copy):
from dvc.dvcfile import PROJECT_FILE

Expand Down

0 comments on commit 428c094

Please sign in to comment.