fix dvc.yaml filepath in error message when reading from git revisions (#10847)

This commit is contained in:
skshetry
2025-08-16 13:44:48 +00:00
committed by GitHub
parent 9b5ee43ce1
commit d142d0d6af
2 changed files with 32 additions and 18 deletions
+24 -14
View File
@@ -20,6 +20,7 @@ from dvc.utils.serialize import (
parse_yaml, parse_yaml,
parse_yaml_for_update, parse_yaml_for_update,
) )
from dvc_objects.fs.local import LocalFileSystem
if TYPE_CHECKING: if TYPE_CHECKING:
from rich.syntax import Syntax from rich.syntax import Syntax
@@ -34,15 +35,24 @@ _T = TypeVar("_T")
merge_conflict_marker = re.compile("^([<=>]{7}) .*$", re.MULTILINE) merge_conflict_marker = re.compile("^([<=>]{7}) .*$", re.MULTILINE)
def make_relpath(path: str) -> str: def make_relpath(fs_path: str, fs: "Optional[FileSystem]" = None) -> str:
import os from os import curdir, pardir, sep
from dvc.utils import relpath from dvc.utils import relpath
rel = relpath(path) if not fs_path:
prefix = "" return fs_path
if not rel.startswith(".."):
prefix = "./" if os.name == "posix" else ".\\" if fs and not isinstance(fs, LocalFileSystem):
rel = fs.relpath(fs_path).replace(fs.sep, sep)
else:
rel = relpath(fs_path)
if rel.startswith(sep):
# if it is an absolute path, return as it is.
# This can happen on Windows when we are on a different drive.
return rel
prefix = curdir + sep if not rel.startswith(pardir) else ""
return prefix + rel return prefix + rel
@@ -77,14 +87,14 @@ class YAMLSyntaxError(PrettyDvcException, YAMLFileCorruptedError):
exc: Exception, exc: Exception,
rev: Optional[str] = None, rev: Optional[str] = None,
) -> None: ) -> None:
self.path: str = path
self.yaml_text: str = yaml_text self.yaml_text: str = yaml_text
self.exc: Exception = exc self.exc: Exception = exc
merge_conflicts = merge_conflict_marker.search(self.yaml_text) merge_conflicts = merge_conflict_marker.search(self.yaml_text)
self.hint = " (possible merge conflicts)" if merge_conflicts else "" self.hint = " (possible merge conflicts)" if merge_conflicts else ""
self.rev: Optional[str] = rev self.rev: Optional[str] = rev
super().__init__(self.path) super().__init__(path)
self.relpath: str = path
def __pretty_exc__(self, **kwargs: Any) -> None: # noqa: C901 def __pretty_exc__(self, **kwargs: Any) -> None: # noqa: C901
from ruamel.yaml.error import MarkedYAMLError from ruamel.yaml.error import MarkedYAMLError
@@ -134,9 +144,8 @@ class YAMLSyntaxError(PrettyDvcException, YAMLFileCorruptedError):
# if there are no other outputs # if there are no other outputs
lines.insert(0, "") lines.insert(0, "")
rel = make_relpath(self.path)
rev_msg = f" in revision '{self.rev[:7]}'" if self.rev else "" rev_msg = f" in revision '{self.rev[:7]}'" if self.rev else ""
msg_fmt = f"'{rel}' is invalid{self.hint}{rev_msg}." msg_fmt = f"'{self.relpath}' is invalid{self.hint}{rev_msg}."
lines.insert(0, _prepare_message(msg_fmt)) lines.insert(0, _prepare_message(msg_fmt))
for line in lines: for line in lines:
ui.error_write(line, styled=True) ui.error_write(line, styled=True)
@@ -198,10 +207,9 @@ class YAMLValidationError(PrettyDvcException):
self.text = text or "" self.text = text or ""
self.exc = exc self.exc = exc
rel = make_relpath(path) if path else ""
self.path = path or "" self.path = path or ""
message = f"'{rel}' validation failed" message = f"'{self.path}' validation failed"
message += f" in revision '{rev[:7]}'" if rev else "" message += f" in revision '{rev[:7]}'" if rev else ""
if len(self.exc.errors) > 1: if len(self.exc.errors) > 1:
message += f": {len(self.exc.errors)} errors" message += f": {len(self.exc.errors)} errors"
@@ -288,10 +296,12 @@ def load(
raise EncodingError(path, encoding) from exc raise EncodingError(path, encoding) from exc
except YAMLFileCorruptedError as exc: except YAMLFileCorruptedError as exc:
cause = exc.__cause__ cause = exc.__cause__
raise YAMLSyntaxError(path, text, exc, rev=rev) from cause relpath = make_relpath(path, fs)
raise YAMLSyntaxError(relpath, text, exc, rev=rev) from cause
if schema: if schema:
relpath = make_relpath(path, fs)
# not returning validated data, as it may remove # not returning validated data, as it may remove
# details from CommentedMap that we get from roundtrip parser # details from CommentedMap that we get from roundtrip parser
validate(data, schema, text=text, path=path, rev=rev) validate(data, schema, text=text, path=relpath, rev=rev)
return data, text return data, text
+8 -4
View File
@@ -1,4 +1,4 @@
import os from os import curdir, pardir, sep
import pytest import pytest
from ruamel.yaml import __with_libyaml__ as ruamel_clib from ruamel.yaml import __with_libyaml__ as ruamel_clib
@@ -372,18 +372,22 @@ def test_on_revision(
assert expected.format(short_rev=scm.get_rev()[:7]) in err assert expected.format(short_rev=scm.get_rev()[:7]) in err
def test_make_relpath(tmp_dir, monkeypatch): def test_make_relpath(tmp_dir, dvc, scm, monkeypatch):
from dvc.utils.strictyaml import make_relpath from dvc.utils.strictyaml import make_relpath
gitfs = scm.get_fs("HEAD")
path = tmp_dir / "dvc.yaml" path = tmp_dir / "dvc.yaml"
expected_path = "./dvc.yaml" if os.name == "posix" else ".\\dvc.yaml" expected_path = curdir + sep + "dvc.yaml"
assert make_relpath(path) == expected_path assert make_relpath(path) == expected_path
assert make_relpath("/dvc.yaml", gitfs) == expected_path
(tmp_dir / "dir").mkdir(exist_ok=True) (tmp_dir / "dir").mkdir(exist_ok=True)
monkeypatch.chdir("dir") monkeypatch.chdir("dir")
gitfs.chdir("/dir")
expected_path = "../dvc.yaml" if os.name == "posix" else "..\\dvc.yaml" expected_path = pardir + sep + "dvc.yaml"
assert make_relpath(path) == expected_path assert make_relpath(path) == expected_path
assert make_relpath("/dvc.yaml", gitfs) == expected_path
def test_fallback_exception_message(tmp_dir, dvc, mocker, caplog): def test_fallback_exception_message(tmp_dir, dvc, mocker, caplog):