You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Disagreement between PureWindowsPath.relative_to() and ntpath.relpath() with rootless drives #99029
On Windows, the path C: means "the current directory on the C: drive".
But pathlib's relative_to() treats it as the immediate parent of C:\. This makes sense lexographically, but it's inconsistent with everything else:
C:\Users\Barney>python>>>importntpath, pathlib>>>ntpath.relpath('C:/Windows', 'C:/Users/Barney')
'..\\..\\Windows'>>>ntpath.relpath('C:/Windows', '.')
'..\\..\\Windows'>>>ntpath.relpath('C:/Windows', 'C:')
'..\\..\\Windows'>>>pathlib.Path('C:/Windows').relative_to('C:/Users/Barney', walk_up=True)
WindowsPath('../../Windows')
>>>pathlib.Path('C:/Windows').relative_to('.', walk_up=True)
ValueError: Onepathisrelativeandtheotherisabsolute.
>>>pathlib.Path('C:/Windows').relative_to('C:')
WindowsPath('/Windows') # should be ValueError, as we're mixing absolute + relative paths
This prevents us from using relpath() from pathlib, and renders the two implementations incompatible. Booo! Also prevents us from simplifying is_relative_to() down to other == self or other in self.parents
Special cases aren't special enough to break the rules. Let's make these compatible!
Pathlib uses a pure implementation that doesn't rely on global state, such as the current working directory or current operating system (the implementation is in PurePath.relative_to). We don't resolve symlinks or junctions here, so why would we combine it with CWD?
If anything, we should make relative_to raise because C: is a relative path, and it requires an absolute path. There's a note saying that it treats a pathless root like this on purpose, but it's in a comment and not the docstring.
Provided walk_up=True doesn't affect this result, the result is correct. Anywhere on the C drive combined with \Windows will get to the right path - if it gets ..\..\Windows then it's incorrect. But I didn't try this because it's new in 3.12 and so we can change the behaviour if we feel like it.
If anything, we should make relative_to raise because C: is a relative path, and it requires an absolute path. There's a note saying that it treats a pathless root like this on purpose, but it's in a comment and not the docstring.
Indeed. This seems to be the only case where a.relative_path(b) allows a and b to have different anchors (e.g. one relative, one absolute; or different Windows drives); in all other cases, ValueError is raised. My suggestion here is that we make Path('C:/Windows').relative_to('C:') also raise.
I've logged #99199 to cover relpath() calling abspath(). It doesn't cause us any problems for us in pathlib, though (see discussion on #99031). It's "wrong" but has no observable effect, as far as I can tell.
Activity
pythongh-99029: Fix handling of `PureWindowsPath('C:\<blah>').relativ…
brettcannon commentedon Nov 4, 2022
@zooba @eryksun any reason to not standardize on
os.path.relpath()instead of pathlib's custom code?zooba commentedon Nov 7, 2022
Pathlib uses a pure implementation that doesn't rely on global state, such as the current working directory or current operating system (the implementation is in
PurePath.relative_to). We don't resolve symlinks or junctions here, so why would we combine it with CWD?If anything, we should make
relative_toraise becauseC:is a relative path, and it requires an absolute path. There's a note saying that it treats a pathless root like this on purpose, but it's in a comment and not the docstring.Provided
walk_up=Truedoesn't affect this result, the result is correct. Anywhere on the C drive combined with\Windowswill get to the right path - if it gets..\..\Windowsthen it's incorrect. But I didn't try this because it's new in 3.12 and so we can change the behaviour if we feel like it.barneygale commentedon Nov 7, 2022
Indeed. This seems to be the only case where
a.relative_path(b)allows a and b to have different anchors (e.g. one relative, one absolute; or different Windows drives); in all other cases,ValueErroris raised. My suggestion here is that we makePath('C:/Windows').relative_to('C:')also raise.I've logged #99199 to cover
relpath()callingabspath(). It doesn't cause us any problems for us in pathlib, though (see discussion on #99031). It's "wrong" but has no observable effect, as far as I can tell.barneygale commentedon Nov 12, 2022
I found another incompatibility between
PurePath.relative_to()andos.path.relpath()- see #99199 (comment)This bug is still valid, but we can't fix it by using
relpath(). I've revised my PR.gh-99029: Fix handling of `PureWindowsPath('C:\<blah>').relative_to('…