The python library pathlib
provides Path.relative_to
. This function works fine if one path is a subpath of the other one, like this:
from pathlib import Path foo = Path("C:\\foo") bar = Path("C:\\foo\\bar") bar.relative_to(foo) > WindowsPath('bar')
However, if two paths are on the same level, relative_to
does not work.
baz = Path("C:\\baz") foo.relative_to(baz) > ValueError: 'C:\\foo' does not start with 'C:\\baz'
I would expect the result to be
WindowsPath("..\\baz")
The function os.path.relpath
does this correctly:
import os foo = "C:\\foo" bar = "C:\\bar" os.path.relpath(foo, bar) > '..\\foo'
Is there a way to achieve the functionality of os.path.relpath
using pathlib.Path
?
Basically you can do it either way, it really doesn't matter much. It probably boils down to what syntax you prefer. Personally I don't like the slash being “abused” as a path concatenation operator, therefore I prefer os.
The Python pathlib module provides an easier method to interact with the filesystem no matter what the operating system is. It allows a more intuitive, more pythonic way to interface with file paths (the name of a file including any of its directories and subdirectories). In the os module, paths are regular strings.
Create Directory in Python Using the Path. mkdir() Method of the pathlib Module. The Path. mkdir() method, in Python 3.5 and above, takes the path as input and creates any missing directories of the path, including the parent directory if the parents flag is True .
👉 It allows you to iterate on directories and perform pattern matching. Let's assume you have a Path object that points to a directory. Pathlib allows you to easily iterate over that directory's content and also get files and folders that match a specific pattern.
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.
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")
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')
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With