Skip to content

Commit

Permalink
Normalize databricks paths as part of resolving them (#157)
Browse files Browse the repository at this point in the history
`Path("/a/b/../c").resolve()` returns `Path("/a/c")`
Databricks paths should behave the same, but currently don't.
This PR fixes the issue, which participates in
databrickslabs/ucx#2882

Progresses databrickslabs/ucx#2882

---------

Co-authored-by: Eric Vergnaud <[email protected]>
  • Loading branch information
ericvergnaud and ericvergnaud authored Oct 10, 2024
1 parent dede64d commit ca6571c
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
18 changes: 17 additions & 1 deletion src/databricks/labs/blueprint/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,23 @@ def resolve(self: P, strict: bool = False) -> P:
if strict and not absolute.exists():
msg = f"Path does not exist: {self}"
raise FileNotFoundError(msg)
return absolute
# pylint: disable=protected-access
return absolute._normalize()

def _normalize(self: P) -> P:
if ".." not in self._path_parts:
return self
segments: list[str] = []
for part in self._path_parts:
if part == "..":
if segments:
segments.pop()
elif part is None or part == '.':
continue
else:
segments.append(part)
# pylint: disable=protected-access
return self.with_segments(self.anchor, *segments)._normalize()

def absolute(self: P) -> P:
if self.is_absolute():
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,22 @@ def test_replace_file(ws, make_random, cls):
tmp_dir.rmdir(recursive=True)


@pytest.mark.parametrize("cls", DATABRICKS_PATHLIKE)
def test_resolve_is_consistent(ws, cls):
path = cls(ws, "/a/b/c") / Path("../../d")
resolved = path.resolve()
assert resolved == cls(ws, "/a/d")
path = cls(ws, "/a/b/c") / "../../d"
resolved = path.resolve()
assert resolved == cls(ws, "/a/d")
resolved = cls(ws, "/a/b/c/../../d").resolve()
assert resolved == cls(ws, "/a/d")
resolved = cls(ws, "/../d").resolve()
assert resolved == cls(ws, "/d")
resolved = cls(ws, "/a/b/c/./../../d").resolve()
assert resolved == cls(ws, "/a/d")


def test_workspace_as_fuse(ws):
wsp = WorkspacePath(ws, "/Users/foo/bar/baz")
assert Path("/Workspace/Users/foo/bar/baz") == wsp.as_fuse()
Expand Down

0 comments on commit ca6571c

Please sign in to comment.