diff options
Diffstat (limited to 'Lib/ntpath.py')
-rw-r--r-- | Lib/ntpath.py | 260 |
1 files changed, 126 insertions, 134 deletions
diff --git a/Lib/ntpath.py b/Lib/ntpath.py index af3fb87..ee1a215 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -32,48 +32,12 @@ if 'ce' in sys.builtin_module_names: defpath = '\\Windows' devnull = 'nul' -def _get_empty(path): - if isinstance(path, bytes): - return b'' - else: - return '' - -def _get_sep(path): - if isinstance(path, bytes): - return b'\\' - else: - return '\\' - -def _get_altsep(path): - if isinstance(path, bytes): - return b'/' - else: - return '/' - def _get_bothseps(path): if isinstance(path, bytes): return b'\\/' else: return '\\/' -def _get_dot(path): - if isinstance(path, bytes): - return b'.' - else: - return '.' - -def _get_colon(path): - if isinstance(path, bytes): - return b':' - else: - return ':' - -def _get_special(path): - if isinstance(path, bytes): - return (b'\\\\.\\', b'\\\\?\\') - else: - return ('\\\\.\\', '\\\\?\\') - # Normalize the case of a pathname and map slashes to backslashes. # Other normalizations (such as optimizing '../' away) are not done # (this is done by normpath). @@ -82,10 +46,16 @@ def normcase(s): """Normalize case of pathname. Makes all characters lowercase and all slashes into backslashes.""" - if not isinstance(s, (bytes, str)): - raise TypeError("normcase() argument must be str or bytes, " - "not '{}'".format(s.__class__.__name__)) - return s.replace(_get_altsep(s), _get_sep(s)).lower() + 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 # Return whether a path is absolute. @@ -97,40 +67,49 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" s = splitdrive(s)[1] - return len(s) > 0 and s[:1] in _get_bothseps(s) + return len(s) > 0 and s[0] in _get_bothseps(s) # Join two (or more) paths. def join(path, *paths): - sep = _get_sep(path) - seps = _get_bothseps(path) - colon = _get_colon(path) - result_drive, result_path = splitdrive(path) - for p in 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 + if isinstance(path, bytes): + sep = b'\\' + seps = b'\\/' + colon = b':' + else: + sep = '\\' + seps = '\\/' + colon = ':' + try: + result_drive, result_path = splitdrive(path) + for p in 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 - # 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 + 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 # Split a path in a drive specification (a drive letter followed by a @@ -155,10 +134,16 @@ def splitdrive(p): Paths cannot contain both a drive letter and a UNC path. """ - empty = _get_empty(p) - if len(p) > 1: - sep = _get_sep(p) - normp = p.replace(_get_altsep(p), sep) + 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 @@ -166,18 +151,18 @@ def splitdrive(p): # directory ^^^^^^^^^^^^^^^ index = normp.find(sep, 2) if index == -1: - return empty, p + 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 empty, p + return p[:0], p if index2 == -1: index2 = len(p) return p[:index2], p[index2:] - if normp[1:2] == _get_colon(p): + if normp[1:2] == colon: return p[:2], p[2:] - return empty, p + return p[:0], p # Parse UNC paths @@ -221,10 +206,7 @@ def split(p): i -= 1 head, tail = p[:i], p[i:] # now tail has no slashes # remove trailing slashes from head, unless it's all slashes - head2 = head - while head2 and head2[-1:] in seps: - head2 = head2[:-1] - head = head2 or head + head = head.rstrip(seps) or head return d + head, tail @@ -234,8 +216,10 @@ def split(p): # It is always true that root + ext == p. def splitext(p): - return genericpath._splitext(p, _get_sep(p), _get_altsep(p), - _get_dot(p)) + if isinstance(p, bytes): + return genericpath._splitext(p, b'\\', b'/', b'.') + else: + return genericpath._splitext(p, '\\', '/', '.') splitext.__doc__ = genericpath._splitext.__doc__ @@ -343,7 +327,7 @@ def expanduser(path): userhome = join(drive, os.environ['HOMEPATH']) if isinstance(path, bytes): - userhome = userhome.encode(sys.getfilesystemencoding()) + userhome = os.fsencode(userhome) if i != 1: #~user userhome = join(dirname(userhome), path[1:i]) @@ -369,13 +353,14 @@ def expandvars(path): Unknown variables are left unchanged.""" if isinstance(path, bytes): - if ord('$') not in path and ord('%') not in path: + 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(os, 'environb', None) else: @@ -386,6 +371,7 @@ def expandvars(path): quote = '\'' percent = '%' brace = '{' + rbrace = '}' dollar = '$' environ = os.environ res = path[:0] @@ -432,15 +418,9 @@ def expandvars(path): path = path[index+2:] pathlen = len(path) try: - if isinstance(path, bytes): - index = path.index(b'}') - else: - index = path.index('}') + index = path.index(rbrace) except ValueError: - if isinstance(path, bytes): - res += b'${' + path - else: - res += '${' + path + res += dollar + brace + path index = pathlen - 1 else: var = path[:index] @@ -450,10 +430,7 @@ def expandvars(path): else: value = environ[var] except KeyError: - if isinstance(path, bytes): - value = b'${' + var + b'}' - else: - value = '${' + var + '}' + value = dollar + brace + var + rbrace res += value else: var = path[:0] @@ -485,16 +462,25 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc.""" - sep = _get_sep(path) - dotdot = _get_dot(path) * 2 - special_prefixes = _get_special(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(_get_altsep(path), sep) + path = path.replace(altsep, sep) prefix, path = splitdrive(path) # collapse initial backslashes @@ -505,13 +491,13 @@ def normpath(path): comps = path.split(sep) i = 0 while i < len(comps): - if not comps[i] or comps[i] == _get_dot(path): + if not comps[i] or comps[i] == curdir: del comps[i] - elif comps[i] == dotdot: - if i > 0 and comps[i-1] != dotdot: + 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(_get_sep(path)): + elif i == 0 and prefix.endswith(sep): del comps[i] else: i += 1 @@ -519,7 +505,7 @@ def normpath(path): i += 1 # If the path is now empty, substitute '.' if not prefix and not comps: - comps.append(_get_dot(path)) + comps.append(curdir) return prefix + sep.join(comps) @@ -559,42 +545,48 @@ realpath = abspath supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and sys.getwindowsversion()[3] >= 2) -def relpath(path, start=curdir): +def relpath(path, start=None): """Return a relative version of a path""" - sep = _get_sep(path) + if isinstance(path, bytes): + sep = b'\\' + curdir = b'.' + pardir = b'..' + else: + sep = '\\' + curdir = '.' + pardir = '..' - if start is curdir: - start = _get_dot(path) + if start is None: + start = curdir if not path: raise ValueError("no path specified") - 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): - error = "path is on mount '{0}', start on mount '{1}'".format( - path_drive, start_drive) - raise ValueError(error) - - 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 + 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 - if isinstance(path, bytes): - pardir = b'..' - else: - pardir = '..' - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return _get_dot(path) - return join(*rel_list) + 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 # determine if two files are in fact the same file |