ffile.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # python3X
  2. """
  3. Convenient methods for managing files and directories
  4. @author: olivier.massot
  5. """
  6. import os
  7. import re
  8. import shutil
  9. import subprocess
  10. import sys
  11. import tempfile
  12. __VERSION__ = "0.2"
  13. def fdir(path):
  14. """return the parent directory of the file or directory
  15. (no file existence control)"""
  16. if path[-1] == os.path.sep:
  17. path = os.path.dirname(path)
  18. return os.path.normpath(os.path.dirname(path))
  19. def fname(path, with_ext=True):
  20. """return the name of the file
  21. with_ext: return the name with its extension
  22. (no file existence control)"""
  23. if not with_ext:
  24. path, _ = os.path.splitext(path)
  25. if path[-1] == os.path.sep:
  26. path = os.path.dirname(path)
  27. return os.path.basename(path)
  28. def fext(path):
  29. """return the extension of the file
  30. (no file existence control)"""
  31. return os.path.splitext(path)[1]
  32. def fabs(path):
  33. """ return the absolute expanded normalized path """
  34. return os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
  35. def fexists(path):
  36. """test the existence of a file or directory"""
  37. return os.path.exists(os.path.normpath(path))
  38. def fisurl(instr):
  39. """return True if the 'instr' is a web url"""
  40. return re.match(r"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)", \
  41. instr)
  42. def fstart(path):
  43. """run/open the required file, directory, webpage... with the default software"""
  44. if not fisurl(path):
  45. path = os.path.normpath(path)
  46. if sys.platform == "win32":
  47. os.startfile(path)
  48. else:
  49. opener = "open" if sys.platform == "darwin" else "xdg-open"
  50. subprocess.call([opener, path])
  51. def flist(target_dir, files=True, dirs=False, recursive=True):
  52. """list the subdirectories and/or the files 'relative paths inside the directory
  53. """
  54. if not os.path.isdir(target_dir):
  55. raise NotADirectoryError("'{var}' is not an existing directory".format(var=target_dir))
  56. result = []
  57. for _root, _dirs, _files in os.walk(target_dir):
  58. rel_dir = os.path.relpath(_root, target_dir)
  59. if dirs:
  60. result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _dirs)
  61. if files:
  62. result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _files)
  63. if not recursive:
  64. break
  65. return result
  66. def ftree(dirpath):
  67. """ return a set (unordered) of the paths of the sub-directories
  68. """
  69. result = set()
  70. for sdpath in (os.path.join(dirpath, dirname) for dirname \
  71. in os.listdir(dirpath)):
  72. if os.path.isdir(sdpath):
  73. sdpath = os.path.normpath(sdpath)
  74. result |= ({sdpath} | ftree(sdpath))
  75. return result
  76. def fcopy(source, targetdir, rename_as="", erase=False):
  77. """ copy the file to the target directory
  78. > return the path of the newly created file
  79. """
  80. source = fabs(source)
  81. targetdir = fabs(targetdir)
  82. if not os.path.isdir(targetdir):
  83. raise NotADirectoryError("'targetdir' has to be a directory (given: {})".format(targetdir))
  84. target_name, target_ext = os.path.splitext(fname(source)) if not rename_as else os.path.splitext(rename_as)
  85. targetpath = os.path.join(targetdir, '%s%s' % (target_name, target_ext))
  86. if not erase and fexists(targetpath):
  87. targetpath = next((os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext)) for counter in range(2, 100000000) \
  88. if not fexists(os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext)))))
  89. shutil.copyfile(source, targetpath)
  90. return targetpath
  91. def faccess(path, mode="r"):
  92. """ return True if user has an access to the directory or file in parameter
  93. """
  94. path = os.path.normpath(path)
  95. if os.path.isfile(path):
  96. try:
  97. with open(path, mode) as _:
  98. pass
  99. return True
  100. except PermissionError:
  101. return False
  102. except FileNotFoundError:
  103. # could be a directory
  104. pass
  105. except OSError:
  106. pass
  107. if os.path.isdir(path):
  108. # try to explore as a directory
  109. if mode[0] == "r":
  110. try:
  111. next(os.walk(path))
  112. except StopIteration:
  113. return False
  114. return True
  115. else:
  116. try:
  117. with tempfile.TemporaryFile(mode=mode, dir=path) as _:
  118. pass
  119. return True
  120. except PermissionError:
  121. return False
  122. raise FileNotFoundError("'{path}' does not exist or is unaccessible".format(path=path))
  123. def fjoin(*args):
  124. """recursively normalize and join the paths in args"""
  125. return os.path.join(*[os.path.normpath(p) for p in args])
  126. def fmkdir(dirpath):
  127. """ create the directory if it do not exist """
  128. if not os.path.exists(dirpath):
  129. os.mkdir(dirpath)
  130. def fmktree(dirpath):
  131. """ create the directory recursively """
  132. if not os.path.exists(os.path.dirname(dirpath)):
  133. fmktree(os.path.dirname(dirpath))
  134. fmkdir(dirpath)
  135. def frmtree(dirpath):
  136. """recursively delete the directory, even it is not empty"""
  137. try:
  138. shutil.rmtree(dirpath)
  139. except FileNotFoundError:
  140. pass
  141. def fhumansize(size):
  142. """return a human readable size of file"""
  143. suffixes = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']
  144. suffix_index = 0
  145. while size >= 1024:
  146. suffix_index += 1 # increment the index of the suffix
  147. size /= 1024.0 # apply the division
  148. return "{0:03.2f} {1}".format(size, suffixes[suffix_index]) if suffix_index > 0 \
  149. else "{0} {1}".format(size, suffixes[suffix_index])
  150. def fsub(dir1, dir2):
  151. """ return True if dir1 a sub-directory of dir2 """
  152. dir1, dir2 = map(fabs, [dir1, dir2])
  153. return os.path.commonprefix([dir1, dir2]) == dir2