summaryrefslogtreecommitdiff
path: root/Lib/posixpath.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/posixpath.py')
-rw-r--r--Lib/posixpath.py73
1 files changed, 59 insertions, 14 deletions
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index 0aa53feac2..09b8897968 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