Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ntpath.py871 linesDownload Raw Back to Lib
1# Module 'ntpath' -- common operations on WinNT/Win95 pathnames2"""Common pathname manipulations, WindowsNT/95 version.3 4Instead of importing this module directly, import os and refer to this5module as os.path.6"""7 8# strings representing various path-related bits and pieces9# These are primarily for export; internally, they are hardcoded.10# Should be set before imports for resolving cyclic dependency.11curdir = '.'12pardir = '..'13extsep = '.'14sep = '\\'15pathsep = ';'16altsep = '/'17defpath = '.;C:\\bin'18devnull = 'nul'19 20import os21import sys22import genericpath23from genericpath import *24 25__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",26           "basename","dirname","commonprefix","getsize","getmtime",27           "getatime","getctime", "islink","exists","lexists","isdir","isfile",28           "ismount","isreserved","expanduser","expandvars","normpath",29           "abspath","curdir","pardir","sep","pathsep","defpath","altsep",30           "extsep","devnull","realpath","supports_unicode_filenames","relpath",31           "samefile", "sameopenfile", "samestat", "commonpath", "isjunction",32           "isdevdrive", "ALLOW_MISSING"]33 34def _get_bothseps(path):35    if isinstance(path, bytes):36        return b'\\/'37    else:38        return '\\/'39 40# Normalize the case of a pathname and map slashes to backslashes.41# Other normalizations (such as optimizing '../' away) are not done42# (this is done by normpath).43 44try:45    from _winapi import (46        LCMapStringEx as _LCMapStringEx,47        LOCALE_NAME_INVARIANT as _LOCALE_NAME_INVARIANT,48        LCMAP_LOWERCASE as _LCMAP_LOWERCASE)49 50    def normcase(s):51        """Normalize case of pathname.52 53        Makes all characters lowercase and all slashes into backslashes.54        """55        s = os.fspath(s)56        if not s:57            return s58        if isinstance(s, bytes):59            encoding = sys.getfilesystemencoding()60            s = s.decode(encoding, 'surrogateescape').replace('/', '\\')61            s = _LCMapStringEx(_LOCALE_NAME_INVARIANT,62                               _LCMAP_LOWERCASE, s)63            return s.encode(encoding, 'surrogateescape')64        else:65            return _LCMapStringEx(_LOCALE_NAME_INVARIANT,66                                  _LCMAP_LOWERCASE,67                                  s.replace('/', '\\'))68except ImportError:69    def normcase(s):70        """Normalize case of pathname.71 72        Makes all characters lowercase and all slashes into backslashes.73        """74        s = os.fspath(s)75        if isinstance(s, bytes):76            return os.fsencode(os.fsdecode(s).replace('/', '\\').lower())77        return s.replace('/', '\\').lower()78 79 80def isabs(s):81    """Test whether a path is absolute"""82    s = os.fspath(s)83    if isinstance(s, bytes):84        sep = b'\\'85        altsep = b'/'86        colon_sep = b':\\'87        double_sep = b'\\\\'88    else:89        sep = '\\'90        altsep = '/'91        colon_sep = ':\\'92        double_sep = '\\\\'93    s = s[:3].replace(altsep, sep)94    # Absolute: UNC, device, and paths with a drive and root.95    return s.startswith(colon_sep, 1) or s.startswith(double_sep)96 97 98# Join two (or more) paths.99def join(path, *paths):100    path = os.fspath(path)101    if isinstance(path, bytes):102        sep = b'\\'103        seps = b'\\/'104        colon_seps = b':\\/'105    else:106        sep = '\\'107        seps = '\\/'108        colon_seps = ':\\/'109    try:110        result_drive, result_root, result_path = splitroot(path)111        for p in paths:112            p_drive, p_root, p_path = splitroot(p)113            if p_root:114                # Second path is absolute115                if p_drive or not result_drive:116                    result_drive = p_drive117                result_root = p_root118                result_path = p_path119                continue120            elif p_drive and p_drive != result_drive:121                if p_drive.lower() != result_drive.lower():122                    # Different drives => ignore the first path entirely123                    result_drive = p_drive124                    result_root = p_root125                    result_path = p_path126                    continue127                # Same drive in different case128                result_drive = p_drive129            # Second path is relative to the first130            if result_path and result_path[-1] not in seps:131                result_path = result_path + sep132            result_path = result_path + p_path133        ## add separator between UNC and non-absolute path134        if (result_path and not result_root and135            result_drive and result_drive[-1] not in colon_seps):136            return result_drive + sep + result_path137        return result_drive + result_root + result_path138    except (TypeError, AttributeError, BytesWarning):139        genericpath._check_arg_types('join', path, *paths)140        raise141 142 143# Split a path in a drive specification (a drive letter followed by a144# colon) and the path specification.145# It is always true that drivespec + pathspec == p146def splitdrive(p):147    """Split a pathname into drive/UNC sharepoint and relative path specifiers.148    Returns a 2-tuple (drive_or_unc, path); either part may be empty.149 150    If you assign151        result = splitdrive(p)152    It is always true that:153        result[0] + result[1] == p154 155    If the path contained a drive letter, drive_or_unc will contain everything156    up to and including the colon.  e.g. splitdrive("c:/dir") returns ("c:", "/dir")157 158    If the path contained a UNC path, the drive_or_unc will contain the host name159    and share up to but not including the fourth directory separator character.160    e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")161 162    Paths cannot contain both a drive letter and a UNC path.163 164    """165    drive, root, tail = splitroot(p)166    return drive, root + tail167 168 169try:170    from nt import _path_splitroot_ex as splitroot171except ImportError:172    def splitroot(p):173        """Split a pathname into drive, root and tail.174 175        The tail contains anything after the root."""176        p = os.fspath(p)177        if isinstance(p, bytes):178            sep = b'\\'179            altsep = b'/'180            colon = b':'181            unc_prefix = b'\\\\?\\UNC\\'182            empty = b''183        else:184            sep = '\\'185            altsep = '/'186            colon = ':'187            unc_prefix = '\\\\?\\UNC\\'188            empty = ''189        normp = p.replace(altsep, sep)190        if normp[:1] == sep:191            if normp[1:2] == sep:192                # UNC drives, e.g. \\server\share or \\?\UNC\server\share193                # Device drives, e.g. \\.\device or \\?\device194                start = 8 if normp[:8].upper() == unc_prefix else 2195                index = normp.find(sep, start)196                if index == -1:197                    return p, empty, empty198                index2 = normp.find(sep, index + 1)199                if index2 == -1:200                    return p, empty, empty201                return p[:index2], p[index2:index2 + 1], p[index2 + 1:]202            else:203                # Relative path with root, e.g. \Windows204                return empty, p[:1], p[1:]205        elif normp[1:2] == colon:206            if normp[2:3] == sep:207                # Absolute drive-letter path, e.g. X:\Windows208                return p[:2], p[2:3], p[3:]209            else:210                # Relative path with drive, e.g. X:Windows211                return p[:2], empty, p[2:]212        else:213            # Relative path, e.g. Windows214            return empty, empty, p215 216 217# Split a path in head (everything up to the last '/') and tail (the218# rest).  After the trailing '/' is stripped, the invariant219# join(head, tail) == p holds.220# The resulting head won't end in '/' unless it is the root.221 222def split(p):223    """Split a pathname.224 225    Return tuple (head, tail) where tail is everything after the final slash.226    Either part may be empty."""227    p = os.fspath(p)228    seps = _get_bothseps(p)229    d, r, p = splitroot(p)230    # set i to index beyond p's last slash231    i = len(p)232    while i and p[i-1] not in seps:233        i -= 1234    head, tail = p[:i], p[i:]  # now tail has no slashes235    return d + r + head.rstrip(seps), tail236 237 238# Split a path in root and extension.239# The extension is everything starting at the last dot in the last240# pathname component; the root is everything before that.241# It is always true that root + ext == p.242 243def splitext(p):244    p = os.fspath(p)245    if isinstance(p, bytes):246        return genericpath._splitext(p, b'\\', b'/', b'.')247    else:248        return genericpath._splitext(p, '\\', '/', '.')249splitext.__doc__ = genericpath._splitext.__doc__250 251 252# Return the tail (basename) part of a path.253 254def basename(p):255    """Returns the final component of a pathname"""256    return split(p)[1]257 258 259# Return the head (dirname) part of a path.260 261def dirname(p):262    """Returns the directory component of a pathname"""263    return split(p)[0]264 265 266# Is a path a mount point?267# Any drive letter root (eg c:\)268# Any share UNC (eg \\server\share)269# Any volume mounted on a filesystem folder270#271# No one method detects all three situations. Historically we've lexically272# detected drive letter roots and share UNCs. The canonical approach to273# detecting mounted volumes (querying the reparse tag) fails for the most274# common case: drive letter roots. The alternative which uses GetVolumePathName275# fails if the drive letter is the result of a SUBST.276try:277    from nt import _getvolumepathname278except ImportError:279    _getvolumepathname = None280def ismount(path):281    """Test whether a path is a mount point (a drive root, the root of a282    share, or a mounted volume)"""283    path = os.fspath(path)284    seps = _get_bothseps(path)285    path = abspath(path)286    drive, root, rest = splitroot(path)287    if drive and drive[0] in seps:288        return not rest289    if root and not rest:290        return True291 292    if _getvolumepathname:293        x = path.rstrip(seps)294        y =_getvolumepathname(path).rstrip(seps)295        return x.casefold() == y.casefold()296    else:297        return False298 299 300_reserved_chars = frozenset(301    {chr(i) for i in range(32)} |302    {'"', '*', ':', '<', '>', '?', '|', '/', '\\'}303)304 305_reserved_names = frozenset(306    {'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} |307    {f'COM{c}' for c in '123456789\xb9\xb2\xb3'} |308    {f'LPT{c}' for c in '123456789\xb9\xb2\xb3'}309)310 311def isreserved(path):312    """Return true if the pathname is reserved by the system."""313    # Refer to "Naming Files, Paths, and Namespaces":314    # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file315    path = os.fsdecode(splitroot(path)[2]).replace(altsep, sep)316    return any(_isreservedname(name) for name in reversed(path.split(sep)))317 318def _isreservedname(name):319    """Return true if the filename is reserved by the system."""320    # Trailing dots and spaces are reserved.321    if name[-1:] in ('.', ' '):322        return name not in ('.', '..')323    # Wildcards, separators, colon, and pipe (*?"<>/\:|) are reserved.324    # ASCII control characters (0-31) are reserved.325    # Colon is reserved for file streams (e.g. "name:stream[:type]").326    if _reserved_chars.intersection(name):327        return True328    # DOS device names are reserved (e.g. "nul" or "nul .txt"). The rules329    # are complex and vary across Windows versions. On the side of330    # caution, return True for names that may not be reserved.331    return name.partition('.')[0].rstrip(' ').upper() in _reserved_names332 333 334# Expand paths beginning with '~' or '~user'.335# '~' means $HOME; '~user' means that user's home directory.336# If the path doesn't begin with '~', or if the user or $HOME is unknown,337# the path is returned unchanged (leaving error reporting to whatever338# function is called with the expanded path as argument).339# See also module 'glob' for expansion of *, ? and [...] in pathnames.340# (A function should also be defined to do full *sh-style environment341# variable expansion.)342 343def expanduser(path):344    """Expand ~ and ~user constructs.345 346    If user or $HOME is unknown, do nothing."""347    path = os.fspath(path)348    if isinstance(path, bytes):349        seps = b'\\/'350        tilde = b'~'351    else:352        seps = '\\/'353        tilde = '~'354    if not path.startswith(tilde):355        return path356    i, n = 1, len(path)357    while i < n and path[i] not in seps:358        i += 1359 360    if 'USERPROFILE' in os.environ:361        userhome = os.environ['USERPROFILE']362    elif 'HOMEPATH' not in os.environ:363        return path364    else:365        drive = os.environ.get('HOMEDRIVE', '')366        userhome = join(drive, os.environ['HOMEPATH'])367 368    if i != 1: #~user369        target_user = path[1:i]370        if isinstance(target_user, bytes):371            target_user = os.fsdecode(target_user)372        current_user = os.environ.get('USERNAME')373 374        if target_user != current_user:375            # Try to guess user home directory.  By default all user376            # profile directories are located in the same place and are377            # named by corresponding usernames.  If userhome isn't a378            # normal profile directory, this guess is likely wrong,379            # so we bail out.380            if current_user != basename(userhome):381                return path382            userhome = join(dirname(userhome), target_user)383 384    if isinstance(path, bytes):385        userhome = os.fsencode(userhome)386 387    return userhome + path[i:]388 389 390# Expand paths containing shell variable substitutions.391# The following rules apply:392#       - no expansion within single quotes393#       - '$$' is translated into '$'394#       - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%395#       - ${varname} is accepted.396#       - $varname is accepted.397#       - %varname% is accepted.398#       - varnames can be made out of letters, digits and the characters '_-'399#         (though is not verified in the ${varname} and %varname% cases)400# XXX With COMMAND.COM you can use any characters in a variable name,401# XXX except '^|<>='.402 403_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"404_varsub = None405_varsubb = None406 407def expandvars(path):408    """Expand shell variables of the forms $var, ${var} and %var%.409 410    Unknown variables are left unchanged."""411    path = os.fspath(path)412    global _varsub, _varsubb413    if isinstance(path, bytes):414        if b'$' not in path and b'%' not in path:415            return path416        if not _varsubb:417            import re418            _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub419        sub = _varsubb420        percent = b'%'421        brace = b'{'422        rbrace = b'}'423        dollar = b'$'424        environ = getattr(os, 'environb', None)425    else:426        if '$' not in path and '%' not in path:427            return path428        if not _varsub:429            import re430            _varsub = re.compile(_varpattern, re.ASCII).sub431        sub = _varsub432        percent = '%'433        brace = '{'434        rbrace = '}'435        dollar = '$'436        environ = os.environ437 438    def repl(m):439        lastindex = m.lastindex440        if lastindex is None:441            return m[0]442        name = m[lastindex]443        if lastindex == 1:444            if name == percent:445                return name446            if not name.endswith(percent):447                return m[0]448            name = name[:-1]449        else:450            if name == dollar:451                return name452            if name.startswith(brace):453                if not name.endswith(rbrace):454                    return m[0]455                name = name[1:-1]456 457        try:458            if environ is None:459                return os.fsencode(os.environ[os.fsdecode(name)])460            else:461                return environ[name]462        except KeyError:463            return m[0]464 465    return sub(repl, path)466 467 468# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.469# Previously, this function also truncated pathnames to 8+3 format,470# but as this module is called "ntpath", that's obviously wrong!471try:472    from nt import _path_normpath as normpath473 474except ImportError:475    def normpath(path):476        """Normalize path, eliminating double slashes, etc."""477        path = os.fspath(path)478        if isinstance(path, bytes):479            sep = b'\\'480            altsep = b'/'481            curdir = b'.'482            pardir = b'..'483        else:484            sep = '\\'485            altsep = '/'486            curdir = '.'487            pardir = '..'488        path = path.replace(altsep, sep)489        drive, root, path = splitroot(path)490        prefix = drive + root491        comps = path.split(sep)492        i = 0493        while i < len(comps):494            if not comps[i] or comps[i] == curdir:495                del comps[i]496            elif comps[i] == pardir:497                if i > 0 and comps[i-1] != pardir:498                    del comps[i-1:i+1]499                    i -= 1500                elif i == 0 and root:501                    del comps[i]502                else:503                    i += 1504            else:505                i += 1506        # If the path is now empty, substitute '.'507        if not prefix and not comps:508            comps.append(curdir)509        return prefix + sep.join(comps)510 511 512# Return an absolute path.513try:514    from nt import _getfullpathname515 516except ImportError: # not running on Windows - mock up something sensible517    def abspath(path):518        """Return the absolute version of a path."""519        path = os.fspath(path)520        if not isabs(path):521            if isinstance(path, bytes):522                cwd = os.getcwdb()523            else:524                cwd = os.getcwd()525            path = join(cwd, path)526        return normpath(path)527 528else:  # use native Windows method on Windows529    def abspath(path):530        """Return the absolute version of a path."""531        try:532            return _getfullpathname(normpath(path))533        except (OSError, ValueError):534            # See gh-75230, handle outside for cleaner traceback535            pass536        path = os.fspath(path)537        if not isabs(path):538            if isinstance(path, bytes):539                sep = b'\\'540                getcwd = os.getcwdb541            else:542                sep = '\\'543                getcwd = os.getcwd544            drive, root, path = splitroot(path)545            # Either drive or root can be nonempty, but not both.546            if drive or root:547                try:548                    path = join(_getfullpathname(drive + root), path)549                except (OSError, ValueError):550                    # Drive "\0:" cannot exist; use the root directory.551                    path = drive + sep + path552            else:553                path = join(getcwd(), path)554        return normpath(path)555 556try:557    from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink558except ImportError:559    # realpath is a no-op on systems without _getfinalpathname support.560    def realpath(path, *, strict=False):561        return abspath(path)562else:563    def _readlink_deep(path, ignored_error=OSError):564        # These error codes indicate that we should stop reading links and565        # return the path we currently have.566        # 1: ERROR_INVALID_FUNCTION567        # 2: ERROR_FILE_NOT_FOUND568        # 3: ERROR_DIRECTORY_NOT_FOUND569        # 5: ERROR_ACCESS_DENIED570        # 21: ERROR_NOT_READY (implies drive with no media)571        # 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)572        # 50: ERROR_NOT_SUPPORTED (implies no support for reparse points)573        # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)574        # 87: ERROR_INVALID_PARAMETER575        # 4390: ERROR_NOT_A_REPARSE_POINT576        # 4392: ERROR_INVALID_REPARSE_DATA577        # 4393: ERROR_REPARSE_TAG_INVALID578        allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 4390, 4392, 4393579 580        seen = set()581        while normcase(path) not in seen:582            seen.add(normcase(path))583            try:584                old_path = path585                path = _nt_readlink(path)586                # Links may be relative, so resolve them against their587                # own location588                if not isabs(path):589                    # If it's something other than a symlink, we don't know590                    # what it's actually going to be resolved against, so591                    # just return the old path.592                    if not islink(old_path):593                        path = old_path594                        break595                    path = normpath(join(dirname(old_path), path))596            except ignored_error as ex:597                if ex.winerror in allowed_winerror:598                    break599                raise600            except ValueError:601                # Stop on reparse points that are not symlinks602                break603        return path604 605    def _getfinalpathname_nonstrict(path, ignored_error=OSError):606        # These error codes indicate that we should stop resolving the path607        # and return the value we currently have.608        # 1: ERROR_INVALID_FUNCTION609        # 2: ERROR_FILE_NOT_FOUND610        # 3: ERROR_DIRECTORY_NOT_FOUND611        # 5: ERROR_ACCESS_DENIED612        # 21: ERROR_NOT_READY (implies drive with no media)613        # 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)614        # 50: ERROR_NOT_SUPPORTED615        # 53: ERROR_BAD_NETPATH616        # 65: ERROR_NETWORK_ACCESS_DENIED617        # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)618        # 87: ERROR_INVALID_PARAMETER619        # 123: ERROR_INVALID_NAME620        # 161: ERROR_BAD_PATHNAME621        # 1005: ERROR_UNRECOGNIZED_VOLUME622        # 1920: ERROR_CANT_ACCESS_FILE623        # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)624        allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1005, 1920, 1921625 626        # Non-strict algorithm is to find as much of the target directory627        # as we can and join the rest.628        tail = path[:0]629        while path:630            try:631                path = _getfinalpathname(path)632                return join(path, tail) if tail else path633            except ignored_error as ex:634                if ex.winerror not in allowed_winerror:635                    raise636                try:637                    # The OS could not resolve this path fully, so we attempt638                    # to follow the link ourselves. If we succeed, join the tail639                    # and return.640                    new_path = _readlink_deep(path,641                                              ignored_error=ignored_error)642                    if new_path != path:643                        return join(new_path, tail) if tail else new_path644                except ignored_error:645                    # If we fail to readlink(), let's keep traversing646                    pass647                # If we get these errors, try to get the real name of the file without accessing it.648                if ex.winerror in (1, 5, 32, 50, 87, 1920, 1921):649                    try:650                        name = _findfirstfile(path)651                        path, _ = split(path)652                    except ignored_error:653                        path, name = split(path)654                else:655                    path, name = split(path)656                if path and not name:657                    return path + tail658                tail = join(name, tail) if tail else name659        return tail660 661    def realpath(path, *, strict=False):662        path = normpath(path)663        if isinstance(path, bytes):664            prefix = b'\\\\?\\'665            unc_prefix = b'\\\\?\\UNC\\'666            new_unc_prefix = b'\\\\'667            cwd = os.getcwdb()668            # bpo-38081: Special case for realpath(b'nul')669            devnull = b'nul'670            if normcase(path) == devnull:671                return b'\\\\.\\NUL'672        else:673            prefix = '\\\\?\\'674            unc_prefix = '\\\\?\\UNC\\'675            new_unc_prefix = '\\\\'676            cwd = os.getcwd()677            # bpo-38081: Special case for realpath('nul')678            devnull = 'nul'679            if normcase(path) == devnull:680                return '\\\\.\\NUL'681        had_prefix = path.startswith(prefix)682 683        if strict is ALLOW_MISSING:684            ignored_error = FileNotFoundError685            strict = True686        elif strict:687            ignored_error = ()688        else:689            ignored_error = OSError690 691        if not had_prefix and not isabs(path):692            path = join(cwd, path)693        try:694            path = _getfinalpathname(path)695            initial_winerror = 0696        except ValueError as ex:697            # gh-106242: Raised for embedded null characters698            # In strict modes, we convert into an OSError.699            # Non-strict mode returns the path as-is, since we've already700            # made it absolute.701            if strict:702                raise OSError(str(ex)) from None703            path = normpath(path)704        except ignored_error as ex:705            initial_winerror = ex.winerror706            path = _getfinalpathname_nonstrict(path,707                                               ignored_error=ignored_error)708        # The path returned by _getfinalpathname will always start with \\?\ -709        # strip off that prefix unless it was already provided on the original710        # path.711        if not had_prefix and path.startswith(prefix):712            # For UNC paths, the prefix will actually be \\?\UNC\713            # Handle that case as well.714            if path.startswith(unc_prefix):715                spath = new_unc_prefix + path[len(unc_prefix):]716            else:717                spath = path[len(prefix):]718            # Ensure that the non-prefixed path resolves to the same path719            try:720                if _getfinalpathname(spath) == path:721                    path = spath722            except ValueError as ex:723                # Unexpected, as an invalid path should not have gained a prefix724                # at any point, but we ignore this error just in case.725                pass726            except OSError as ex:727                # If the path does not exist and originally did not exist, then728                # strip the prefix anyway.729                if ex.winerror == initial_winerror:730                    path = spath731        return path732 733 734# All supported version have Unicode filename support.735supports_unicode_filenames = True736 737def relpath(path, start=None):738    """Return a relative version of a path"""739    path = os.fspath(path)740    if not path:741        raise ValueError("no path specified")742 743    if isinstance(path, bytes):744        sep = b'\\'745        curdir = b'.'746        pardir = b'..'747    else:748        sep = '\\'749        curdir = '.'750        pardir = '..'751 752    if start is None:753        start = curdir754    else:755        start = os.fspath(start)756 757    try:758        start_abs = abspath(start)759        path_abs = abspath(path)760        start_drive, _, start_rest = splitroot(start_abs)761        path_drive, _, path_rest = splitroot(path_abs)762        if normcase(start_drive) != normcase(path_drive):763            raise ValueError("path is on mount %r, start on mount %r" % (764                path_drive, start_drive))765 766        start_list = start_rest.split(sep) if start_rest else []767        path_list = path_rest.split(sep) if path_rest else []768        # Work out how much of the filepath is shared by start and path.769        i = 0770        for e1, e2 in zip(start_list, path_list):771            if normcase(e1) != normcase(e2):772                break773            i += 1774 775        rel_list = [pardir] * (len(start_list)-i) + path_list[i:]776        if not rel_list:777            return curdir778        return sep.join(rel_list)779    except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):780        genericpath._check_arg_types('relpath', path, start)781        raise782 783 784# Return the longest common sub-path of the iterable of paths given as input.785# The function is case-insensitive and 'separator-insensitive', i.e. if the786# only difference between two paths is the use of '\' versus '/' as separator,787# they are deemed to be equal.788#789# However, the returned path will have the standard '\' separator (even if the790# given paths had the alternative '/' separator) and will have the case of the791# first path given in the iterable. Additionally, any trailing separator is792# stripped from the returned path.793 794def commonpath(paths):795    """Given an iterable of path names, returns the longest common sub-path."""796    paths = tuple(map(os.fspath, paths))797    if not paths:798        raise ValueError('commonpath() arg is an empty iterable')799 800    if isinstance(paths[0], bytes):801        sep = b'\\'802        altsep = b'/'803        curdir = b'.'804    else:805        sep = '\\'806        altsep = '/'807        curdir = '.'808 809    try:810        drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths]811        split_paths = [p.split(sep) for d, r, p in drivesplits]812 813        # Check that all drive letters or UNC paths match. The check is made only814        # now otherwise type errors for mixing strings and bytes would not be815        # caught.816        if len({d for d, r, p in drivesplits}) != 1:817            raise ValueError("Paths don't have the same drive")818 819        drive, root, path = splitroot(paths[0].replace(altsep, sep))820        if len({r for d, r, p in drivesplits}) != 1:821            if drive:822                raise ValueError("Can't mix absolute and relative paths")823            else:824                raise ValueError("Can't mix rooted and not-rooted paths")825 826        common = path.split(sep)827        common = [c for c in common if c and c != curdir]828 829        split_paths = [[c for c in s if c and c != curdir] for s in split_paths]830        s1 = min(split_paths)831        s2 = max(split_paths)832        for i, c in enumerate(s1):833            if c != s2[i]:834                common = common[:i]835                break836        else:837            common = common[:len(s1)]838 839        return drive + root + sep.join(common)840    except (TypeError, AttributeError):841        genericpath._check_arg_types('commonpath', *paths)842        raise843 844 845try:846    # The isdir(), isfile(), islink(), exists() and lexists() implementations847    # in genericpath use os.stat(). This is overkill on Windows. Use simpler848    # builtin functions if they are available.849    from nt import _path_isdir as isdir850    from nt import _path_isfile as isfile851    from nt import _path_islink as islink852    from nt import _path_isjunction as isjunction853    from nt import _path_exists as exists854    from nt import _path_lexists as lexists855except ImportError:856    # Use genericpath.* as imported above857    pass858 859 860try:861    from nt import _path_isdevdrive862    def isdevdrive(path):863        """Determines whether the specified path is on a Windows Dev Drive."""864        try:865            return _path_isdevdrive(abspath(path))866        except OSError:867            return False868except ImportError:869    # Use genericpath.isdevdrive as imported above870    pass871 
codekingpro/portable-devtools · Team Ai