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
pathlib's relative_to should behave like os.path.relpath #84538
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2020-04-22.00:03:03.148>labels= ['type-feature', 'library', '3.11']
title="pathlib's relative_to should behave like os.path.relpath"updated_at=<Date2022-02-22.23:57:02.772>user='https://github.com/domragusa'
Note that the implementation of relpath is pure and thus assumes it's working with existing, resolved paths (i.e. "the filesystem is not accessed to confirm the existence or nature of path or start"). For example:
If "symlink" targets "/spam/eggs/foo", then the resolved path would be "/spam/eggs/some/thing" instead of "/some/thing". Maybe the relative_to method should default to a strict mode that raises ValueError on ambiguous cases that depend on the "existence or nature" of the paths. I think the current implementation is strict.
domragusa commented on Apr 28, 2020
domragusamannequin
MannequinAuthor
More actions
Yeah, you're right, there's no access to the filesystem and the result
is generated assuming the paths are already resolved. strict seems to be an appropriate name for the option, thanks.
I've looked into the test suite, it helped a lot especially with
Windows paths, they were more complicated than I though.
I've duplicated the tests to verify that it still function as before
and I've added some tests for values that would raise an exception. It
works.
I'm not overly fond of the way I check for unrelated paths, but I
couldn't think of something more elegant.
Thank you for your work on this Domenico. For reviewing the code, would you mind creating a Github pull request for it as described here https://devguide.python.org/pullrequest/
domragusa commented on Apr 30, 2020
domragusamannequin
MannequinAuthor
More actions
I may have forgotten to use the proper format for the title of each
commit, should I delete the pull request and make a new one or can it
be fixed when (or if) it's pulled?
I've solved the conflicts with #63810 (bpo-23082: Better error message for PurePath.relative_to() from pathlib) that was merged in the mean time and improved the documentation.
Everything appears to be in order, can you take a look at it?
os.path.relpath() always calls abspath(), which is "impure" by pathlib standards.
If both arguments to relpath() are relative paths then the abspath() calls can probably be removed. It would save a system call, and allow PurePath.relative_to() to call into os.path.relpath() safely:
defrelative_to(self, *other):
ifnotother:
raiseTypeError("need at least one argument")
other=type(self)(*other)
ifself.is_absolute() !=other.is_absolute():
raiseValueError("one path is relative and the other is absolute: ""{!r}, {!r}".format(str(self), str(other)))
rel=self._flavour.relpath(self, other)
returntype(self)(rel)
# For the purpose of this method, drive and root are considered# separate parts, i.e.:# Path('c:/').relative_to('c:') gives Path('/')# Path('c:/').relative_to('/') raise ValueError
Pathlib's behaviour is deliberate but questionable. If we're willing to drop it, then we can call through to os.path.relpath() when users call relative_to(..., strict=False).
Also, path.is_relative_to(other) would simplify down to other == path or other in path.parents, which is so simple it barely qualifies for a (misleadingly named) method. It might make sense to deprecate it.
Thanks Antoine! That makes sense, but it still seems a odd to me that naked Windows drive paths are the only place where absolute and relative paths can be mixed! Extending your logic:
Should PureWindowsPath('c:/').relative_to('c:foo') also return '/'?
Should PurePosixPath('/').relative_to('foo') also return '/'?
Both of these raise ValueError at the moment. Maybe this is a "practicality beats purity" thing?
C: is a relative path, and relpath() transforms it to an absolute path (i.e. prepends the working directory) before working out the result. From an actual windows box and C:\Users\me:
>>>importpathlib>>>pathlib.PureWindowsPath('C:/').relative_to('C:')
PureWindowsPath('/')
>>>pathlib.PureWindowsPath('C:/').relative_to('C:foo')
Traceback (mostrecentcalllast):
File"<stdin>", line1, in<module>File"/Users/barney.gale/code/cpython/Lib/pathlib.py", line673, inrelative_toraiseValueError("{!r} is not in the subpath of {!r}"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ValueError: 'C:\\'isnotinthesubpathof'C:foo'ORonepathisrelativeandtheotherisabsolute.
Activity
domragusa commentedon Apr 22, 2020
Can we improve pathlib.relative_to(other) so that it handles the case of a path not being a direct child of other, like os.path.relpath?
For example:
Path('/some/thing').relative_to('/foo') -> Path('../some/thing')
At the moment it just raises an exception.
pitrou commentedon Apr 22, 2020
The current behaviour is by design. I would not mind adding an option to control it, though.
If you are new to Python development and want to submit a patch or PR, I recommend reading the Developer's Guide:
https://devguide.python.org/
domragusa commentedon Apr 22, 2020
Thanks for your answer. Yeah, I'm new, I'm reading the guide, sorry for any
faux pas :)
Ok, an option would be great as well, a simple True/False switch? Any
suggestion for the name?
I'll get back with a proper patch this time.
On Wed, Apr 22, 2020 at 8:18 PM Antoine Pitrou <report@bugs.python.org>
wrote:
eryksun commentedon Apr 22, 2020
Note that the implementation of relpath is pure and thus assumes it's working with existing, resolved paths (i.e. "the filesystem is not accessed to confirm the existence or nature of path or start"). For example:
>>> os.path.relpath('/some/thing', '/symlink') '../some/thing'If "symlink" targets "/spam/eggs/foo", then the resolved path would be "/spam/eggs/some/thing" instead of "/some/thing". Maybe the relative_to method should default to a
strictmode that raises ValueError on ambiguous cases that depend on the "existence or nature" of the paths. I think the current implementation is strict.domragusa commentedon Apr 28, 2020
Yeah, you're right, there's no access to the filesystem and the result
is generated assuming the paths are already resolved.
strictseems to be an appropriate name for the option, thanks.I've looked into the test suite, it helped a lot especially with
Windows paths, they were more complicated than I though.
I've duplicated the tests to verify that it still function as before
and I've added some tests for values that would raise an exception. It
works.
I'm not overly fond of the way I check for unrelated paths, but I
couldn't think of something more elegant.
Any input is appreciated.
ammaraskar commentedon Apr 29, 2020
Thank you for your work on this Domenico. For reviewing the code, would you mind creating a Github pull request for it as described here https://devguide.python.org/pullrequest/
domragusa commentedon Apr 30, 2020
I may have forgotten to use the proper format for the title of each
commit, should I delete the pull request and make a new one or can it
be fixed when (or if) it's pulled?
On Thu, Apr 30, 2020 at 2:03 AM Roundup Robot <report@bugs.python.org> wrote:
domragusa commentedon May 28, 2020
I've solved the conflicts with #63810 (bpo-23082: Better error message for PurePath.relative_to() from pathlib) that was merged in the mean time and improved the documentation.
Everything appears to be in order, can you take a look at it?
terryjreedy commentedon May 14, 2021
On bpo-44078 (closed as duplicate), Mark Hammond made a similar request.
barneygale commentedon May 17, 2021
Also requested in bpo-42234.
CAM-Gerlach commentedon Dec 10, 2021
For the record, requested on Discourse as well, with a fairly similar proposal.
barneygale commentedon Jun 26, 2022
os.path.relpath()always callsabspath(), which is "impure" by pathlib standards.If both arguments to
relpath()are relative paths then theabspath()calls can probably be removed. It would save a system call, and allowPurePath.relative_to()to call intoos.path.relpath()safely:barneygale commentedon Jul 12, 2022
An interesting comment in
PurePath.relative_to():This isn't true for
ntpath.relpath():Pathlib's behaviour is deliberate but questionable. If we're willing to drop it, then we can call through to
os.path.relpath()when users callrelative_to(..., strict=False).Also,
path.is_relative_to(other)would simplify down toother == path or other in path.parents, which is so simple it barely qualifies for a (misleadingly named) method. It might make sense to deprecate it.pitrou commentedon Jul 12, 2022
"C:" is the current directory in drive C, so I think pathlib's answer is the correct one here (of course, I wrote it so I may be biaised :-)).
barneygale commentedon Jul 12, 2022
Thanks Antoine! That makes sense, but it still seems a odd to me that naked Windows drive paths are the only place where absolute and relative paths can be mixed! Extending your logic:
PureWindowsPath('c:/').relative_to('c:foo')also return'/'?PurePosixPath('/').relative_to('foo')also return'/'?Both of these raise
ValueErrorat the moment. Maybe this is a "practicality beats purity" thing?pitrou commentedon Jul 12, 2022
I would say it is a practically beats purity thing indeed. I don't remember the details of why I chose to allow it at the time.
barneygale commentedon Jul 15, 2022
Correction to my earlier post:
C:is a relative path, andrelpath()transforms it to an absolute path (i.e. prepends the working directory) before working out the result. From an actual windows box andC:\Users\me:Here's
pathlibfor comparison's sake:gh-84538: add strict argument to pathlib.PurePath.relative_to (GH-19813)