You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
3.2 KiB

  1. import posixpath
  2. class File:
  3. """File abstraction. Only real method is to get fullpath"""
  4. def __init__(self, name, parent_dir):
  5. self.name = name
  6. self.parent_dir = parent_dir
  7. self.fullpath = None
  8. def get_fullpath(self, real_time=False):
  9. if self.fullpath is None or real_time:
  10. if self.parent is None:
  11. self.fullpath = self.name
  12. else:
  13. self.fullpath = posixpath.join(
  14. self.parent_dir.get_fullpath(real_time), self.name)
  15. return self.fullpath
  16. class Directory(File):
  17. """Directory abstraction with optional listener"""
  18. def __init__(self, name, is_listening=False):
  19. super().__init__(name, None)
  20. self.children = {}
  21. self.is_listening = is_listening
  22. if is_listening:
  23. self.modified = False
  24. def __index__(self, key):
  25. return self.children[key]
  26. def mark_modified(self):
  27. if self.is_listening:
  28. self.modified = True
  29. if self.parent_dir:
  30. self.parent_dir.mark_modified()
  31. def traverse(self, path, create_intermediate=False, notify=True):
  32. """Traverse filesystem returning either file or directory"""
  33. if type(path) == str:
  34. path = path.split('/')
  35. if len(path) == 1:
  36. try:
  37. return self[path[0]]
  38. except KeyError:
  39. raise FileNotFoundError
  40. _next, *rest = path
  41. if _next == "..":
  42. if self.parent_dir:
  43. selected = self.parent_dir
  44. else:
  45. selected = self
  46. elif _next == '.':
  47. selected = self
  48. else:
  49. try:
  50. selected = self[_next]
  51. except KeyError:
  52. if create_intermediate:
  53. new = Directory(_next, self.is_listening)
  54. self.add_child(new, notify)
  55. selected = new
  56. else:
  57. raise FileNotFoundError
  58. return selected.traverse(
  59. rest, create_intermediate=create_intermediate,
  60. notify=notify
  61. )
  62. def add_child(self, child, notify=True):
  63. if notify and self.is_listening:
  64. self.mark_modified()
  65. self.children[child.name] = child
  66. child.parent_dir = self
  67. def remove_child(self, child_name, notify=True):
  68. if notify and self.is_listening:
  69. self.mark_modified()
  70. del self[child_name]
  71. def iter_files(self):
  72. for child in self.children.values():
  73. if isinstance(child, Directory):
  74. yield from child.iter_files()
  75. else:
  76. yield child
  77. @classmethod
  78. def from_dir_listing(
  79. cls,
  80. listing,
  81. rootname="",
  82. sep='/',
  83. is_listening=False
  84. ):
  85. """Create a Directory object with files from file listing"""
  86. root = Directory(rootname, is_listening=is_listening)
  87. for file in listing:
  88. parts, file = file.split(sep)
  89. parent = root.traverse(
  90. parts, create_intermediate=True, notify=False)
  91. file = File(file)
  92. parent.add_child(file, notify=False)
  93. return root