classification
Title: pathlib.Path.resolve() returns path with double slash when resolving a relative path in root directory
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: QbLearningPython, corona10, killerrex, lukasz.langa, mbarkhau, miss-islington, pitrou, serhiy.storchaka, tuxtimo
Priority: normal Keywords: patch

Created on 2018-05-27 10:58 by QbLearningPython, last changed 2020-12-13 00:59 by miss-islington. This issue is now closed.

Files
File name Uploaded Description Edit
plbug.py killerrex, 2018-06-01 18:41 Test script of the pathlib behaviour
Pull Requests
URL Status Linked Edit
PR 7666 closed corona10, 2018-06-12 14:32
PR 21971 closed mbarkhau, 2020-08-26 23:06
PR 21972 merged lukasz.langa, 2020-08-26 23:49
PR 21974 merged miss-islington, 2020-08-27 00:25
PR 21975 merged miss-islington, 2020-08-27 00:25
PR 23750 closed miss-islington, 2020-12-13 00:58
PR 23751 closed miss-islington, 2020-12-13 00:59
Messages (10)
msg317790 - (view) Author: (QbLearningPython) Date: 2018-05-27 10:58
I have recently found a weird behaviour while trying to resolve a relative path located on the root directory on a macOs.

I tried to resolve a Path('spam') and the interpreter answered PosixPath('//spam') —double slash for root— instead of (my) expected PosixPath('/spam').

I think that this is a bug.

I ran the interpreter from root directory (cd /; python). Once running the interpreter, this is what I did:

>>> import pathlib
>>> pathlib.Path.cwd()
PosixPath('/')
# since the interpreter has been launched from root
>>> p = pathlib.Path('spam')
>>> p
PosixPath('spam')
# just for checking
>>> p.resolve()
PosixPath('//spam')
# beware of double slash instead of single slash


I also checked the behaviour of Path.resolve() in a non-root directory (in my case launching the interpreter from /Applications).

>>> import pathlib
>>> pathlib.Path.cwd()
PosixPath('/Applications')
>>> p = pathlib.Path('eggs')
>>> p
PosixPath('eggs')
>>> p.resolve()
PosixPath('/Applications/eggs')
# just one slash as root in this case (as should be)

So it seems that double slashes just appear while resolving relative paths in the root directory.

More examples are:
	
>>> pathlib.Path('spam/egg').resolve()
PosixPath('//spam/egg')
>>> pathlib.Path('./spam').resolve()
PosixPath('//spam')
>>> pathlib.Path('./spam/egg').resolve()
PosixPath('//spam/egg')

but

>>> pathlib.Path('').resolve()
PosixPath('/')
>>> pathlib.Path('.').resolve()
PosixPath('/')

Intriguingly,

>>> pathlib.Path('spam').resolve().resolve()
PosixPath('/spam')
# 'spam'.resolve = '//spam'
# '//spam'.resolve = '/spam'!!!
>>> pathlib.Path('//spam').resolve()
PosixPath('/spam')

I have found the same behaviour in several Python versions:

Python 3.6.5 (default, May 15 2018, 08:20:57)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin

Python 3.4.8 (default, Mar 29 2018, 16:18:25)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin

Python 3.5.5 (default, Mar 29 2018, 16:22:58)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin

Python 3.7.0b4 (default, May 4 2018, 22:01:49)
[Clang 9.1.0 (clang-902.0.39.1)] on darwin


All running on: macOs High Sierra 10.13.4 (17E202)


There is also confirmation of same issue on Ubuntu 16.04 (Python 3.5.2) and Opensuse tumbleweed (Python 3.6.5)


I have searched for some information on this issue but I did not found anything useful.

