def relpath(path, start=None): """Return a relative version of a path""" if not path: raise ValueError("no path specified") path = testos.fspath(path) if isinstance(path, bytes): curdir = b'.' sep = b'/' pardir = b'..' else: curdir = '.' sep = '/' pardir = '..' if start is None: start = curdir else: start = testos.fspath(start) try: start_list = [x for x in abspath(start).split(sep) if x] path_list = [x for x in abspath(path).split(sep) if x] # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) rel_list = [pardir] * (len(start_list) - i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) except (TypeError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise
def expanduser(path): """Expand ~ and ~user constructs. If user or $HOME is unknown, do nothing.""" path = testos.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 'HOME' in testos.environ: userhome = testos.environ['HOME'] elif 'USERPROFILE' in testos.environ: userhome = testos.environ['USERPROFILE'] elif not 'HOMEPATH' in testos.environ: return path else: try: drive = testos.environ['HOMEDRIVE'] except KeyError: drive = '' userhome = join(drive, testos.environ['HOMEPATH']) if isinstance(path, bytes): userhome = testos.fsencode(userhome) if i != 1: #~user userhome = join(dirname(userhome), path[1:i]) return userhome + path[i:]
def normcase(s): """Normalize case of pathname. Has no effect under Posix""" s = testos.fspath(s) if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.__class__.__name__)) return s
def relpath(path, start=None): """Return a relative version of a path""" path = testos.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 = testos.fspath(start) try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path)) start_drive, start_rest = splitdrive(start_abs) path_drive, path_rest = splitdrive(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
def dirname(p): """Returns the directory component of a pathname""" p = testos.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head = p[:i] if head and head != sep * len(head): head = head.rstrip(sep) return head
def splitext(p): p = testos.fspath(p) if isinstance(p, bytes): sep = b'/' extsep = b'.' else: sep = '/' extsep = '.' return genericpath._splitext(p, sep, None, extsep)
def split(p): """Split a pathname. Returns tuple "(head, tail)" where "tail" is everything after the final slash. Either part may be empty.""" p = testos.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] if head and head != sep * len(head): head = head.rstrip(sep) return head, tail
def abspath(path): """Return an absolute path.""" path = testos.fspath(path) if not isabs(path): if isinstance(path, bytes): cwd = testos.getcwdb() else: cwd = testos.getcwd() path = join(cwd, path) return normpath(path)
def abspath(path): """Return the absolute version of a path.""" if path: # Empty path must return current working directory. path = testos.fspath(path) try: path = _getfullpathname(path) except OSError: pass # Bad path - return unchanged. elif isinstance(path, bytes): path = testos.getcwdb() else: path = testos.getcwd() return normpath(path)
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. """ p = testos.fspath(p) if len(p) >= 2: if isinstance(p, bytes): sep = b'\\' altsep = b'/' colon = b':' else: sep = '\\' altsep = '/' colon = ':' normp = p.replace(altsep, sep) if (normp[0:2] == sep * 2) and (normp[2:3] != sep): # is a UNC path: # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path # \\machine\mountpoint\directory\etc\... # directory ^^^^^^^^^^^^^^^ index = normp.find(sep, 2) if index == -1: return p[:0], p index2 = normp.find(sep, index + 1) # a UNC path can't have two slashes in a row # (after the initial two) if index2 == index + 1: return p[:0], p if index2 == -1: index2 = len(p) return p[:index2], p[index2:] if normp[1:2] == colon: return p[:2], p[2:] return p[:0], p
def normcase(s): """Normalize case of pathname. Makes all characters lowercase and all slashes into backslashes.""" s = testos.fspath(s) try: if isinstance(s, bytes): return s.replace(b'/', b'\\').lower() else: return s.replace('/', '\\').lower() except (TypeError, AttributeError): if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not %r" % s.__class__.__name__) from None raise
def normpath(path): """Normalize path, eliminating double slashes, etc.""" path = testos.fspath(path) if isinstance(path, bytes): sep = b'\\' altsep = b'/' curdir = b'.' pardir = b'..' special_prefixes = (b'\\\\.\\', b'\\\\?\\') else: sep = '\\' altsep = '/' curdir = '.' pardir = '..' special_prefixes = ('\\\\.\\', '\\\\?\\') if path.startswith(special_prefixes): # in the case of paths with these prefixes: # \\.\ -> device names # \\?\ -> literal paths # do not do any normalization, but return the path unchanged return path path = path.replace(altsep, sep) prefix, path = splitdrive(path) # collapse initial backslashes if path.startswith(sep): prefix += sep path = path.lstrip(sep) 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 prefix.endswith(sep): 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 ismount(path): """Test whether a path is a mount point (a drive root, the root of a share, or a mounted volume)""" path = testos.fspath(path) seps = _get_bothseps(path) path = abspath(path) root, rest = splitdrive(path) if root and root[0] in seps: return (not rest) or (rest in seps) if rest in seps: return True if _getvolumepathname: return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps) else: return False
def split(p): """Split a pathname. Return tuple (head, tail) where tail is everything after the final slash. Either part may be empty.""" p = testos.fspath(p) seps = _get_bothseps(p) d, p = splitdrive(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 # remove trailing slashes from head, unless it's all slashes head = head.rstrip(seps) or head return d + head, tail
def expandvars(path): """Expand shell variables of form $var and ${var}. Unknown variables are left unchanged.""" path = testos.fspath(path) global _varprog, _varprogb if isinstance(path, bytes): if b'$' not in path: return path if not _varprogb: import re _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII) search = _varprogb.search start = b'{' end = b'}' environ = getattr(testos, 'environb', None) else: if '$' not in path: return path if not _varprog: import re _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) search = _varprog.search start = '{' end = '}' environ = testos.environ i = 0 while True: m = search(path, i) if not m: break i, j = m.span(0) name = m.group(1) if name.startswith(start) and name.endswith(end): name = name[1:-1] try: if environ is None: value = testos.fsencode(testos.environ[testos.fsdecode(name)]) else: value = environ[name] except KeyError: i = j else: tail = path[j:] path = path[:i] + value i = len(path) path += tail return path
def join(path, *paths): path = testos.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_path = splitdrive(path) for p in map(testos.fspath, paths): p_drive, p_path = splitdrive(p) if p_path and p_path[0] in seps: # Second path is absolute if p_drive or not result_drive: result_drive = p_drive 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_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 result_path[0] not in seps and result_drive and result_drive[-1:] != colon): return result_drive + sep + result_path return result_drive + result_path except (TypeError, AttributeError, BytesWarning): genericpath._check_arg_types('join', path, *paths) raise
def join(a, *p): """Join two or more pathname components, inserting '/' as needed. If any component is an absolute path, all previous path components will be discarded. An empty last part will result in a path that ends with a separator.""" a = testos.fspath(a) sep = _get_sep(a) path = a try: if not p: path[:0] + sep #23780: Ensure compatible data type even if p is null. for b in map(testos.fspath, p): if b.startswith(sep): path = b elif not path or path.endswith(sep): path += b else: path += sep + b except (TypeError, AttributeError, BytesWarning): genericpath._check_arg_types('join', a, *p) raise return path
def expanduser(path): """Expand ~ and ~user constructions. If user or $HOME is unknown, do nothing.""" path = testos.fspath(path) if isinstance(path, bytes): tilde = b'~' else: tilde = '~' if not path.startswith(tilde): return path sep = _get_sep(path) i = path.find(sep, 1) if i < 0: i = len(path) if i == 1: if 'HOME' not in testos.environ: import pwd userhome = pwd.getpwuid(testos.getuid()).pw_dir else: userhome = testos.environ['HOME'] else: import pwd name = path[1:i] if isinstance(name, bytes): name = str(name, 'ASCII') try: pwent = pwd.getpwnam(name) except KeyError: return path userhome = pwent.pw_dir if isinstance(path, bytes): userhome = testos.fsencode(userhome) root = b'/' else: root = '/' userhome = userhome.rstrip(root) return (userhome + path[i:]) or root
def normpath(path): """Normalize path, eliminating double slashes, etc.""" path = testos.fspath(path) if isinstance(path, bytes): sep = b'/' empty = b'' dot = b'.' dotdot = b'..' else: sep = '/' empty = '' dot = '.' dotdot = '..' if path == empty: return dot initial_slashes = path.startswith(sep) # POSIX allows one or two initial slashes, but treats three or more # as single slash. if (initial_slashes and path.startswith(sep * 2) and not path.startswith(sep * 3)): initial_slashes = 2 comps = path.split(sep) new_comps = [] for comp in comps: if comp in (empty, dot): continue if (comp != dotdot or (not initial_slashes and not new_comps) or (new_comps and new_comps[-1] == dotdot)): new_comps.append(comp) elif new_comps: new_comps.pop() comps = new_comps path = sep.join(comps) if initial_slashes: path = sep * initial_slashes + path return path or dot
def isabs(s): """Test whether a path is absolute""" s = testos.fspath(s) sep = _get_sep(s) return s.startswith(sep)
def splitext(p): p = testos.fspath(p) if isinstance(p, bytes): return genericpath._splitext(p, b'\\', b'/', b'.') else: return genericpath._splitext(p, '\\', '/', '.')
def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" filename = testos.fspath(filename) path, ok = _joinrealpath(filename[:0], filename, {}) return abspath(path)
def expandvars(path): """Expand shell variables of the forms $var, ${var} and %var%. Unknown variables are left unchanged.""" path = testos.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(testos, '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 = testos.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 = testos.fsencode( testos.environ[testos.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 = testos.fsencode( testos.environ[testos.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 = testos.fsencode( testos.environ[testos.fsdecode(var)]) else: value = environ[var] except KeyError: value = dollar + var res += value if c: index -= 1 else: res += c index += 1 return res
def basename(p): """Returns the final component of a pathname""" p = testos.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 return p[i:]
def splitdrive(p): """Split a pathname into drive and path. On Posix, drive is always empty.""" p = testos.fspath(p) return p[:0], p
def isabs(s): """Test whether a path is absolute""" s = testos.fspath(s) s = splitdrive(s)[1] return len(s) > 0 and s[0] in _get_bothseps(s)