Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using pathlib's relative_to for directories on the same level

Tags:

python

pathlib

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?

like image 528
JFB Avatar asked Jun 28 '16 18:06

JFB


People also ask

Should I use Pathlib or os 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.

Why is Pathlib better than os path?

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.

How can we create a directory with the Pathlib module?

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 .

Why you should be using Pathlib?

👉 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.


2 Answers

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)))]) 
like image 192
Carel Avatar answered Oct 05 '22 06:10

Carel


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) 
like image 22
Brett Ryland Avatar answered Oct 05 '22 07:10

Brett Ryland