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:
/home/project/cluster-scope/base/namespaces/acm
../../../components/monitoring-rbac
/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.
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.
"""