From 62537bc0acfe2adfe1a483bda13434ab30c7a02b Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Fri, 13 Apr 2018 13:14:51 +1000 Subject: sb/path: Update the path module to the latest from master. - Fixes Windows path issues - Fixes infinite loop with a read-only path. Close #3394 --- source-builder/sb/path.py | 147 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 45 deletions(-) diff --git a/source-builder/sb/path.py b/source-builder/sb/path.py index 43f41eb..e2fd3d4 100644 --- a/source-builder/sb/path.py +++ b/source-builder/sb/path.py @@ -34,6 +34,7 @@ import string import error windows = os.name == 'nt' +win_maxpath = 254 def host(path): if path is not None: @@ -46,13 +47,21 @@ def host(path): path[1] in string.ascii_uppercase): path = '%s:%s' % (path[1], path[2:]) path = path.replace('/', '\\') - if not path.startswith('\\\\?\\') and len(path) > 254: - path = '\\\\?\\' + path + if len(path) > win_maxpath: + if path.startswith('\\\\?\\'): + path = path[4:] + path = u'\\'.join([u'\\\\?', path]) return path +def is_abspath(path): + if path is not None and len(path) > 0: + return '/' == path[0] + return False + def shell(path): if path is not None: if windows: + path = path.encode('ascii', 'ignore') if path.startswith('\\\\?\\'): path = path[4:] if len(path) > 1 and path[1] == ':': @@ -63,9 +72,11 @@ def shell(path): return path def basename(path): - return shell(os.path.basename(path)) + path = shell(path) + return shell(os.path.basename(host(path))) def dirname(path): + path = shell(path) return shell(os.path.dirname(path)) def join(path, *args): @@ -78,42 +89,68 @@ def join(path, *args): return shell(path) def abspath(path): + path = shell(path) return shell(os.path.abspath(host(path))) +def relpath(path, start = None): + path = shell(path) + if start is None: + path = os.path.relpath(host(path)) + else: + path = os.path.relpath(host(path), start) + return shell(path) + def splitext(path): + path = shell(path) root, ext = os.path.splitext(host(path)) return shell(root), ext +def listdir(path): + path = shell(path) + hp = host(path) + if not os.path.exists(hp): + return [] + return os.listdir(hp) + def exists(paths): + def _exists(p): + if not is_abspath(p): + p = shell(join(os.getcwd(), host(p))) + return basename(p) in ['.'] + listdir(dirname(p)) + if type(paths) == list: results = [] for p in paths: - results += [os.path.exists(host(p))] + results += [_exists(shell(p))] return results - return os.path.exists(host(paths)) + return _exists(shell(paths)) def isdir(path): + path = shell(path) return os.path.isdir(host(path)) def isfile(path): + path = shell(path) return os.path.isfile(host(path)) def isabspath(path): + path = shell(path) return path[0] == '/' def iswritable(path): + path = shell(path) return os.access(host(path), os.W_OK) def ispathwritable(path): - path = host(path) - while len(path) != 0: - if os.path.exists(path): + path = shell(path) + while len(path) > 1: + if exists(path): return iswritable(path) - path = os.path.dirname(path) + path = dirname(path) return False def mkdir(path): - path = host(path) + path = shell(path) if exists(path): if not isdir(path): raise error.general('path exists and is not a directory: %s' % (path)) @@ -135,41 +172,60 @@ def mkdir(path): except OSError as err: raise error.general('cannot make directory: %s' % (path)) +def chdir(path): + path = shell(path) + os.chdir(host(path)) + def removeall(path): # # Perform the removal of the directory tree manually so we can - # make sure on Windows the files and correctly encoded to avoid - # the size limit. + # make sure on Windows the files are correctly encoded to avoid + # the file name size limit. On Windows the os.walk fails once we + # get to the max path length on Windows. # - path = host(path) - for root, dirs, files in os.walk(path, topdown = False): - for name in files: - file = host(os.path.join(root, name)) - if not os.path.islink(file) and not os.access(file, os.W_OK): - os.chmod(file, stat.S_IWUSR) - os.unlink(file) - for name in dirs: - dir = host(os.path.join(root, name)) - if os.path.islink(dir): - os.unlink(dir) + def _isdir(path): + hpath = host(path) + return os.path.isdir(hpath) and not os.path.islink(hpath) + + def _remove_node(path): + hpath = host(path) + if not os.path.islink(hpath) and not os.access(hpath, os.W_OK): + os.chmod(hpath, stat.S_IWUSR) + if _isdir(path): + os.rmdir(hpath) + else: + os.unlink(hpath) + + def _remove(path): + dirs = [] + for name in listdir(path): + path_ = join(path, name) + hname = host(path_) + if _isdir(path_): + dirs += [name] else: - if not os.access(dir, os.W_OK): - os.chmod(dir, stat.S_IWUSR) - os.rmdir(dir) - if not os.path.islink(path) and not os.access(path, os.W_OK): - os.chmod(path, stat.S_IWUSR) - if os.path.islink(path): - os.unlink(path) - else: - os.rmdir(path) + _remove_node(path_) + for name in dirs: + dir = join(path, name) + _remove(dir) + _remove_node(dir) + + path = shell(path) + hpath = host(path) + + if os.path.exists(hpath): + _remove(path) + _remove_node(path) def expand(name, paths): l = [] for p in paths: - l += [join(p, name)] + l += [join(shell(p), name)] return l def copy(src, dst): + src = shell(src) + dst = shell(dst) hsrc = host(src) hdst = host(dst) try: @@ -179,7 +235,7 @@ def copy(src, dst): if WindowsError is not None and isinstance(why, WindowsError): pass else: - raise error.general('copying tree: %s -> %s: %s' % (hsrc, hdst, str(why))) + raise error.general('copying tree (1): %s -> %s: %s' % (hsrc, hdst, str(why))) def copy_tree(src, dst): trace = False @@ -187,17 +243,17 @@ def copy_tree(src, dst): hsrc = host(src) hdst = host(dst) - if os.path.exists(hsrc): - names = os.listdir(hsrc) + if exists(src): + names = listdir(src) else: names = [] if trace: print('path.copy_tree:') - print(' src: %s' % (src)) - print(' hsrc: %s' % (hsrc)) - print(' dst: %s' % (dst)) - print(' hdst: %s' % (hdst)) + print(' src: "%s"' % (src)) + print(' hsrc: "%s"' % (hsrc)) + print(' dst: "%s"' % (dst)) + print(' hdst: "%s"' % (hdst)) print(' names: %r' % (names)) if not os.path.isdir(hdst): @@ -215,7 +271,7 @@ def copy_tree(src, dst): try: if os.path.islink(srcname): linkto = os.readlink(srcname) - if os.path.exists(dstname): + if exists(shell(dstname)): if os.path.islink(dstname): dstlinkto = os.readlink(dstname) if linkto != dstlinkto: @@ -231,12 +287,13 @@ def copy_tree(src, dst): elif os.path.isdir(srcname): copy_tree(srcname, dstname) else: - shutil.copy2(host(srcname), host(dstname)) + shutil.copyfile(host(srcname), host(dstname)) + shutil.copystat(host(srcname), host(dstname)) except shutil.Error as err: - raise error.general('copying tree: %s -> %s: %s' % \ + raise error.general('copying tree (2): %s -> %s: %s' % \ (hsrc, hdst, str(err))) except EnvironmentError as why: - raise error.general('copying tree: %s -> %s: %s' % \ + raise error.general('copying tree (3): %s -> %s: %s' % \ (srcname, dstname, str(why))) try: shutil.copystat(hsrc, hdst) @@ -245,7 +302,7 @@ def copy_tree(src, dst): if WindowsError is not None and isinstance(why, WindowsError): pass else: - raise error.general('copying tree: %s -> %s: %s' % (hsrc, hdst, str(why))) + raise error.general('copying tree (4): %s -> %s: %s' % (hsrc, hdst, str(why))) if __name__ == '__main__': print(host('/a/b/c/d-e-f')) -- cgit v1.2.3