Search code examples
pythonfilesystemspathlib

pathlib.Path.relative_to vs os.path.relpath


I would like to find the relative path between two absolute paths. I have existing code that is generally using pathlib.Path for interacting with the filesystem, but I've run into a problem that seems easy to solve with os.path.relpath but (so far) intractable with pathlib.Path.

I have:

  • (A), An absolute path to a directory, /home/project/cluster-scope/base/namespaces/acm
  • (B), A relative path from (A) to another directory, ../../../components/monitoring-rbac
  • (C), An absolute path to a second directory, /home/project/cluster-scope/base/core/namespaces/acm

I want to compute a new relative path to (B) from (C). This works:

>>> import os
>>> path_A = '/home/project/cluster-scope/base/namespaces/acm'
>>> path_B = '../../../components/monitoring-rbac'
>>> path_C = '/home/project/cluster-scope/base/core/namespaces/acm'
>>> path_B_abs = os.path.abspath(os.path.join(path_A, path_B))
>>> os.path.relpath(path_B_abs, path_C)
'../../../../components/monitoring-rbac'

But this does not:

>>> from pathlib import Path
>>> path_A = Path('/home/project/cluster-scope/base/namespaces/acm')
>>> path_B = Path('../../../components/monitoring-rbac')
>>> path_C = Path('/home/project/cluster-scope/base/core/namespaces/acm')
>>> path_B_abs = (path_A / path_B).resolve()
>>> path_B_abs.relative_to(path_C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.9/pathlib.py", line 928, in relative_to
    raise ValueError("{!r} is not in the subpath of {!r}"
ValueError: '/home/project/cluster-scope/components/monitoring-rbac' is not in the subpath of '/home/project/cluster-scope/base/core/namespaces/acm' OR one path is relative and the other is absolute.

The message in that ValueError exception seems inaccurate, or at least misleading: the two paths obviously share a common parent. Is there any way to compute the new relative path using the pathlib.Path? I realize I can just use os.path.relpath and be done with it, but I'm curious if I'm misunderstanding the use of pathlib's relative_to method.


Solution

  • Apparently this is not possible. According to an issue:

    I agree it's worth improving the error message (and probably the docs too).

    It's never made clear that relative_to only looks deeper (won't ever generate leading ".." parts) - the closest hint in the docs is that os.path.relpath is different (and that isn't even in the relative_to() section).

    In the current version the docstring says

    """Return the relative path to another path identified by the passed
    arguments.  If the operation is not possible (because this is not
    a subpath of the other path), raise ValueError.
    """