# python3X """ Convenient methods for managing files and directories @author: olivier.massot """ import os import re import shutil import subprocess import sys import tempfile __VERSION__ = "0.2" def fdir(path): """return the parent directory of the file or directory (no file existence control)""" if path[-1] == os.path.sep: path = os.path.dirname(path) return os.path.normpath(os.path.dirname(path)) def fname(path, with_ext=True): """return the name of the file with_ext: return the name with its extension (no file existence control)""" if not with_ext: path, _ = os.path.splitext(path) if path[-1] == os.path.sep: path = os.path.dirname(path) return os.path.basename(path) def fext(path): """return the extension of the file (no file existence control)""" return os.path.splitext(path)[1] def fabs(path): """ return the absolute expanded normalized path """ return os.path.abspath(os.path.expandvars(os.path.expanduser(path))) def fexists(path): """test the existence of a file or directory""" return os.path.exists(os.path.normpath(path)) def fisurl(instr): """return True if the 'instr' is a web url""" return re.match(r"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)", \ instr) def fstart(path): """run/open the required file, directory, webpage... with the default software""" if not fisurl(path): path = os.path.normpath(path) if sys.platform == "win32": os.startfile(path) else: opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, path]) def flist(target_dir, files=True, dirs=False, recursive=True): """list the subdirectories and/or the files 'relative paths inside the directory """ if not os.path.isdir(target_dir): raise NotADirectoryError("'{var}' is not an existing directory".format(var=target_dir)) result = [] for _root, _dirs, _files in os.walk(target_dir): rel_dir = os.path.relpath(_root, target_dir) if dirs: result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _dirs) if files: result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _files) if not recursive: break return result def ftree(dirpath): """ return a set (unordered) of the paths of the sub-directories """ result = set() for sdpath in (os.path.join(dirpath, dirname) for dirname \ in os.listdir(dirpath)): if os.path.isdir(sdpath): sdpath = os.path.normpath(sdpath) result |= ({sdpath} | ftree(sdpath)) return result def fcopy(source, targetdir, rename_as="", erase=False): """ copy the file to the target directory > return the path of the newly created file """ source = fabs(source) targetdir = fabs(targetdir) if not os.path.isdir(targetdir): raise NotADirectoryError("'targetdir' has to be a directory (given: {})".format(targetdir)) target_name, target_ext = os.path.splitext(fname(source)) if not rename_as else os.path.splitext(rename_as) targetpath = os.path.join(targetdir, '%s%s' % (target_name, target_ext)) if not erase and fexists(targetpath): targetpath = next((os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext)) for counter in range(2, 100000000) \ if not fexists(os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext))))) shutil.copyfile(source, targetpath) return targetpath def faccess(path, mode="r"): """ return True if user has an access to the directory or file in parameter """ path = os.path.normpath(path) if os.path.isfile(path): try: with open(path, mode) as _: pass return True except PermissionError: return False except FileNotFoundError: # could be a directory pass except OSError: pass if os.path.isdir(path): # try to explore as a directory if mode[0] == "r": try: next(os.walk(path)) except StopIteration: return False return True else: try: with tempfile.TemporaryFile(mode=mode, dir=path) as _: pass return True except PermissionError: return False raise FileNotFoundError("'{path}' does not exist or is unaccessible".format(path=path)) def fjoin(*args): """recursively normalize and join the paths in args""" return os.path.join(*[os.path.normpath(p) for p in args]) def fmkdir(dirpath): """ create the directory if it do not exist """ if not os.path.exists(dirpath): os.mkdir(dirpath) def fmktree(dirpath): """ create the directory recursively """ if not os.path.exists(os.path.dirname(dirpath)): fmktree(os.path.dirname(dirpath)) fmkdir(dirpath) def frmtree(dirpath): """recursively delete the directory, even it is not empty""" try: shutil.rmtree(dirpath) except FileNotFoundError: pass def fhumansize(size): """return a human readable size of file""" suffixes = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'] suffix_index = 0 while size >= 1024: suffix_index += 1 # increment the index of the suffix size /= 1024.0 # apply the division return "{0:03.2f} {1}".format(size, suffixes[suffix_index]) if suffix_index > 0 \ else "{0} {1}".format(size, suffixes[suffix_index]) def fsub(dir1, dir2): """ return True if dir1 a sub-directory of dir2 """ dir1, dir2 = map(fabs, [dir1, dir2]) return os.path.commonprefix([dir1, dir2]) == dir2