pathlib.Path.relative_to doesn't resolve path

Question:

I’m getting a nonsymetric behavior when using Path.relative_to versus os.path.relpath, see examples below. In Correspondence to tools in the os module, I was guided to believe they behave the same.

I’m working with two paths here

  • C:SyncRmaster_head_bin
  • C:Syncinstalled

I’m using Python 3.9.15.

os.path.relpath

>>> import os.path
>>> import pathlib
>>> start = pathlib.Path(r"../../installed")
>>> rel_path = os.path.relpath(pathlib.Path(r"C:/Sync/Rmaster_head_/bin"), start=start)
>>> rel_path
'..\..\..\..\Sync\Rmaster_head_\bin'
>>> start / pathlib.Path(rel_path)
WindowsPath('../../installed/../../../../Sync/Rmaster_head_/bin')
>>> (start / pathlib.Path(rel_path)).resolve()
WindowsPath('C:/Sync/Rmaster_head_/bin')

pathlib.Path.relative_to in both directions

>>> pathlib.Path(r"C:/Sync/Rmaster_head_/bin").relative_to(pathlib.Path(r"../../installed"))
Traceback (most recent call last):
  File "C:SyncinstalledR2023.1.175_installsyspython3x86_64-unknown-winnt_i19v19libcode.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "C:SyncinstalledR2023.1.175_installsyspython3x86_64-unknown-winnt_i19v19libpathlib.py", line 939, in relative_to
    raise ValueError("{!r} is not in the subpath of {!r}"
ValueError: 'C:\Sync\Rmaster_head_\bin' is not in the subpath of '..\..\installed' OR one path is relative and the other is absolute.
>>> pathlib.Path(r"../../installed").relative_to(pathlib.Path(r"C:/Sync/Rmaster_head_/bin"))
Traceback (most recent call last):
  File "C:SyncinstalledR2023.1.175_installsyspython3x86_64-unknown-winnt_i19v19libcode.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "C:SyncinstalledR2023.1.175_installsyspython3x86_64-unknown-winnt_i19v19libpathlib.py", line 939, in relative_to
    raise ValueError("{!r} is not in the subpath of {!r}"
ValueError: '..\..\installed' is not in the subpath of 'C:\Sync\Rmaster_head_\bin' OR one path is relative and the other is absolute.

I’ve noticed that the exception says that one of the paths is relative, but it doesn’t also work when using full paths.

>>> path1 = pathlib.Path(r"C:SyncRmaster_head_bin")
>>> path2 = pathlib.Path(r"C:SyncinstalledR2023.1.175_installdocumentation")
>>> os.path.relpath(path1, start=path2)
'..\..\..\Rmaster_head_\bin'
>>> os.path.relpath(path2, start=path1)
'..\..\installed\R2023.1.175_install\documentation'

and path1.relative_to(path2) and path2.relative_to(path1) both fail.

What am I missing?

Asked By: Daniel

||

Answers:

What you’re missing is the notes about these methods in the pathlib documentation:

Below the documentation of relative_to():

NOTE: This function is part of PurePath and works with strings. It does not check or access the underlying file structure.

Although it would still be possible to derive the relative path you’re looking for based on strings, apparently the method doesn’t do that. The example and the error message are clear about this.

That the function is different than os.path.relpath() is also explicitly mentions at the top of the section you mentioned in your question:

Note: Although os.path.relpath() and PurePath.relative_to() have some overlapping use-cases, their semantics differ enough to warrant not considering them equivalent.

Unfortunately, this (along with some other things) means that pathlib cannot be used to fully replace the os functions. In many cases you’ll have to use a mix of both 🙁

Answered By: wovano
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.