123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889 |
- # Module 'ntpath' -- common operations on WinNT/Win95 pathnames
- """Common pathname manipulations, WindowsNT/95 version.
- Instead of importing this module directly, import os and refer to this
- module as os.path.
- """
- # strings representing various path-related bits and pieces
- # These are primarily for export; internally, they are hardcoded.
- # Should be set before imports for resolving cyclic dependency.
- curdir = '.'
- pardir = '..'
- extsep = '.'
- sep = '\\'
- pathsep = ';'
- altsep = '/'
- defpath = '.;C:\\bin'
- devnull = 'nul'
- import os
- import sys
- import stat
- import genericpath
- from genericpath import *
- __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
- "basename","dirname","commonprefix","getsize","getmtime",
- "getatime","getctime", "islink","exists","lexists","isdir","isfile",
- "ismount", "expanduser","expandvars","normpath","abspath",
- "curdir","pardir","sep","pathsep","defpath","altsep",
- "extsep","devnull","realpath","supports_unicode_filenames","relpath",
- "samefile", "sameopenfile", "samestat", "commonpath", "isjunction"]
- def _get_bothseps(path):
- if isinstance(path, bytes):
- return b'\\/'
- else:
- return '\\/'
- # Normalize the case of a pathname and map slashes to backslashes.
- # Other normalizations (such as optimizing '../' away) are not done
- # (this is done by normpath).
- try:
- from _winapi import (
- LCMapStringEx as _LCMapStringEx,
- LOCALE_NAME_INVARIANT as _LOCALE_NAME_INVARIANT,
- LCMAP_LOWERCASE as _LCMAP_LOWERCASE)
- def normcase(s):
- """Normalize case of pathname.
- Makes all characters lowercase and all slashes into backslashes.
- """
- s = os.fspath(s)
- if not s:
- return s
- if isinstance(s, bytes):
- encoding = sys.getfilesystemencoding()
- s = s.decode(encoding, 'surrogateescape').replace('/', '\\')
- s = _LCMapStringEx(_LOCALE_NAME_INVARIANT,
- _LCMAP_LOWERCASE, s)
- return s.encode(encoding, 'surrogateescape')
- else:
- return _LCMapStringEx(_LOCALE_NAME_INVARIANT,
- _LCMAP_LOWERCASE,
- s.replace('/', '\\'))
- except ImportError:
- def normcase(s):
- """Normalize case of pathname.
- Makes all characters lowercase and all slashes into backslashes.
- """
- s = os.fspath(s)
- if isinstance(s, bytes):
- return os.fsencode(os.fsdecode(s).replace('/', '\\').lower())
- return s.replace('/', '\\').lower()
- # Return whether a path is absolute.
- # Trivial in Posix, harder on Windows.
- # For Windows it is absolute if it starts with a slash or backslash (current
- # volume), or if a pathname after the volume-letter-and-colon or UNC-resource
- # starts with a slash or backslash.
- def isabs(s):
- """Test whether a path is absolute"""
- s = os.fspath(s)
- if isinstance(s, bytes):
- sep = b'\\'
- altsep = b'/'
- colon_sep = b':\\'
- else:
- sep = '\\'
- altsep = '/'
- colon_sep = ':\\'
- s = s[:3].replace(altsep, sep)
- # Absolute: UNC, device, and paths with a drive and root.
- # LEGACY BUG: isabs("/x") should be false since the path has no drive.
- if s.startswith(sep) or s.startswith(colon_sep, 1):
- return True
- return False
- # Join two (or more) paths.
- def join(path, *paths):
- path = os.fspath(path)
- if isinstance(path, bytes):
- sep = b'\\'
- seps = b'\\/'
- colon = b':'
- else:
- sep = '\\'
- seps = '\\/'
- colon = ':'
- try:
- if not paths:
- path[:0] + sep #23780: Ensure compatible data type even if p is null.
- result_drive, result_root, result_path = splitroot(path)
- for p in map(os.fspath, paths):
- p_drive, p_root, p_path = splitroot(p)
- if p_root:
- # Second path is absolute
- if p_drive or not result_drive:
- result_drive = p_drive
- result_root = p_root
- result_path = p_path
- continue
- elif p_drive and p_drive != result_drive:
- if p_drive.lower() != result_drive.lower():
- # Different drives => ignore the first path entirely
- result_drive = p_drive
- result_root = p_root
- result_path = p_path
- continue
- # Same drive in different case
- result_drive = p_drive
- # Second path is relative to the first
- if result_path and result_path[-1] not in seps:
- result_path = result_path + sep
- result_path = result_path + p_path
- ## add separator between UNC and non-absolute path
- if (result_path and not result_root and
- result_drive and result_drive[-1:] not in colon + seps):
- return result_drive + sep + result_path
- return result_drive + result_root + result_path
- except (TypeError, AttributeError, BytesWarning):
- genericpath._check_arg_types('join', path, *paths)
- raise
- # Split a path in a drive specification (a drive letter followed by a
- # colon) and the path specification.
- # It is always true that drivespec + pathspec == p
- def splitdrive(p):
- """Split a pathname into drive/UNC sharepoint and relative path specifiers.
- Returns a 2-tuple (drive_or_unc, path); either part may be empty.
- If you assign
- result = splitdrive(p)
- It is always true that:
- result[0] + result[1] == p
- If the path contained a drive letter, drive_or_unc will contain everything
- up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
- If the path contained a UNC path, the drive_or_unc will contain the host name
- and share up to but not including the fourth directory separator character.
- e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
- Paths cannot contain both a drive letter and a UNC path.
- """
- drive, root, tail = splitroot(p)
- return drive, root + tail
- def splitroot(p):
- """Split a pathname into drive, root and tail. The drive is defined
- exactly as in splitdrive(). On Windows, the root may be a single path
- separator or an empty string. The tail contains anything after the root.
- For example:
- splitroot('//server/share/') == ('//server/share', '/', '')
- splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney')
- splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham')
- splitroot('Windows/notepad') == ('', '', 'Windows/notepad')
- """
- p = os.fspath(p)
- if isinstance(p, bytes):
- sep = b'\\'
- altsep = b'/'
- colon = b':'
- unc_prefix = b'\\\\?\\UNC\\'
- empty = b''
- else:
- sep = '\\'
- altsep = '/'
- colon = ':'
- unc_prefix = '\\\\?\\UNC\\'
- empty = ''
- normp = p.replace(altsep, sep)
- if normp[:1] == sep:
- if normp[1:2] == sep:
- # UNC drives, e.g. \\server\share or \\?\UNC\server\share
- # Device drives, e.g. \\.\device or \\?\device
- start = 8 if normp[:8].upper() == unc_prefix else 2
- index = normp.find(sep, start)
- if index == -1:
- return p, empty, empty
- index2 = normp.find(sep, index + 1)
- if index2 == -1:
- return p, empty, empty
- return p[:index2], p[index2:index2 + 1], p[index2 + 1:]
- else:
- # Relative path with root, e.g. \Windows
- return empty, p[:1], p[1:]
- elif normp[1:2] == colon:
- if normp[2:3] == sep:
- # Absolute drive-letter path, e.g. X:\Windows
- return p[:2], p[2:3], p[3:]
- else:
- # Relative path with drive, e.g. X:Windows
- return p[:2], empty, p[2:]
- else:
- # Relative path, e.g. Windows
- return empty, empty, p
- # Split a path in head (everything up to the last '/') and tail (the
- # rest). After the trailing '/' is stripped, the invariant
- # join(head, tail) == p holds.
- # The resulting head won't end in '/' unless it is the root.
- def split(p):
- """Split a pathname.
- Return tuple (head, tail) where tail is everything after the final slash.
- Either part may be empty."""
- p = os.fspath(p)
- seps = _get_bothseps(p)
- d, r, p = splitroot(p)
- # set i to index beyond p's last slash
- i = len(p)
- while i and p[i-1] not in seps:
- i -= 1
- head, tail = p[:i], p[i:] # now tail has no slashes
- return d + r + head.rstrip(seps), tail
- # Split a path in root and extension.
- # The extension is everything starting at the last dot in the last
- # pathname component; the root is everything before that.
- # It is always true that root + ext == p.
- def splitext(p):
- p = os.fspath(p)
- if isinstance(p, bytes):
- return genericpath._splitext(p, b'\\', b'/', b'.')
- else:
- return genericpath._splitext(p, '\\', '/', '.')
- splitext.__doc__ = genericpath._splitext.__doc__
- # Return the tail (basename) part of a path.
- def basename(p):
- """Returns the final component of a pathname"""
- return split(p)[1]
- # Return the head (dirname) part of a path.
- def dirname(p):
- """Returns the directory component of a pathname"""
- return split(p)[0]
- # Is a path a junction?
- if hasattr(os.stat_result, 'st_reparse_tag'):
- def isjunction(path):
- """Test whether a path is a junction"""
- try:
- st = os.lstat(path)
- except (OSError, ValueError, AttributeError):
- return False
- return bool(st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)
- else:
- def isjunction(path):
- """Test whether a path is a junction"""
- os.fspath(path)
- return False
- # Being true for dangling symbolic links is also useful.
- def lexists(path):
- """Test whether a path exists. Returns True for broken symbolic links"""
- try:
- st = os.lstat(path)
- except (OSError, ValueError):
- return False
- return True
- # Is a path a mount point?
- # Any drive letter root (eg c:\)
- # Any share UNC (eg \\server\share)
- # Any volume mounted on a filesystem folder
- #
- # No one method detects all three situations. Historically we've lexically
- # detected drive letter roots and share UNCs. The canonical approach to
- # detecting mounted volumes (querying the reparse tag) fails for the most
- # common case: drive letter roots. The alternative which uses GetVolumePathName
- # fails if the drive letter is the result of a SUBST.
- try:
- from nt import _getvolumepathname
- except ImportError:
- _getvolumepathname = None
- def ismount(path):
- """Test whether a path is a mount point (a drive root, the root of a
- share, or a mounted volume)"""
- path = os.fspath(path)
- seps = _get_bothseps(path)
- path = abspath(path)
- drive, root, rest = splitroot(path)
- if drive and drive[0] in seps:
- return not rest
- if root and not rest:
- return True
- if _getvolumepathname:
- x = path.rstrip(seps)
- y =_getvolumepathname(path).rstrip(seps)
- return x.casefold() == y.casefold()
- else:
- return False
- # Expand paths beginning with '~' or '~user'.
- # '~' means $HOME; '~user' means that user's home directory.
- # If the path doesn't begin with '~', or if the user or $HOME is unknown,
- # the path is returned unchanged (leaving error reporting to whatever
- # function is called with the expanded path as argument).
- # See also module 'glob' for expansion of *, ? and [...] in pathnames.
- # (A function should also be defined to do full *sh-style environment
- # variable expansion.)
- def expanduser(path):
- """Expand ~ and ~user constructs.
- If user or $HOME is unknown, do nothing."""
- path = os.fspath(path)
- if isinstance(path, bytes):
- tilde = b'~'
- else:
- tilde = '~'
- if not path.startswith(tilde):
- return path
- i, n = 1, len(path)
- while i < n and path[i] not in _get_bothseps(path):
- i += 1
- if 'USERPROFILE' in os.environ:
- userhome = os.environ['USERPROFILE']
- elif not 'HOMEPATH' in os.environ:
- return path
- else:
- try:
- drive = os.environ['HOMEDRIVE']
- except KeyError:
- drive = ''
- userhome = join(drive, os.environ['HOMEPATH'])
- if i != 1: #~user
- target_user = path[1:i]
- if isinstance(target_user, bytes):
- target_user = os.fsdecode(target_user)
- current_user = os.environ.get('USERNAME')
- if target_user != current_user:
- # Try to guess user home directory. By default all user
- # profile directories are located in the same place and are
- # named by corresponding usernames. If userhome isn't a
- # normal profile directory, this guess is likely wrong,
- # so we bail out.
- if current_user != basename(userhome):
- return path
- userhome = join(dirname(userhome), target_user)
- if isinstance(path, bytes):
- userhome = os.fsencode(userhome)
- return userhome + path[i:]
- # Expand paths containing shell variable substitutions.
- # The following rules apply:
- # - no expansion within single quotes
- # - '$$' is translated into '$'
- # - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
- # - ${varname} is accepted.
- # - $varname is accepted.
- # - %varname% is accepted.
- # - varnames can be made out of letters, digits and the characters '_-'
- # (though is not verified in the ${varname} and %varname% cases)
- # XXX With COMMAND.COM you can use any characters in a variable name,
- # XXX except '^|<>='.
- def expandvars(path):
- """Expand shell variables of the forms $var, ${var} and %var%.
- Unknown variables are left unchanged."""
- path = os.fspath(path)
- if isinstance(path, bytes):
- if b'$' not in path and b'%' not in path:
- return path
- import string
- varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
- quote = b'\''
- percent = b'%'
- brace = b'{'
- rbrace = b'}'
- dollar = b'$'
- environ = getattr(os, 'environb', None)
- else:
- if '$' not in path and '%' not in path:
- return path
- import string
- varchars = string.ascii_letters + string.digits + '_-'
- quote = '\''
- percent = '%'
- brace = '{'
- rbrace = '}'
- dollar = '$'
- environ = os.environ
- res = path[:0]
- index = 0
- pathlen = len(path)
- while index < pathlen:
- c = path[index:index+1]
- if c == quote: # no expansion within single quotes
- path = path[index + 1:]
- pathlen = len(path)
- try:
- index = path.index(c)
- res += c + path[:index + 1]
- except ValueError:
- res += c + path
- index = pathlen - 1
- elif c == percent: # variable or '%'
- if path[index + 1:index + 2] == percent:
- res += c
- index += 1
- else:
- path = path[index+1:]
- pathlen = len(path)
- try:
- index = path.index(percent)
- except ValueError:
- res += percent + path
- index = pathlen - 1
- else:
- var = path[:index]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = percent + var + percent
- res += value
- elif c == dollar: # variable or '$$'
- if path[index + 1:index + 2] == dollar:
- res += c
- index += 1
- elif path[index + 1:index + 2] == brace:
- path = path[index+2:]
- pathlen = len(path)
- try:
- index = path.index(rbrace)
- except ValueError:
- res += dollar + brace + path
- index = pathlen - 1
- else:
- var = path[:index]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = dollar + brace + var + rbrace
- res += value
- else:
- var = path[:0]
- index += 1
- c = path[index:index + 1]
- while c and c in varchars:
- var += c
- index += 1
- c = path[index:index + 1]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = dollar + var
- res += value
- if c:
- index -= 1
- else:
- res += c
- index += 1
- return res
- # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
- # Previously, this function also truncated pathnames to 8+3 format,
- # but as this module is called "ntpath", that's obviously wrong!
- try:
- from nt import _path_normpath as normpath
- except ImportError:
- def normpath(path):
- """Normalize path, eliminating double slashes, etc."""
- path = os.fspath(path)
- if isinstance(path, bytes):
- sep = b'\\'
- altsep = b'/'
- curdir = b'.'
- pardir = b'..'
- else:
- sep = '\\'
- altsep = '/'
- curdir = '.'
- pardir = '..'
- path = path.replace(altsep, sep)
- drive, root, path = splitroot(path)
- prefix = drive + root
- comps = path.split(sep)
- i = 0
- while i < len(comps):
- if not comps[i] or comps[i] == curdir:
- del comps[i]
- elif comps[i] == pardir:
- if i > 0 and comps[i-1] != pardir:
- del comps[i-1:i+1]
- i -= 1
- elif i == 0 and root:
- del comps[i]
- else:
- i += 1
- else:
- i += 1
- # If the path is now empty, substitute '.'
- if not prefix and not comps:
- comps.append(curdir)
- return prefix + sep.join(comps)
- def _abspath_fallback(path):
- """Return the absolute version of a path as a fallback function in case
- `nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
- more.
- """
- path = os.fspath(path)
- if not isabs(path):
- if isinstance(path, bytes):
- cwd = os.getcwdb()
- else:
- cwd = os.getcwd()
- path = join(cwd, path)
- return normpath(path)
- # Return an absolute path.
- try:
- from nt import _getfullpathname
- except ImportError: # not running on Windows - mock up something sensible
- abspath = _abspath_fallback
- else: # use native Windows method on Windows
- def abspath(path):
- """Return the absolute version of a path."""
- try:
- return _getfullpathname(normpath(path))
- except (OSError, ValueError):
- return _abspath_fallback(path)
- 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
- # 53: ERROR_BAD_NETPATH
- # 65: ERROR_NETWORK_ACCESS_DENIED
- # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
- # 87: ERROR_INVALID_PARAMETER
- # 123: ERROR_INVALID_NAME
- # 161: ERROR_BAD_PATHNAME
- # 1920: ERROR_CANT_ACCESS_FILE
- # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
- allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1920, 1921
- # Non-strict algorithm is to find as much of the target directory
- # as we can and join the rest.
- tail = path[:0]
- 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, *, strict=False):
- 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 ValueError as ex:
- # gh-106242: Raised for embedded null characters
- # In strict mode, we convert into an OSError.
- # Non-strict mode returns the path as-is, since we've already
- # made it absolute.
- if strict:
- raise OSError(str(ex)) from None
- path = normpath(path)
- except OSError as ex:
- if strict:
- raise
- 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 ValueError as ex:
- # Unexpected, as an invalid path should not have gained a prefix
- # at any point, but we ignore this error just in case.
- pass
- 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
- # All supported version have Unicode filename support.
- supports_unicode_filenames = True
- def relpath(path, start=None):
- """Return a relative version of a path"""
- path = os.fspath(path)
- if isinstance(path, bytes):
- sep = b'\\'
- curdir = b'.'
- pardir = b'..'
- else:
- sep = '\\'
- curdir = '.'
- pardir = '..'
- if start is None:
- start = curdir
- if not path:
- raise ValueError("no path specified")
- start = os.fspath(start)
- try:
- start_abs = abspath(normpath(start))
- path_abs = abspath(normpath(path))
- start_drive, _, start_rest = splitroot(start_abs)
- path_drive, _, path_rest = splitroot(path_abs)
- if normcase(start_drive) != normcase(path_drive):
- raise ValueError("path is on mount %r, start on mount %r" % (
- path_drive, start_drive))
- start_list = [x for x in start_rest.split(sep) if x]
- path_list = [x for x in path_rest.split(sep) if x]
- # Work out how much of the filepath is shared by start and path.
- i = 0
- for e1, e2 in zip(start_list, path_list):
- if normcase(e1) != normcase(e2):
- break
- i += 1
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return curdir
- return join(*rel_list)
- except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
- genericpath._check_arg_types('relpath', path, start)
- raise
- # Return the longest common sub-path of the sequence of paths given as input.
- # The function is case-insensitive and 'separator-insensitive', i.e. if the
- # only difference between two paths is the use of '\' versus '/' as separator,
- # they are deemed to be equal.
- #
- # However, the returned path will have the standard '\' separator (even if the
- # given paths had the alternative '/' separator) and will have the case of the
- # first path given in the sequence. Additionally, any trailing separator is
- # stripped from the returned path.
- def commonpath(paths):
- """Given a sequence of path names, returns the longest common sub-path."""
- if not paths:
- raise ValueError('commonpath() arg is an empty sequence')
- paths = tuple(map(os.fspath, paths))
- if isinstance(paths[0], bytes):
- sep = b'\\'
- altsep = b'/'
- curdir = b'.'
- else:
- sep = '\\'
- altsep = '/'
- curdir = '.'
- try:
- drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths]
- split_paths = [p.split(sep) for d, r, p in drivesplits]
- if len({r for d, r, p in drivesplits}) != 1:
- raise ValueError("Can't mix absolute and relative paths")
- # Check that all drive letters or UNC paths match. The check is made only
- # now otherwise type errors for mixing strings and bytes would not be
- # caught.
- if len({d for d, r, p in drivesplits}) != 1:
- raise ValueError("Paths don't have the same drive")
- drive, root, path = splitroot(paths[0].replace(altsep, sep))
- common = path.split(sep)
- common = [c for c in common if c and c != curdir]
- split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
- s1 = min(split_paths)
- s2 = max(split_paths)
- for i, c in enumerate(s1):
- if c != s2[i]:
- common = common[:i]
- break
- else:
- common = common[:len(s1)]
- return drive + root + sep.join(common)
- except (TypeError, AttributeError):
- genericpath._check_arg_types('commonpath', *paths)
- raise
- try:
- # The isdir(), isfile(), islink() and exists() implementations in
- # genericpath use os.stat(). This is overkill on Windows. Use simpler
- # builtin functions if they are available.
- from nt import _path_isdir as isdir
- from nt import _path_isfile as isfile
- from nt import _path_islink as islink
- from nt import _path_exists as exists
- except ImportError:
- # Use genericpath.* as imported above
- pass
- try:
- from nt import _path_isdevdrive
- except ImportError:
- def isdevdrive(path):
- """Determines whether the specified path is on a Windows Dev Drive."""
- # Never a Dev Drive
- return False
- else:
- def isdevdrive(path):
- """Determines whether the specified path is on a Windows Dev Drive."""
- try:
- return _path_isdevdrive(abspath(path))
- except OSError:
- return False
|