Skip to content

Commit e0d0951

Browse files
committed
pathlib: add analogues to py.path.local's bestrelpath and common
An equivalent for these py.path.local functions is needed for some upcoming py.path -> pathlib conversions.
1 parent aa9905d commit e0d0951

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

src/_pytest/pathlib.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,35 @@ def visit(
569569
for entry in entries:
570570
if entry.is_dir(follow_symlinks=False) and recurse(entry):
571571
yield from visit(entry.path, recurse)
572+
573+
574+
def commonpath(path1: Path, path2: Path) -> Optional[Path]:
575+
"""Return the common part shared with the other path, or None if there is
576+
no common part."""
577+
try:
578+
return Path(os.path.commonpath((str(path1), str(path2))))
579+
except ValueError:
580+
return None
581+
582+
583+
def bestrelpath(directory: Path, dest: Path) -> str:
584+
"""Return a string which is a relative path from directory to dest such
585+
that directory/bestrelpath == dest.
586+
587+
If no such path can be determined, returns dest.
588+
"""
589+
if dest == directory:
590+
return os.curdir
591+
# Find the longest common directory.
592+
base = commonpath(directory, dest)
593+
# Can be the case on Windows.
594+
if not base:
595+
return str(dest)
596+
reldirectory = directory.relative_to(base)
597+
reldest = dest.relative_to(base)
598+
return os.path.join(
599+
# Back from directory to base.
600+
*([os.pardir] * len(reldirectory.parts)),
601+
# Forward from base to dest.
602+
*reldest.parts,
603+
)

testing/test_pathlib.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import py
77

88
import pytest
9+
from _pytest.pathlib import bestrelpath
10+
from _pytest.pathlib import commonpath
911
from _pytest.pathlib import ensure_deletable
1012
from _pytest.pathlib import fnmatch_ex
1113
from _pytest.pathlib import get_extended_length_path_str
@@ -381,3 +383,21 @@ def test_suppress_error_removing_lock(tmp_path):
381383
# check now that we can remove the lock file in normal circumstances
382384
assert ensure_deletable(path, consider_lock_dead_if_created_before=mtime + 30)
383385
assert not lock.is_file()
386+
387+
388+
def test_bestrelpath() -> None:
389+
curdir = Path("/foo/bar/baz/path")
390+
assert bestrelpath(curdir, curdir) == "."
391+
assert bestrelpath(curdir, curdir / "hello" / "world") == "hello" + os.sep + "world"
392+
assert bestrelpath(curdir, curdir.parent / "sister") == ".." + os.sep + "sister"
393+
assert bestrelpath(curdir, curdir.parent) == ".."
394+
assert bestrelpath(curdir, Path("hello")) == "hello"
395+
396+
397+
def test_commonpath() -> None:
398+
path = Path("/foo/bar/baz/path")
399+
subpath = path / "sampledir"
400+
assert commonpath(path, subpath) == path
401+
assert commonpath(subpath, path) == path
402+
assert commonpath(Path(str(path) + "suffix"), path) == path.parent
403+
assert commonpath(path, path.parent.parent) == path.parent.parent

0 commit comments

Comments
 (0)