Using pathlib's relative_to for directories on the same level
The first section solves the OP's problem, though if like me, he really wanted the solution relative to a common root then the second section solves it for him. The third section describes how I originally approached it and is kept for interest sake.
Relative Paths
Recently, as in Python 3.4-6, the os.path
module has been extended to accept pathlib.Path
objects. In the following case however it does not return a Path object and one is forced to wrap the result.
foo = Path("C:\\foo")
baz = Path("C:\\baz")
Path(os.path.relpath(foo, baz))
> Path("..\\foo")
Common Path
My suspicion is that you're really looking a path relative to a common root. If that is the case the following, from EOL, is more useful
Path(os.path.commonpath([foo, baz]))
> Path('c:/root')
Common Prefix
Before I'd struck upon os.path.commonpath
I'd used os.path.comonprefix
.
foo = Path("C:\\foo")
baz = Path("C:\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))
> Path('baz')
But be forewarned you are not supposed to use it in this context (See commonprefix : Yes, that old chestnut)
foo = Path("C:\\route66\\foo")
baz = Path("C:\\route44\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))
> ...
> ValueError : `c:\\route44\baz` does not start with `C:\\route`
but rather the following one from J. F. Sebastian.
Path(*os.path.commonprefix([foo.parts, baz.parts]))
> Path('c:/root')
... or if you're feeling verbose ...
from itertools import takewhile
Path(*[set(i).pop() for i in (takewhile(lambda x : x[0]==x[1], zip(foo.parts, baz.parts)))])
This was bugging me, so here's a pathlib-only version that I think does what os.path.relpath
does.
def relpath(path_to, path_from):
path_to = Path(path_to).resolve()
path_from = Path(path_from).resolve()
try:
for p in (*reversed(path_from.parents), path_from):
head, tail = p, path_to.relative_to(p)
except ValueError: # Stop when the paths diverge.
pass
return Path('../' * (len(path_from.parents) - len(head.parents))).joinpath(tail)