diff options
Diffstat (limited to 'Lib/posixpath.py')
-rw-r--r-- | Lib/posixpath.py | 73 |
1 files changed, 59 insertions, 14 deletions
diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 0aa53fe..09b8897 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -22,7 +22,8 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "ismount", "expanduser","expandvars","normpath","abspath", "samefile","sameopenfile","samestat", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames","relpath"] + "devnull","realpath","supports_unicode_filenames","relpath", + "commonpath"] # Strings representing various path-related bits and pieces. # These are primarily for export; internally, they are hardcoded. @@ -75,6 +76,8 @@ def join(a, *p): 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 p: if b.startswith(sep): path = b @@ -82,11 +85,8 @@ def join(a, *p): path += b else: path += sep + b - except TypeError: - if all(isinstance(s, (str, bytes)) for s in (a,) + p): - # Must have a mixture of text and binary data - raise TypeError("Can't mix strings and bytes in path " - "components") from None + except (TypeError, AttributeError, BytesWarning): + genericpath._check_arg_types('join', a, *p) raise return path @@ -445,13 +445,58 @@ def relpath(path, start=None): if start is None: start = curdir - start_list = [x for x in abspath(start).split(sep) if x] - path_list = [x for x in abspath(path).split(sep) if x] + 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 + + +# Return the longest common sub-path of the sequence of paths given as input. +# The paths are not normalized before comparing them (this is the +# responsibility of the caller). Any trailing separator is stripped from the +# returned path. - # Work out how much of the filepath is shared by start and path. - i = len(commonprefix([start_list, path_list])) +def commonpath(paths): + """Given a sequence of path names, returns the longest common sub-path.""" - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return curdir - return join(*rel_list) + if not paths: + raise ValueError('commonpath() arg is an empty sequence') + + if isinstance(paths[0], bytes): + sep = b'/' + curdir = b'.' + else: + sep = '/' + curdir = '.' + + try: + split_paths = [path.split(sep) for path in paths] + + try: + isabs, = set(p[:1] == sep for p in paths) + except ValueError: + raise ValueError("Can't mix absolute and relative paths") from None + + 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) + common = s1 + for i, c in enumerate(s1): + if c != s2[i]: + common = s1[:i] + break + + prefix = sep if isabs else sep[:0] + return prefix + sep.join(common) + except (TypeError, AttributeError): + genericpath._check_arg_types('commonpath', *paths) + raise |