This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: pathlib should allow converting to absolute paths without resolving symlinks
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.6
process
Status: closed Resolution: duplicate
Dependencies: Superseder: Add support for Path.absolute()
View: 29688
Assigned To: Nosy List: Socob, eryksun, mu_mind, pitrou, serhiy.storchaka
Priority: normal Keywords:

Created on 2015-09-05 22:05 by mu_mind, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (8)
msg249936 - (view) Author: David Barnett (mu_mind) Date: 2015-09-05 22:05
There doesn't seem to be any helper in pathlib to expand a relative path to an absolute path *without* resolving symlinks.

For example, if I want to convert
  pathlib.Path('some/path')
to
  pathlib.Path('/full/path/to/some/path')
where some/path is a symlink, my best option seems to be
  pathlib.Path(os.path.abspath(str(pathlib.Path('some/path'))))
msg249941 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-09-05 22:24
pathlib.Path.cwd() / pathlib.Path('some/path')
msg249968 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2015-09-06 05:57
There's a public method that's almost what you want:

    >>> print(pathlib.Path.absolute.__doc__)
    Return an absolute version of this path.  This function works
            even if the path doesn't point to anything.

            No normalization is done, i.e. all '.' and '..' will be kept along.
            Use resolve() to get the canonical path to a file.

However, it appears to be only accidentally public. It isn't tested; it isn't mentioned in the docs; and it doesn't do the [expected] path normalization. Currently it's used as a default in resolve() if self._flavour.resolve(self) returns None.
msg250129 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-09-07 20:56
Yes, this wasn't meant to be a public method really. I'm still on the fence as to whether we should expose something like this. What is your use case?
msg250139 - (view) Author: David Barnett (mu_mind) Date: 2015-09-07 23:21
The idiom of
  pathlib.Path.cwd() / pathlib.Path('some/path')
isn't too bad of an approach if it could just be mentioned in the docs. I would intuitively expected something like
  pathlib.Path('some/path').resolve(follow_symlinks=False)

My use case was that I had an equality check like this failing:
  expected_path = pathlib.Path.cwd() / pathlib.Path('some/path')
  pathlib.Path('some/path') == expected_path
I suppose I should file that as a separate bug because semantically those paths are equal, but I was trying to work around it like
  pathlib.Path('some/path').resolve() == expected_path
which in my case still failed because some/path was a symlink.

Even if that's fixed, I would still expect to run into cases where I wanted to print specifically the relative or absolute path in informational messages and would not want to follow symlinks (e.g., in "Requested path X not found", the user would recognize the absolute path as the one they entered but not necessarily the symlink-resolved version).
msg250167 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-09-08 07:39
Le 08/09/2015 01:21, David Barnett a écrit :
> 
> My use case was that I had an equality check like this failing:
>   expected_path = pathlib.Path.cwd() / pathlib.Path('some/path')
>   pathlib.Path('some/path') == expected_path
> I suppose I should file that as a separate bug because semantically those paths are equal, but I was trying to work around it like
>   pathlib.Path('some/path').resolve() == expected_path
> which in my case still failed because some/path was a symlink.

Semantically, the paths are equal only in that particular system
configuration, with os.getcwd() pointing to a particular directory. Path
comparison is done on "pure" path objects (as explained in the
documentation) and therefore doesn't take into account system specifics.
This makes comparisons much more predictable.

Currently the way to implement "absolute" comparison is to call
.resolve() on both paths. Note you might care about actual file identity
and call os.path.samefile() instead.

> Even if that's fixed, I would still expect to run into cases where I
wanted to print specifically the relative or absolute path in
informational messages and would not want to follow symlinks (e.g., in
"Requested path X not found", the user would recognize the absolute path
as the one they entered but not necessarily the symlink-resolved version).

Yes... and yet that may be misleading, since the "absolute" path
obtained may not correspond to the actual one, especially if ".."
fragments are collapsed.

I'll have to think about this one a bit more.
msg250208 - (view) Author: David Barnett (mu_mind) Date: 2015-09-08 14:25
Right, and to clarify a bit further why I didn't just use A.resolve() == B.resolve() from the beginning, this is in a unit test where the equality check wasn't in my code. I wanted to assert I received a certain call on my mock, like
  mock_open_method.assert_called_once_with(pathlib.Path('foo'))
so it's much more convenient to be able to create a path object that matches with my target path instead of more complicated matching.
msg250209 - (view) Author: David Barnett (mu_mind) Date: 2015-09-08 14:28
And the symlinks for my paths refer to really cryptic hashes in a virtual filesystem for the tests, so rendering them into the assertion failed errors would really make the failure messages hard to interpret.
History
Date User Action Args
2022-04-11 14:58:20adminsetgithub: 69200
2021-03-17 07:10:19eryksunsetstatus: open -> closed
superseder: Add support for Path.absolute()
resolution: duplicate
stage: resolved
2019-08-26 20:24:20Socobsetnosy: + Socob
2015-09-08 14:28:02mu_mindsetmessages: + msg250209
2015-09-08 14:25:01mu_mindsetmessages: + msg250208
2015-09-08 07:39:52pitrousetmessages: + msg250167
2015-09-07 23:21:29mu_mindsetmessages: + msg250139
2015-09-07 20:56:18pitrousettype: behavior -> enhancement
messages: + msg250129
versions: + Python 3.6, - Python 3.4
2015-09-06 05:57:05eryksunsetnosy: + eryksun

messages: + msg249968
versions: + Python 3.4, - Python 2.7
2015-09-05 22:24:26serhiy.storchakasetnosy: + serhiy.storchaka, pitrou
messages: + msg249941
2015-09-05 22:05:48mu_mindcreate