mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Update ntpath.py to version from CPython 3.8
This commit is contained in:
1 file changed
+153
-20
Vendored
+153
-20
@@ -61,6 +61,14 @@ def normcase(s):
|
||||
def isabs(s):
|
||||
"""Test whether a path is absolute"""
|
||||
s = os.fspath(s)
|
||||
# Paths beginning with \\?\ are always absolute, but do not
|
||||
# necessarily contain a drive.
|
||||
if isinstance(s, bytes):
|
||||
if s.replace(b'/', b'\\').startswith(b'\\\\?\\'):
|
||||
return True
|
||||
else:
|
||||
if s.replace('/', '\\').startswith('\\\\?\\'):
|
||||
return True
|
||||
s = splitdrive(s)[1]
|
||||
return len(s) > 0 and s[0] in _get_bothseps(s)
|
||||
|
||||
@@ -458,7 +466,8 @@ def normpath(path):
|
||||
# in the case of paths with these prefixes:
|
||||
# \\.\ -> device names
|
||||
# \\?\ -> literal paths
|
||||
# do not do any normalization, but return the path unchanged
|
||||
# do not do any normalization, but return the path
|
||||
# unchanged apart from the call to os.fspath()
|
||||
return path
|
||||
path = path.replace(altsep, sep)
|
||||
prefix, path = splitdrive(path)
|
||||
@@ -519,8 +528,149 @@ else: # use native Windows method on Windows
|
||||
except (OSError, ValueError):
|
||||
return _abspath_fallback(path)
|
||||
|
||||
# realpath is a no-op on systems without islink support
|
||||
realpath = abspath
|
||||
try:
|
||||
from nt import _getfinalpathname, readlink as _nt_readlink
|
||||
except ImportError:
|
||||
# realpath is a no-op on systems without _getfinalpathname support.
|
||||
realpath = abspath
|
||||
else:
|
||||
def _readlink_deep(path):
|
||||
# These error codes indicate that we should stop reading links and
|
||||
# return the path we currently have.
|
||||
# 1: ERROR_INVALID_FUNCTION
|
||||
# 2: ERROR_FILE_NOT_FOUND
|
||||
# 3: ERROR_DIRECTORY_NOT_FOUND
|
||||
# 5: ERROR_ACCESS_DENIED
|
||||
# 21: ERROR_NOT_READY (implies drive with no media)
|
||||
# 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
|
||||
# 50: ERROR_NOT_SUPPORTED (implies no support for reparse points)
|
||||
# 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
|
||||
# 87: ERROR_INVALID_PARAMETER
|
||||
# 4390: ERROR_NOT_A_REPARSE_POINT
|
||||
# 4392: ERROR_INVALID_REPARSE_DATA
|
||||
# 4393: ERROR_REPARSE_TAG_INVALID
|
||||
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 4390, 4392, 4393
|
||||
|
||||
seen = set()
|
||||
while normcase(path) not in seen:
|
||||
seen.add(normcase(path))
|
||||
try:
|
||||
old_path = path
|
||||
path = _nt_readlink(path)
|
||||
# Links may be relative, so resolve them against their
|
||||
# own location
|
||||
if not isabs(path):
|
||||
# If it's something other than a symlink, we don't know
|
||||
# what it's actually going to be resolved against, so
|
||||
# just return the old path.
|
||||
if not islink(old_path):
|
||||
path = old_path
|
||||
break
|
||||
path = normpath(join(dirname(old_path), path))
|
||||
except OSError as ex:
|
||||
if ex.winerror in allowed_winerror:
|
||||
break
|
||||
raise
|
||||
except ValueError:
|
||||
# Stop on reparse points that are not symlinks
|
||||
break
|
||||
return path
|
||||
|
||||
def _getfinalpathname_nonstrict(path):
|
||||
# These error codes indicate that we should stop resolving the path
|
||||
# and return the value we currently have.
|
||||
# 1: ERROR_INVALID_FUNCTION
|
||||
# 2: ERROR_FILE_NOT_FOUND
|
||||
# 3: ERROR_DIRECTORY_NOT_FOUND
|
||||
# 5: ERROR_ACCESS_DENIED
|
||||
# 21: ERROR_NOT_READY (implies drive with no media)
|
||||
# 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
|
||||
# 50: ERROR_NOT_SUPPORTED
|
||||
# 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
|
||||
# 87: ERROR_INVALID_PARAMETER
|
||||
# 123: ERROR_INVALID_NAME
|
||||
# 1920: ERROR_CANT_ACCESS_FILE
|
||||
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
|
||||
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
|
||||
# as we can and join the rest.
|
||||
tail = ''
|
||||
while path:
|
||||
try:
|
||||
path = _getfinalpathname(path)
|
||||
return join(path, tail) if tail else path
|
||||
except OSError as ex:
|
||||
if ex.winerror not in allowed_winerror:
|
||||
raise
|
||||
try:
|
||||
# The OS could not resolve this path fully, so we attempt
|
||||
# to follow the link ourselves. If we succeed, join the tail
|
||||
# and return.
|
||||
new_path = _readlink_deep(path)
|
||||
if new_path != path:
|
||||
return join(new_path, tail) if tail else new_path
|
||||
except OSError:
|
||||
# If we fail to readlink(), let's keep traversing
|
||||
pass
|
||||
path, name = split(path)
|
||||
# TODO (bpo-38186): Request the real file name from the directory
|
||||
# entry using FindFirstFileW. For now, we will return the path
|
||||
# as best we have it
|
||||
if path and not name:
|
||||
return path + tail
|
||||
tail = join(name, tail) if tail else name
|
||||
return tail
|
||||
|
||||
def realpath(path):
|
||||
path = normpath(path)
|
||||
if isinstance(path, bytes):
|
||||
prefix = b'\\\\?\\'
|
||||
unc_prefix = b'\\\\?\\UNC\\'
|
||||
new_unc_prefix = b'\\\\'
|
||||
cwd = os.getcwdb()
|
||||
# bpo-38081: Special case for realpath(b'nul')
|
||||
if normcase(path) == normcase(os.fsencode(devnull)):
|
||||
return b'\\\\.\\NUL'
|
||||
else:
|
||||
prefix = '\\\\?\\'
|
||||
unc_prefix = '\\\\?\\UNC\\'
|
||||
new_unc_prefix = '\\\\'
|
||||
cwd = os.getcwd()
|
||||
# bpo-38081: Special case for realpath('nul')
|
||||
if normcase(path) == normcase(devnull):
|
||||
return '\\\\.\\NUL'
|
||||
had_prefix = path.startswith(prefix)
|
||||
if not had_prefix and not isabs(path):
|
||||
path = join(cwd, 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 \\?\ -
|
||||
# strip off that prefix unless it was already provided on the original
|
||||
# path.
|
||||
if not had_prefix and path.startswith(prefix):
|
||||
# For UNC paths, the prefix will actually be \\?\UNC\
|
||||
# Handle that case as well.
|
||||
if path.startswith(unc_prefix):
|
||||
spath = new_unc_prefix + path[len(unc_prefix):]
|
||||
else:
|
||||
spath = path[len(prefix):]
|
||||
# Ensure that the non-prefixed path resolves to the same path
|
||||
try:
|
||||
if _getfinalpathname(spath) == path:
|
||||
path = spath
|
||||
except OSError as ex:
|
||||
# If the path does not exist and originally did not exist, then
|
||||
# strip the prefix anyway.
|
||||
if ex.winerror == initial_winerror:
|
||||
path = spath
|
||||
return path
|
||||
|
||||
|
||||
# Win9x family and earlier have no Unicode filename support.
|
||||
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
|
||||
sys.getwindowsversion()[3] >= 2)
|
||||
@@ -633,23 +783,6 @@ def commonpath(paths):
|
||||
raise
|
||||
|
||||
|
||||
# determine if two files are in fact the same file
|
||||
try:
|
||||
# GetFinalPathNameByHandle is available starting with Windows 6.0.
|
||||
# Windows XP and non-Windows OS'es will mock _getfinalpathname.
|
||||
if sys.getwindowsversion()[:2] >= (6, 0):
|
||||
from nt import _getfinalpathname
|
||||
else:
|
||||
raise ImportError
|
||||
except (AttributeError, ImportError):
|
||||
# On Windows XP and earlier, two files are the same if their absolute
|
||||
# pathnames are the same.
|
||||
# Non-Windows operating systems fake this method with an XP
|
||||
# approximation.
|
||||
def _getfinalpathname(f):
|
||||
return normcase(abspath(f))
|
||||
|
||||
|
||||
try:
|
||||
# The genericpath.isdir implementation uses os.stat and checks the mode
|
||||
# attribute to tell whether or not the path is a directory.
|
||||
|
||||
Reference in new issue
Block a user