GeezerVison is showing github.com — ntpath.realpath() mishandles filenames that resemble drives · Issue #102475 · python/cpython
ntpath.realpath() mishandles filenames that resemble drives #102475DescriptionThe realpath() docs say:
If strict is False, the path is resolved as far as possible and any remainder is appended without checking whether it exists.
Note the word "appended". In fact, realpath() uses os.path.join() to join the path segments, and as we all know/love, os.path.join() supports resetting the drive or root, thus discarding prior parts. As a result:
>>> os.path.realpath('c:/a:b')
'a:b' # should be 'c:/a:b'
Linked PRs
Activitykostyafarber commented on Mar 18, 2023 Any ideas on how to go about fixing this one? Keen to work on it but it seems (with regards to the linked issue) there are other things related to those to sort out first? barneygale commented on Mar 19, 2023 ContributorAuthor More actions Thanks for taking a look!
Essentially: ntpath.realpath() should not call ntpath.join() internally. Instead, it should use a more naive form of joining, like sep.join() or simply head + sep + tail. Care must be taken not to double up slashes. A small utility function may be warranted.
I suspect the main problematic case is here:
|
tail = join(name, tail) if tail else name |
... but there are other usages of join() which will need some thought. Reacted by Kostya Farber and Serhiy Storchaka kostyafarber commented on Mar 19, 2023 Thanks for the detailed explanation. I'll have a go at it. eryksun commented on Mar 19, 2023
Any ideas on how to go about fixing this one?
Define a custom join() function inside of ntpath._getfinalpathname_nonstrict(). It can be simplified since it only joins two paths, the second of which is a simple relative path. For example:
if isinstance(path, bytes):
tail = b''
sep = b'\\'
else:
tail = ''
sep = '\\'
def join(path, tail):
if path[-1:] == sep or not tail:
return path + tail
return path + sep + tail
Use this function for all of the four join() calls in _getfinalpathname_nonstrict(). Reacted by Barney Gale and Kostya Farber eryksun commented on Mar 19, 2023
Use this function for all of the four join() calls in _getfinalpathname_nonstrict().
I said four join() calls instead of 3 because I was counting the following as well:
|
if path and not name: |
|
return path + tail |
The working directory on a non-existing drive should be the root path. For example, the following result is wrong:
>>> ntpath.exists('Z:')
False
>>> ntpath.realpath('Z:spam')
'Z:spam'barneygale commented on Mar 19, 2023 ContributorAuthor More actions Eryk, is that function necessary in all four cases? I wonder if we can guarantee that path does or does not end with a separator in any of them. kostyafarber commented on Mar 19, 2023
Any ideas on how to go about fixing this one?
Define a custom join() function inside of ntpath._getfinalpathname_nonstrict(). It can be simplified since it only joins two paths, the second of which is a simple relative path. For example:
if isinstance(path, bytes):
tail = b''
sep = b'\\'
else:
tail = ''
sep = '\\'
def join(path, tail):
if path[-1:] == sep or not tail:
return path + tail
return path + sep + tail
Use this function for all of the four join() calls in _getfinalpathname_nonstrict().
You've done all the work for me! eryksun commented on Mar 19, 2023 The issue also needs tests in "Lib/test_ntpath.py".
is that function necessary in all four cases? I wonder if we can guarantee that path does or does not end with a separator in any of them.
The result from nt._getfinalpathname() or nt.readlink() may or may not end with a separator. For prepending to tail, since name comes from ntpath.split(), it should never contain a backslash. However, tail could be empty, so we would need tail = (name + sep + tail) if tail else name, as opposed to tail = join(name, tail). I'd rather keep the check for an empty tail in join(). johns1c commented on Sep 13, 2023 Is os.path.join just wrong?
Is there a proper definition of a "canonical" path for Windows?
If not should the Python community create a definition? It will have to takes account of namespaces, drive letters, mount points, case sensitivity etc .
I think that one rule should be that If p2 is the result os.path.realpath( p1 ) then if a thing exists or can be created using path p2 it will be the same as that created / accessed via path p1.
I also think that os.path.realpath( 'c:/aa.b' ) should give a similar result to os.path.realpath( 'c:/a.b' )
p.s. see also some of the comments on issue #100162
MetadataMetadataStatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|