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
ntpath.realpath() mishandles filenames that resemble drives #102475
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'
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?
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.
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:
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:
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().
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
Activity
Remove test case affected by pythonGH-102475.
kostyafarber commentedon 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 commentedon Mar 19, 2023
Thanks for taking a look!
Essentially:
ntpath.realpath()should not callntpath.join()internally. Instead, it should use a more naive form of joining, likesep.join()or simplyhead + 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:
cpython/Lib/ntpath.py
Line 697 in ccb5af7
... but there are other usages of
join()which will need some thought.kostyafarber commentedon Mar 19, 2023
Thanks for the detailed explanation. I'll have a go at it.
eryksun commentedon Mar 19, 2023
Define a custom
join()function inside ofntpath._getfinalpathname_nonstrict(). It can be simplified since it only joins two paths, the second of which is a simple relative path. For example:Use this function for all of the four
join()calls in_getfinalpathname_nonstrict().eryksun commentedon Mar 19, 2023
I said four
join()calls instead of 3 because I was counting the following as well:cpython/Lib/ntpath.py
Lines 695 to 696 in 4d1f033
The working directory on a non-existing drive should be the root path. For example, the following result is wrong:
barneygale commentedon Mar 19, 2023
Eryk, is that function necessary in all four cases? I wonder if we can guarantee that
pathdoes or does not end with a separator in any of them.kostyafarber commentedon Mar 19, 2023
You've done all the work for me!
eryksun commentedon Mar 19, 2023
The issue also needs tests in "Lib/test_ntpath.py".
The result from
nt._getfinalpathname()ornt.readlink()may or may not end with a separator. For prepending totail, sincenamecomes fromntpath.split(), it should never contain a backslash. However,tailcould be empty, so we would needtail = (name + sep + tail) if tail else name, as opposed totail = join(name, tail). I'd rather keep the check for an emptytailinjoin().johns1c commentedon 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
p2is the resultos.path.realpath( p1 )then if a thing exists or can be created using pathp2it will be the same as that created / accessed via pathp1.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
3 remaining items
gh-102475: Fix os.path.realpath() for names which look like a drive (G…
[3.13] gh-102475: Fix os.path.realpath() for names which look like a …
[3.14] gh-102475: Fix os.path.realpath() for names which look like a …
pythongh-102475: Fix os.path.realpath() for names which look like a d…
[3.15] gh-102475: Fix os.path.realpath() for names which look like a …