bpo-38355: Fix ntpath.realpath failing on sys.executable (GH-16551)

This commit is contained in:
Steve Dower 2019-10-03 08:31:03 -07:00 committed by GitHub
parent 098e25672f
commit a0e3d27e4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 15 additions and 18 deletions

View File

@ -560,13 +560,6 @@ else:
return path return path
def _getfinalpathname_nonstrict(path): def _getfinalpathname_nonstrict(path):
# Fast path to get the final path name. If this succeeds, there
# is no need to go any further.
try:
return _getfinalpathname(path)
except OSError:
pass
# These error codes indicate that we should stop resolving the path # These error codes indicate that we should stop resolving the path
# and return the value we currently have. # and return the value we currently have.
# 1: ERROR_INVALID_FUNCTION # 1: ERROR_INVALID_FUNCTION
@ -579,8 +572,9 @@ else:
# 67: ERROR_BAD_NET_NAME (implies remote server unavailable) # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
# 87: ERROR_INVALID_PARAMETER # 87: ERROR_INVALID_PARAMETER
# 123: ERROR_INVALID_NAME # 123: ERROR_INVALID_NAME
# 1920: ERROR_CANT_ACCESS_FILE
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink) # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1921 allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1920, 1921
# Non-strict algorithm is to find as much of the target directory # Non-strict algorithm is to find as much of the target directory
# as we can and join the rest. # as we can and join the rest.
@ -615,9 +609,13 @@ else:
unc_prefix = '\\\\?\\UNC\\' unc_prefix = '\\\\?\\UNC\\'
new_unc_prefix = '\\\\' new_unc_prefix = '\\\\'
cwd = os.getcwd() cwd = os.getcwd()
did_not_exist = not exists(path)
had_prefix = path.startswith(prefix) had_prefix = path.startswith(prefix)
path = _getfinalpathname_nonstrict(path) try:
path = _getfinalpathname(path)
initial_winerror = 0
except OSError as ex:
initial_winerror = ex.winerror
path = _getfinalpathname_nonstrict(path)
# The path returned by _getfinalpathname will always start with \\?\ - # The path returned by _getfinalpathname will always start with \\?\ -
# strip off that prefix unless it was already provided on the original # strip off that prefix unless it was already provided on the original
# path. # path.
@ -635,7 +633,7 @@ else:
except OSError as ex: except OSError as ex:
# If the path does not exist and originally did not exist, then # If the path does not exist and originally did not exist, then
# strip the prefix anyway. # strip the prefix anyway.
if ex.winerror in {2, 3} and did_not_exist: if ex.winerror == initial_winerror:
path = spath path = spath
return path return path

View File

@ -329,16 +329,14 @@ class TestNtpath(NtpathTestCase):
self.addCleanup(support.unlink, ABSTFN + "c") self.addCleanup(support.unlink, ABSTFN + "c")
self.addCleanup(support.unlink, ABSTFN + "a") self.addCleanup(support.unlink, ABSTFN + "a")
P = "\\\\?\\"
os.symlink(ABSTFN, ABSTFN) os.symlink(ABSTFN, ABSTFN)
self.assertPathEqual(ntpath.realpath(ABSTFN), P + ABSTFN) self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN)
# cycles are non-deterministic as to which path is returned, but # cycles are non-deterministic as to which path is returned, but
# it will always be the fully resolved path of one member of the cycle # it will always be the fully resolved path of one member of the cycle
os.symlink(ABSTFN + "1", ABSTFN + "2") os.symlink(ABSTFN + "1", ABSTFN + "2")
os.symlink(ABSTFN + "2", ABSTFN + "1") os.symlink(ABSTFN + "2", ABSTFN + "1")
expected = (P + ABSTFN + "1", P + ABSTFN + "2") expected = (ABSTFN + "1", ABSTFN + "2")
self.assertPathIn(ntpath.realpath(ABSTFN + "1"), expected) self.assertPathIn(ntpath.realpath(ABSTFN + "1"), expected)
self.assertPathIn(ntpath.realpath(ABSTFN + "2"), expected) self.assertPathIn(ntpath.realpath(ABSTFN + "2"), expected)
@ -357,14 +355,14 @@ class TestNtpath(NtpathTestCase):
expected) expected)
os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a") os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
self.assertPathEqual(ntpath.realpath(ABSTFN + "a"), P + ABSTFN + "a") self.assertPathEqual(ntpath.realpath(ABSTFN + "a"), ABSTFN + "a")
os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN)) os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
+ "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c") + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
self.assertPathEqual(ntpath.realpath(ABSTFN + "c"), P + ABSTFN + "c") self.assertPathEqual(ntpath.realpath(ABSTFN + "c"), ABSTFN + "c")
# Test using relative path as well. # Test using relative path as well.
self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), P + ABSTFN) self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN)
@support.skip_unless_symlink @support.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')

View File

@ -0,0 +1 @@
Fixes ``ntpath.realpath`` failing on ``sys.executable``.