add: prevent crash while getting relpath (#5837)

* add: prevent crash while getting relpath

Since python 3.8 os.path.realpath has resolved windows network shares
to UNC paths. This breaks the logic for relpath as the UNC and mapped
drive paths don't have an obvious common prefix. As it is not easy to go
back to the mapped drive path from UNC path, this is resolved by always
using the UNC paths for calculating relative paths in windows.

Fixes #5832

* relpath: Update changes following review

Two changes:
 - use pytest.mark.skipif rather than if statement inside test to make
   it clearer that test is windows specific
 - Use tail rather than upper_path to denote rest of path after drive
    name taken off (to match python documentation)

Pull request: #5837

* tests, linter: update to add test case and apply linter though pre-commit

Co-authored-by: Tom Rutter <[email protected]>
This commit is contained in:
tomrutter
2021-06-07 11:33:11 +02:00
committed by GitHub
co-authored by Tom Rutter
parent bf5c28b71a
commit 2f8cf8d7d3
2 changed files with 34 additions and 4 deletions
+13 -4
View File
@@ -319,10 +319,19 @@ def relpath(path, start=os.curdir):
start = os.path.abspath(os.fspath(start))
# Windows path on different drive than curdir doesn't have relpath
if os.name == "nt" and not os.path.commonprefix(
[start, os.path.abspath(path)]
):
return path
if os.name == "nt":
# Since python 3.8 os.realpath resolves network shares to their UNC
# path. So, to be certain that relative paths correctly captured,
# we need to resolve to UNC path first. We resolve only the drive
# name so that we don't follow any 'real' symlinks on the path
def resolve_network_drive_windows(path_to_resolve):
drive, tail = os.path.splitdrive(path_to_resolve)
return os.path.join(os.path.realpath(drive), tail)
path = resolve_network_drive_windows(os.path.abspath(path))
start = resolve_network_drive_windows(start)
if not os.path.commonprefix([start, path]):
return path
return os.path.relpath(path, start)
+21
View File
@@ -113,6 +113,27 @@ def test_relpath():
assert relpath(path) == relpath(path_info)
@pytest.mark.skipif(os.name != "nt", reason="Windows specific")
def test_relpath_windows(monkeypatch):
"""test that relpath correctly generated when run on a
windows network share. The drive mapped path is mapped
to a UNC path by os.path.realpath"""
def dummy_realpath(path):
return path.replace("x:", "\\\\server\\share")
monkeypatch.setattr(os.path, "realpath", dummy_realpath)
assert (
relpath("x:\\dir1\\dir2\\file.txt", "\\\\server\\share\\dir1")
== "dir2\\file.txt"
)
assert (
relpath("y:\\dir1\\dir2\\file.txt", "\\\\server\\share\\dir1")
== "y:\\dir1\\dir2\\file.txt"
)
@pytest.mark.parametrize(
"inp,out,is_dir,expected",
[