Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
posixpath.py593 linesDownload Raw Back to Lib
1"""Common operations on Posix pathnames.2 3Instead of importing this module directly, import os and refer to4this module as os.path.  The "os.path" name is an alias for this5module on Posix systems; on other systems (e.g. Windows),6os.path provides the same operations in a manner specific to that7platform, and is an alias to another module (e.g. ntpath).8 9Some of this can actually be useful on non-Posix systems too, e.g.10for manipulation of the pathname component of URLs.11"""12 13# Strings representing various path-related bits and pieces.14# These are primarily for export; internally, they are hardcoded.15# Should be set before imports for resolving cyclic dependency.16curdir = '.'17pardir = '..'18extsep = '.'19sep = '/'20pathsep = ':'21defpath = '/bin:/usr/bin'22altsep = None23devnull = '/dev/null'24 25import errno26import os27import sys28import stat29import genericpath30from genericpath import *31 32__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",33           "basename","dirname","commonprefix","getsize","getmtime",34           "getatime","getctime","islink","exists","lexists","isdir","isfile",35           "ismount", "expanduser","expandvars","normpath","abspath",36           "samefile","sameopenfile","samestat",37           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",38           "devnull","realpath","supports_unicode_filenames","relpath",39           "commonpath", "isjunction","isdevdrive","ALLOW_MISSING"]40 41 42def _get_sep(path):43    if isinstance(path, bytes):44        return b'/'45    else:46        return '/'47 48# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.49# On MS-DOS this may also turn slashes into backslashes; however, other50# normalizations (such as optimizing '../' away) are not allowed51# (another function should be defined to do that).52 53def normcase(s):54    """Normalize case of pathname.  Has no effect under Posix"""55    return os.fspath(s)56 57 58# Return whether a path is absolute.59# Trivial in Posix, harder on the Mac or MS-DOS.60 61def isabs(s):62    """Test whether a path is absolute"""63    s = os.fspath(s)64    sep = _get_sep(s)65    return s.startswith(sep)66 67 68# Join pathnames.69# Ignore the previous parts if a part is absolute.70# Insert a '/' unless the first part is empty or already ends in '/'.71 72def join(a, *p):73    """Join two or more pathname components, inserting '/' as needed.74    If any component is an absolute path, all previous path components75    will be discarded.  An empty last part will result in a path that76    ends with a separator."""77    a = os.fspath(a)78    sep = _get_sep(a)79    path = a80    try:81        for b in p:82            b = os.fspath(b)83            if b.startswith(sep) or not path:84                path = b85            elif path.endswith(sep):86                path += b87            else:88                path += sep + b89    except (TypeError, AttributeError, BytesWarning):90        genericpath._check_arg_types('join', a, *p)91        raise92    return path93 94 95# Split a path in head (everything up to the last '/') and tail (the96# rest).  If the path ends in '/', tail will be empty.  If there is no97# '/' in the path, head  will be empty.98# Trailing '/'es are stripped from head unless it is the root.99 100def split(p):101    """Split a pathname.  Returns tuple "(head, tail)" where "tail" is102    everything after the final slash.  Either part may be empty."""103    p = os.fspath(p)104    sep = _get_sep(p)105    i = p.rfind(sep) + 1106    head, tail = p[:i], p[i:]107    if head and head != sep*len(head):108        head = head.rstrip(sep)109    return head, tail110 111 112# Split a path in root and extension.113# The extension is everything starting at the last dot in the last114# pathname component; the root is everything before that.115# It is always true that root + ext == p.116 117def splitext(p):118    p = os.fspath(p)119    if isinstance(p, bytes):120        sep = b'/'121        extsep = b'.'122    else:123        sep = '/'124        extsep = '.'125    return genericpath._splitext(p, sep, None, extsep)126splitext.__doc__ = genericpath._splitext.__doc__127 128# Split a pathname into a drive specification and the rest of the129# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.130 131def splitdrive(p):132    """Split a pathname into drive and path. On Posix, drive is always133    empty."""134    p = os.fspath(p)135    return p[:0], p136 137 138try:139    from posix import _path_splitroot_ex as splitroot140except ImportError:141    def splitroot(p):142        """Split a pathname into drive, root and tail.143 144        The tail contains anything after the root."""145        p = os.fspath(p)146        if isinstance(p, bytes):147            sep = b'/'148            empty = b''149        else:150            sep = '/'151            empty = ''152        if p[:1] != sep:153            # Relative path, e.g.: 'foo'154            return empty, empty, p155        elif p[1:2] != sep or p[2:3] == sep:156            # Absolute path, e.g.: '/foo', '///foo', '////foo', etc.157            return empty, sep, p[1:]158        else:159            # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see160            # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13161            return empty, p[:2], p[2:]162 163 164# Return the tail (basename) part of a path, same as split(path)[1].165 166def basename(p):167    """Returns the final component of a pathname"""168    p = os.fspath(p)169    sep = _get_sep(p)170    i = p.rfind(sep) + 1171    return p[i:]172 173 174# Return the head (dirname) part of a path, same as split(path)[0].175 176def dirname(p):177    """Returns the directory component of a pathname"""178    p = os.fspath(p)179    sep = _get_sep(p)180    i = p.rfind(sep) + 1181    head = p[:i]182    if head and head != sep*len(head):183        head = head.rstrip(sep)184    return head185 186 187# Is a path a mount point?188# (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)189 190def ismount(path):191    """Test whether a path is a mount point"""192    try:193        s1 = os.lstat(path)194    except (OSError, ValueError):195        # It doesn't exist -- so not a mount point. :-)196        return False197    else:198        # A symlink can never be a mount point199        if stat.S_ISLNK(s1.st_mode):200            return False201 202    path = os.fspath(path)203    if isinstance(path, bytes):204        parent = join(path, b'..')205    else:206        parent = join(path, '..')207    try:208        s2 = os.lstat(parent)209    except OSError:210        parent = realpath(parent)211        try:212            s2 = os.lstat(parent)213        except OSError:214            return False215 216    # path/.. on a different device as path or the same i-node as path217    return s1.st_dev != s2.st_dev or s1.st_ino == s2.st_ino218 219 220# Expand paths beginning with '~' or '~user'.221# '~' means $HOME; '~user' means that user's home directory.222# If the path doesn't begin with '~', or if the user or $HOME is unknown,223# the path is returned unchanged (leaving error reporting to whatever224# function is called with the expanded path as argument).225# See also module 'glob' for expansion of *, ? and [...] in pathnames.226# (A function should also be defined to do full *sh-style environment227# variable expansion.)228 229def expanduser(path):230    """Expand ~ and ~user constructions.  If user or $HOME is unknown,231    do nothing."""232    path = os.fspath(path)233    if isinstance(path, bytes):234        tilde = b'~'235    else:236        tilde = '~'237    if not path.startswith(tilde):238        return path239    sep = _get_sep(path)240    i = path.find(sep, 1)241    if i < 0:242        i = len(path)243    if i == 1:244        if 'HOME' not in os.environ:245            try:246                import pwd247            except ImportError:248                # pwd module unavailable, return path unchanged249                return path250            try:251                userhome = pwd.getpwuid(os.getuid()).pw_dir252            except KeyError:253                # bpo-10496: if the current user identifier doesn't exist in the254                # password database, return the path unchanged255                return path256        else:257            userhome = os.environ['HOME']258    else:259        try:260            import pwd261        except ImportError:262            # pwd module unavailable, return path unchanged263            return path264        name = path[1:i]265        if isinstance(name, bytes):266            name = os.fsdecode(name)267        try:268            pwent = pwd.getpwnam(name)269        except KeyError:270            # bpo-10496: if the user name from the path doesn't exist in the271            # password database, return the path unchanged272            return path273        userhome = pwent.pw_dir274    # if no user home, return the path unchanged on VxWorks275    if userhome is None and sys.platform == "vxworks":276        return path277    if isinstance(path, bytes):278        userhome = os.fsencode(userhome)279    userhome = userhome.rstrip(sep)280    return (userhome + path[i:]) or sep281 282 283# Expand paths containing shell variable substitutions.284# This expands the forms $variable and ${variable} only.285# Non-existent variables are left unchanged.286 287_varpattern = r'\$(\w+|\{[^}]*\}?)'288_varsub = None289_varsubb = None290 291def expandvars(path):292    """Expand shell variables of form $var and ${var}.  Unknown variables293    are left unchanged."""294    path = os.fspath(path)295    global _varsub, _varsubb296    if isinstance(path, bytes):297        if b'$' not in path:298            return path299        if not _varsubb:300            import re301            _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub302        sub = _varsubb303        start = b'{'304        end = b'}'305        environ = getattr(os, 'environb', None)306    else:307        if '$' not in path:308            return path309        if not _varsub:310            import re311            _varsub = re.compile(_varpattern, re.ASCII).sub312        sub = _varsub313        start = '{'314        end = '}'315        environ = os.environ316 317    def repl(m):318        name = m[1]319        if name.startswith(start):320            if not name.endswith(end):321                return m[0]322            name = name[1:-1]323        try:324            if environ is None:325                value = os.fsencode(os.environ[os.fsdecode(name)])326            else:327                value = environ[name]328        except KeyError:329            return m[0]330        else:331            return value332 333    return sub(repl, path)334 335 336# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.337# It should be understood that this may change the meaning of the path338# if it contains symbolic links!339 340try:341    from posix import _path_normpath as normpath342 343except ImportError:344    def normpath(path):345        """Normalize path, eliminating double slashes, etc."""346        path = os.fspath(path)347        if isinstance(path, bytes):348            sep = b'/'349            dot = b'.'350            dotdot = b'..'351        else:352            sep = '/'353            dot = '.'354            dotdot = '..'355        if not path:356            return dot357        _, initial_slashes, path = splitroot(path)358        comps = path.split(sep)359        new_comps = []360        for comp in comps:361            if not comp or comp == dot:362                continue363            if (comp != dotdot or (not initial_slashes and not new_comps) or364                 (new_comps and new_comps[-1] == dotdot)):365                new_comps.append(comp)366            elif new_comps:367                new_comps.pop()368        comps = new_comps369        path = initial_slashes + sep.join(comps)370        return path or dot371 372 373def abspath(path):374    """Return an absolute path."""375    path = os.fspath(path)376    if isinstance(path, bytes):377        if not path.startswith(b'/'):378            path = join(os.getcwdb(), path)379    else:380        if not path.startswith('/'):381            path = join(os.getcwd(), path)382    return normpath(path)383 384 385# Return a canonical path (i.e. the absolute location of a file on the386# filesystem).387 388def realpath(filename, *, strict=False):389    """Return the canonical path of the specified filename, eliminating any390symbolic links encountered in the path."""391    filename = os.fspath(filename)392    if isinstance(filename, bytes):393        sep = b'/'394        curdir = b'.'395        pardir = b'..'396        getcwd = os.getcwdb397    else:398        sep = '/'399        curdir = '.'400        pardir = '..'401        getcwd = os.getcwd402    if strict is ALLOW_MISSING:403        ignored_error = FileNotFoundError404        strict = True405    elif strict:406        ignored_error = ()407    else:408        ignored_error = OSError409 410    lstat = os.lstat411    readlink = os.readlink412    maxlinks = None413 414    # The stack of unresolved path parts. When popped, a special value of None415    # indicates that a symlink target has been resolved, and that the original416    # symlink path can be retrieved by popping again. The [::-1] slice is a417    # very fast way of spelling list(reversed(...)).418    rest = filename.split(sep)[::-1]419 420    # Number of unprocessed parts in 'rest'. This can differ from len(rest)421    # later, because 'rest' might contain markers for unresolved symlinks.422    part_count = len(rest)423 424    # The resolved path, which is absolute throughout this function.425    # Note: getcwd() returns a normalized and symlink-free path.426    path = sep if filename.startswith(sep) else getcwd()427 428    # Mapping from symlink paths to *fully resolved* symlink targets. If a429    # symlink is encountered but not yet resolved, the value is None. This is430    # used both to detect symlink loops and to speed up repeated traversals of431    # the same links.432    seen = {}433 434    # Number of symlinks traversed. When the number of traversals is limited435    # by *maxlinks*, this is used instead of *seen* to detect symlink loops.436    link_count = 0437 438    while part_count:439        name = rest.pop()440        if name is None:441            # resolved symlink target442            seen[rest.pop()] = path443            continue444        part_count -= 1445        if not name or name == curdir:446            # current dir447            continue448        if name == pardir:449            # parent dir450            path = path[:path.rindex(sep)] or sep451            continue452        if path == sep:453            newpath = path + name454        else:455            newpath = path + sep + name456        try:457            st_mode = lstat(newpath).st_mode458            if not stat.S_ISLNK(st_mode):459                if strict and part_count and not stat.S_ISDIR(st_mode):460                    raise OSError(errno.ENOTDIR, os.strerror(errno.ENOTDIR),461                                  newpath)462                path = newpath463                continue464            elif maxlinks is not None:465                link_count += 1466                if link_count > maxlinks:467                    if strict:468                        raise OSError(errno.ELOOP, os.strerror(errno.ELOOP),469                                      newpath)470                    path = newpath471                    continue472            elif newpath in seen:473                # Already seen this path474                path = seen[newpath]475                if path is not None:476                    # use cached value477                    continue478                # The symlink is not resolved, so we must have a symlink loop.479                if strict:480                    raise OSError(errno.ELOOP, os.strerror(errno.ELOOP),481                                  newpath)482                path = newpath483                continue484            target = readlink(newpath)485        except ignored_error:486            pass487        else:488            # Resolve the symbolic link489            if target.startswith(sep):490                # Symlink target is absolute; reset resolved path.491                path = sep492            if maxlinks is None:493                # Mark this symlink as seen but not fully resolved.494                seen[newpath] = None495                # Push the symlink path onto the stack, and signal its specialness496                # by also pushing None. When these entries are popped, we'll497                # record the fully-resolved symlink target in the 'seen' mapping.498                rest.append(newpath)499                rest.append(None)500            # Push the unresolved symlink target parts onto the stack.501            target_parts = target.split(sep)[::-1]502            rest.extend(target_parts)503            part_count += len(target_parts)504            continue505        # An error occurred and was ignored.506        path = newpath507 508    return path509 510 511supports_unicode_filenames = (sys.platform == 'darwin')512 513def relpath(path, start=None):514    """Return a relative version of a path"""515 516    path = os.fspath(path)517    if not path:518        raise ValueError("no path specified")519 520    if isinstance(path, bytes):521        curdir = b'.'522        sep = b'/'523        pardir = b'..'524    else:525        curdir = '.'526        sep = '/'527        pardir = '..'528 529    if start is None:530        start = curdir531    else:532        start = os.fspath(start)533 534    try:535        start_tail = abspath(start).lstrip(sep)536        path_tail = abspath(path).lstrip(sep)537        start_list = start_tail.split(sep) if start_tail else []538        path_list = path_tail.split(sep) if path_tail else []539        # Work out how much of the filepath is shared by start and path.540        i = len(commonprefix([start_list, path_list]))541 542        rel_list = [pardir] * (len(start_list)-i) + path_list[i:]543        if not rel_list:544            return curdir545        return sep.join(rel_list)546    except (TypeError, AttributeError, BytesWarning, DeprecationWarning):547        genericpath._check_arg_types('relpath', path, start)548        raise549 550 551# Return the longest common sub-path of the sequence of paths given as input.552# The paths are not normalized before comparing them (this is the553# responsibility of the caller). Any trailing separator is stripped from the554# returned path.555 556def commonpath(paths):557    """Given a sequence of path names, returns the longest common sub-path."""558 559    paths = tuple(map(os.fspath, paths))560 561    if not paths:562        raise ValueError('commonpath() arg is an empty sequence')563 564    if isinstance(paths[0], bytes):565        sep = b'/'566        curdir = b'.'567    else:568        sep = '/'569        curdir = '.'570 571    try:572        split_paths = [path.split(sep) for path in paths]573 574        try:575            isabs, = {p.startswith(sep) for p in paths}576        except ValueError:577            raise ValueError("Can't mix absolute and relative paths") from None578 579        split_paths = [[c for c in s if c and c != curdir] for s in split_paths]580        s1 = min(split_paths)581        s2 = max(split_paths)582        common = s1583        for i, c in enumerate(s1):584            if c != s2[i]:585                common = s1[:i]586                break587 588        prefix = sep if isabs else sep[:0]589        return prefix + sep.join(common)590    except (TypeError, AttributeError):591        genericpath._check_arg_types('commonpath', *paths)592        raise593 
codekingpro/portable-devtools · Team Ai