Python docs (https://docs.python.org/3/library/pathlib.html) talks about "UNC shares" but this is not the case (in using a macOs HFS+ filesystem).

PEP 428 (https://www.python.org/dev/peps/pep-0428/) says:


    Multiple leading slashes are treated differently depending on the path flavour. They are always retained on Windows paths (because of the UNC notation):

    >>> PureWindowsPath('//some/path')
    PureWindowsPath('//some/path/')

    On POSIX, they are collapsed except if there are exactly two leading slashes, which is a special case in the POSIX specification on pathname resolution [8] (this is also necessary for Cygwin compatibility):

    >>> PurePosixPath('///some/path')
    PurePosixPath('/some/path')
    >>> PurePosixPath('//some/path')
    PurePosixPath('//some/path')


I do not think that this is related to the aforementioned issue.

However, I also checked the POSIX specification link (http://pubs.opengroup.org/onlinepubs/009...#tag_04_11) and found:

    A pathname that begins with two successive slashes may be interpreted in an implementation-defined manner, although more than two leading slashes shall be treated as a single slash.


I do not really think that this can cause a double slashes while resolving a relative path on macOs.


So, I think that this issue could be a real bug in pathlib.Path.resolve() method. Specifically on POSIX flavour.

A user of Python Forum (killerrex) and I have traced the bugs to Lib/pathlib.py:319 in the Python 3.6 repository https://github.com/python/cpython/blob/3...pathlib.py.

Specifically, in line 319:
	
    newpath = path + sep + name

For pathlib.Path('spam').resolve() in the root directory, newpath is '//spam' since:

    path is '/'
    sep is '/'
    name is 'spam'

killerrex has suggested two solutions:

1) from line 345 

    base = '' if path.is_absolute() else os.getcwd()
    if base == sep:
        base = ''
    return _resolve(base, str(path)) or sep

2) from line 319:

    if path.endswith(sep):
        newpath = path + name
    else:
        newpath = path + sep + name


Thank you.
msg318452 - (view) Author: Andres Ayala (killerrex) * Date: 2018-06-01 18:41
Script to reproduce:

import os
import pathlib

# Change to the Root directory
os.chdir('/')

# Create a relative path object.
p = pathlib.Path('spam')
print(p.resolve())


Expected output:
/span

Incorrect output
//span
msg319395 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2018-06-12 16:12
For reviewers,

I submitted a patch PR 7666.
Please take a look
msg320837 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2018-07-01 13:45
Please review my pull request.
This PR is pending for 19 days.
msg375968 - (view) Author: Manuel Barkhau (mbarkhau) * Date: 2020-08-26 23:17
This issue cropped up recently in the black project: https://github.com/psf/black/issues/1631

It appears to me that PR 7666 was closed without being merged.

I created https://github.com/python/cpython/pull/21971 before I had found this issue.
msg375969 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-08-27 00:24
New changeset 94ad6c674f7687ef22853cb8d42b440d6b42ddc8 by Łukasz Langa (Dong-hee Na) in branch 'master':
bpo-33660: Fix PosixPath to resolve a relative path on root
https://github.com/python/cpython/commit/94ad6c674f7687ef22853cb8d42b440d6b42ddc8
msg375971 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-08-27 00:47
New changeset 7475aa2c590e33a47f5e79e4079bca0645e93f2f by Miss Islington (bot) in branch '3.8':
bpo-33660: Fix PosixPath to resolve a relative path on root (GH-21975)
https://github.com/python/cpython/commit/7475aa2c590e33a47f5e79e4079bca0645e93f2f
msg375973 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-08-27 00:51
New changeset 211e4c6e9c1ab60bb2577dda6587fbec79f679b2 by Miss Islington (bot) in branch '3.9':
bpo-33660: Fix PosixPath to resolve a relative path on root (#21974)
https://github.com/python/cpython/commit/211e4c6e9c1ab60bb2577dda6587fbec79f679b2
msg375974 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-08-27 00:52
Sorry it took so long, this will get released in Python 3.8.6 and newer.
msg375978 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2020-08-27 01:58
Thanks Łukasz for finanlizing this work :)
History
Date User Action Args
2020-12-13 00:59:01miss-islingtonsetpull_requests: + pull_request22609
2020-12-13 00:58:53miss-islingtonsetpull_requests: + pull_request22608
2020-08-27 01:58:26corona10setmessages: + msg375978
2020-08-27 00:52:52lukasz.langasetstatus: open -> closed
versions: - Python 3.6, Python 3.7
messages: + msg375974

resolution: fixed
stage: patch review -> resolved
2020-08-27 00:51:48lukasz.langasetmessages: + msg375973
2020-08-27 00:47:19lukasz.langasetmessages: + msg375971
2020-08-27 00:25:22miss-islingtonsetpull_requests: + pull_request21084
2020-08-27 00:25:12miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request21083
2020-08-27 00:24:46lukasz.langasetmessages: + msg375969
2020-08-26 23:49:41lukasz.langasetnosy: + lukasz.langa
pull_requests: + pull_request21081
2020-08-26 23:17:04mbarkhausetmessages: + msg375968
versions: + Python 3.9, Python 3.10
2020-08-26 23:06:56mbarkhausetnosy: + mbarkhau
pull_requests: + pull_request21079
2018-07-01 13:45:28corona10setmessages: + msg320837
2018-06-12 16:12:25corona10setnosy: + corona10
messages: + msg319395
2018-06-12 14:32:10corona10setkeywords: + patch
stage: test needed -> patch review
pull_requests: + pull_request7281
2018-06-02 10:49:18tuxtimosetnosy: + tuxtimo
2018-06-02 04:35:44serhiy.storchakasetnosy: + pitrou, serhiy.storchaka
2018-06-01 18:41:45killerrexsetfiles: + plbug.py

messages: + msg318452
2018-06-01 17:06:40terry.reedysetstage: test needed
versions: + Python 3.8, - Python 3.4, Python 3.5
2018-05-30 08:05:14killerrexsetnosy: + killerrex
2018-05-27 10:58:33QbLearningPythoncreate