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
Handle leading // for posixpath.commonpath #117201
changed the title [-]`posixpath.commonpath` doesn't handle leading `//` properly[/-][+]Handle leading `//` for `posixpath.commonpath`[/+]on Mar 26, 2024
This is the reason I don't like the current implementation:
fromposixpathimportabspath, commonpath, splitdrivedefisparent(path1, path2):
drive1, tail1=splitdrive(abspath(path1))
drive2, tail2=splitdrive(abspath(path2))
returndrive1==drive2andcommonpath([tail1, tail2]) ==tail1ifisparent("//foo", "//foo/bar"):
print("path1 is a parent of path2")
else:
print("path1 is not a parent of path2")
No, the paths I'm passing to splitdrive() are already normalised (I added abspath() to make that clearer), but I would still need to apply commonpath() to the tail to make the comparison work. I shouldn't have to do that.
Anyway, whether this will be accepted depends on whether this is intentional behaviour or not:
If it's intentional, we should document and test this.
If it isn't, we should document the new behaviour like how os.path.isabs() was changed to handle paths correctly starting with exactly one (back)slash on Windows.
IMHO we should consider dropping special handling for '//' at the start of a path for our path manipulation APIs. I cannot recall a Unix where '//foo' is different from '/foo', but it is an awfully long time that I've used anything other than Linux and macOS.
The Open Group documents that '//foo' may be special ("may be treated in an implementation defined way"), but AFAIK commonly used UNIX-y systems don't use this possibility.
Because most systems don't treat '//foo' specially we shouldn't change the behaviour of os.path.commonpath as this will only lead to confusing users and might have a security impact for code that expects the current behaviour.
Appearently Cygwin uses '//share/path' to access UNC shares, as does UWIN. And finally zOS uses '//path' for something implementation defined as well. The page also lists some no longer supported systems which used '//host/path' for networked file systems.
This complicates things...
Reacted by Nice Zombies, Erlend E. Aasland, GalaxySnail and Oskar Skog
Can we rediscuss the behaviour of commonpath(['//foo', '/foo']) then? As these paths would have a different meaning.
I would prefer to return the empty string in this case as they're both absolute.
As I wrote, it's complicated. My gut feeling is that we shouldn't change the behaviour for all platforms because of most platforms the two paths do no have a different meaning.
The platforms with special treatment for double slashes at the start of a path are not on our list of supported platforms, and are not tested in CI. As such we generally don't complicate the code base for them, although Cygwin used to be somewhat popular (but I haven't used Windows or Cygwin in a long time).
Because of this I'm still of the opinion what we should consider dropping specific support for double slashes at the start of paths in os.path. That's something that needs discussion outside of this issue though, and that might lead to keeping support for this on a specific list of platforms such as Cygwin.
How '//share/path/../../file' is resolved on Cygwin? Is it equal to /file or //share/file? The same question is for other platforms.
Perhaps such platforms should have a separate os.path and pathlib.Path implementations. And have more complete support of normpath(), split(), splitroot(), commonpath() etc if //share is considered a different root.
How '//share/path/../../file' is resolved on Cygwin? Is it equal to /file or //share/file? The same question is for other platforms.
I have no idea. On any platform where "//path/subpath" is special the behaviour is implementation defined, and hence not something we can implement generically in CPython.
Perhaps such platforms should have a separate os.path and pathlib.Path implementations. And have more complete support of normpath(), split(), splitroot(), commonpath() etc if //share is considered a different root.
Probably. That's one more reason to just drop the limited support for this in the stdlib and reconsider when someone proposes to add support for such a platform to CPython. On first glance the only support we have is that normpath doesn't remove the extraneous slash when there are exactly 2 slashes at the start of the path (introduced in bf222c9). Other path manipulation functions don't check for there double slashes at all.
I have no idea. On any platform where "//path/subpath" is special the behaviour is implementation defined, and hence not something we can implement generically in CPython.
Is that really the case? POSIX probably (see my remarks in #138139 (comment)) does define well how such pathnames are e.g. normalised, because it says only the first component is implementation defined.
At least to the point where Python just passes such pathnames on to some syscall or so, things should probably work, as then the OS would take care again of any interpretation of the first component.
The only thing which Python in fact cannot really do generically, would be when e.g. comparing/joining/etc. two such special pathnames (i.e. any pathname operations).
Cause e.g. in some implementation //foo/bar could be considered identical to //FOO/bar where in others not.
Activity
[-]`posixpath.commonpath` doesn't handle leading `//` properly[/-][+]Handle leading `//` for `posixpath.commonpath`[/+]serhiy-storchaka commentedon Apr 4, 2024
For reference, the original issue that introduced
commonpath()is bpo-10395/gh-54604.And its behavior was discussed at https://mail.python.org/archives/list/python-ideas@python.org/thread/VHQTSU4ERTH262OPVZ6SEO2EYCFQF56G/.
erlend-aasland commentedon Apr 4, 2024
cc. @ronaldoussoren
nineteendo commentedon Apr 5, 2024
This is the reason I don't like the current implementation:
Output:
path1 is not a parent of path2.erlend-aasland commentedon Apr 5, 2024
If anything, this looks to me like a
commonpathdocumentation issue.Moreover, if I were to write such a test, I would normalise
tail1before comparing it, or more probable: use a higher level API.nineteendo commentedon Apr 5, 2024
If that's the case, that's fine by me, but that should be confirmed by @ronaldoussoren.
The thing is: the paths are already normalised here, but that won't replace precisely 2 leading slashes. Which is intended behaviour.
erlend-aasland commentedon Apr 5, 2024
splitdrivedoes not normalise the paths, if that is what you imply.nineteendo commentedon Apr 5, 2024
No, the paths I'm passing to
splitdrive()are already normalised (I addedabspath()to make that clearer), but I would still need to applycommonpath()to the tail to make the comparison work. I shouldn't have to do that.erlend-aasland commentedon Apr 5, 2024
Well, it is still a backwards-incompatible change.
nineteendo commentedon Apr 5, 2024
Anyway, whether this will be accepted depends on whether this is intentional behaviour or not:
os.path.isabs()was changed to handle paths correctly starting with exactly one (back)slash on Windows.ronaldoussoren commentedon Apr 5, 2024
IMHO we should consider dropping special handling for '//' at the start of a path for our path manipulation APIs. I cannot recall a Unix where '//foo' is different from '/foo', but it is an awfully long time that I've used anything other than Linux and macOS.
The Open Group documents that '//foo' may be special ("may be treated in an implementation defined way"), but AFAIK commonly used UNIX-y systems don't use this possibility.
Because most systems don't treat '//foo' specially we shouldn't change the behaviour of
os.path.commonpathas this will only lead to confusing users and might have a security impact for code that expects the current behaviour.ronaldoussoren commentedon Apr 5, 2024
To reply to myself: https://unix.stackexchange.com/questions/256497/on-what-systems-is-foo-bar-different-from-foo-bar
Appearently Cygwin uses '//share/path' to access UNC shares, as does UWIN. And finally zOS uses '//path' for something implementation defined as well. The page also lists some no longer supported systems which used '//host/path' for networked file systems.
This complicates things...
nineteendo commentedon Apr 5, 2024
Can we rediscuss the behaviour of
commonpath(['//foo', '/foo'])then? As these paths would have a different meaning.I would prefer to return the empty string in this case as they're both absolute.
ronaldoussoren commentedon Apr 5, 2024
As I wrote, it's complicated. My gut feeling is that we shouldn't change the behaviour for all platforms because of most platforms the two paths do no have a different meaning.
The platforms with special treatment for double slashes at the start of a path are not on our list of supported platforms, and are not tested in CI. As such we generally don't complicate the code base for them, although Cygwin used to be somewhat popular (but I haven't used Windows or Cygwin in a long time).
Because of this I'm still of the opinion what we should consider dropping specific support for double slashes at the start of paths in
os.path. That's something that needs discussion outside of this issue though, and that might lead to keeping support for this on a specific list of platforms such as Cygwin.nineteendo commentedon Apr 5, 2024
OK, then I'll close this issue for the time being. Could you start a discussion about this?
serhiy-storchaka commentedon Apr 5, 2024
How '//share/path/../../file' is resolved on Cygwin? Is it equal to
/fileor//share/file? The same question is for other platforms.Perhaps such platforms should have a separate
os.pathandpathlib.Pathimplementations. And have more complete support ofnormpath(),split(),splitroot(),commonpath()etc if//shareis considered a different root.ronaldoussoren commentedon Apr 5, 2024
I have no idea. On any platform where "//path/subpath" is special the behaviour is implementation defined, and hence not something we can implement generically in CPython.
Probably. That's one more reason to just drop the limited support for this in the stdlib and reconsider when someone proposes to add support for such a platform to CPython. On first glance the only support we have is that
normpathdoesn't remove the extraneous slash when there are exactly 2 slashes at the start of the path (introduced in bf222c9). Other path manipulation functions don't check for there double slashes at all.nineteendo commentedon Apr 5, 2024
posixpath.splitroot():cpython/Lib/posixpath.py
Lines 162 to 164 in 4d4a6f1
The only function that doesn't handle it properly is
posixpath.commonpath()(posixpath.realpath()works as intended: #117338).calestyo commentedon Aug 25, 2025
Just for the records:
Is that really the case? POSIX probably (see my remarks in #138139 (comment)) does define well how such pathnames are e.g. normalised, because it says only the first component is implementation defined.
At least to the point where Python just passes such pathnames on to some syscall or so, things should probably work, as then the OS would take care again of any interpretation of the first component.
The only thing which Python in fact cannot really do generically, would be when e.g. comparing/joining/etc. two such special pathnames (i.e. any pathname operations).
Cause e.g. in some implementation
//foo/barcould be considered identical to//FOO/barwhere in others not.