The Python Oracle

How to get the relative path between two absolute paths in Python using pathlib?

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game 5 Looping

--

Chapters
00:00 How To Get The Relative Path Between Two Absolute Paths In Python Using Pathlib?
00:58 Accepted Answer Score 19
01:51 Answer 2 Score 3
02:42 Thank you

--

Full question
https://stackoverflow.com/questions/5883...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #python3x #pathlib

#avk47



ACCEPTED ANSWER

Score 19


In Python 3.12+, Path.relative_to takes a parameter called walk_up which provides exactly this functionality:

from pathlib import Path

origin = Path('middle-earth/gondor/minas-tirith/castle')
destination = Path('middle-earth/gondor/osgiliath/tower')

assert destination.relative_to(origin, walk_up=True) == Path("../../osgiliath/tower")

According to the official documentation for PurePath.relative_to(other, walk_up=False):

When walk_up is False (the default), the path must start with other. When the argument is True, .. entries may be added to form the relative path. In all other cases, such as the paths referencing different drives, ValueError is raised.

For older Python versions (3.11 and older), os.path.relpath can be used instead:

import os.path
from pathlib import Path

origin      = Path('middle-earth/gondor/minas-tirith/castle').resolve()
destination = Path('middle-earth/gondor/osgiliath/tower').resolve()

assert os.path.relpath(destination, start=origin) == '..\\..\\osgiliath\\tower'

Note that the value returned from os.path.relpath is a string, not a pathlib.Path instance.




ANSWER 2

Score 3


If you'd like your own Python function to convert an absolute path to a relative path:

def absolute_file_path_to_relative(start_file_path, destination_file_path):
    return (start_file_path.count("/") + start_file_path.count("\\") + 1) * (".." + ((start_file_path.find("/") > -1) and "/" or "\\")) + destination_file_path

This assumes that:

1) start_file_path starts with the same root folder as destination_file_path.

2) Types of slashes don't occur interchangably.

3) You're not using a filesystem that permits slashes in the file name.

Those assumptions may be an advantage or disadvantage, depending on your use case.

Disadvantages: if you're using pathlib, you'll break that module's API flow in your code by mixing in this function; limited use cases; inputs have to be sterile for the filesystem you're working with.

Advantages: runs 202x faster than @AdamSmith's answer (tested on Windows 7, 32-bit)