Ивайло обнови решението на 30.04.2015 16:27 (преди над 9 години)
+class FileSystemError(Exception):
+ pass
+
+
+class FileSystemMountError(Exception):
+ pass
+
+
+class NotAMountpointError(FileSystemMountError):
+ pass
+
+
+class MountPointDoesNotExistError(FileSystemMountError):
+ pass
+
+
+class MountPointNotEmptyError(FileSystemMountError):
+ pass
+
+
+class MountPointNotADirectoryError(FileSystemMountError):
+ pass
+
+
+class NonExplicitDirectoryDeletionError(FileSystemError):
+ pass
+
+
+class LinkPathError(FileSystemError):
+ pass
+
+
+class NonEmptyDirectoryDeletionError(FileSystemError):
+ pass
+
+
+class DirectoryHardLinkError(FileSystemError):
+ pass
+
+
+class NotEnoughSpaceError(FileSystemError):
+ pass
+
+
+class DestinationNotADirectoryError(FileSystemError):
+ pass
+
+
+class InvalidPathError(FileSystemError):
+ pass
+
+
+class NodeDoesNotExistError(FileSystemError):
+ pass
+
+
+class DestinationNodeDoesNotExistError(NodeDoesNotExistError):
+ pass
+
+
+class SourceNodeDoesNotExistError(NodeDoesNotExistError):
+ pass
+
+
+class DestinationNodeExistsError(FileSystemError):
+ pass
+
+
+class DirectoryNode(object):
+ def __init__(self):
+ self._nodes = {}
+
+ @property
+ def _size(self):
+ return 1
+
+ @property
+ def size(self):
+ return self._size + sum(n.size for n in self._nodes.values())
+
+ @property
+ def is_directory(self):
+ return True
+
+ @property
+ def nodes(self):
+ return list(self._nodes.values())
+
+ @property
+ def files(self):
+ return [n for n in self._nodes.values() if n.is_directory is False]
+
+ @property
+ def directories(self):
+ return [n for n in self._nodes.values() if n.is_directory is True]
+
+ @property
+ def nodes_dict(self):
+ return self._nodes
+
+
+class MountPointNode(object):
+ def __init__(self, filesystem):
+ self.filesystem = filesystem
+
+ @property
+ def is_directory(self):
+ return True
+
+ @property
+ def _size(self):
+ return 1
+
+ @property
+ def nodes(self):
+ return self.filesystem.root.nodes
+
+ @property
+ def files(self):
+ return self.filesystem.root.files
+
+ @property
+ def directories(self):
+ return self.filesystem.root.directories
+
+ @property
+ def nodes_dict(self):
+ return self.filesystem.root.nodes_dict
+
+
+class ContentNode(object):
+ def __init__(self, content):
+ self.content = content
+
+ @property
+ def size(self):
+ return len(self.content)
+
+ @property
+ def _size(self):
+ return self.size
+
+
+class FileNode(object):
+ def __init__(self, content_node):
+ self.content_node = content_node
+
+ @property
+ def content(self):
+ return self.content_node.content
+
+ @property
+ def size(self):
+ return self.content_node.size + 1
+
+ @property
+ def _size(self):
+ return 1
+
+ def truncate(self, content):
+ self.content_node.content = content
+
+ def append(self, content):
+ self.content_node.content += content
+
+ @property
+ def is_directory(self):
+ return False
+
+
+class SymbolicLinkNode(object):
+ def __init__(self, filesystem, path):
+ self.filesystem = filesystem
+ self.path = path
+
+ @property
+ def _size(self):
+ return 1
+
+ @property
+ def node(self):
+ try:
+ return self.filesystem.get_node(self.path)
+ except NodeDoesNotExistError:
+ raise LinkPathError()
+
+ @property
+ def is_directory(self):
+ return self.node.is_directory
+
+ @property
+ def nodes(self):
+ if not self.node.is_directory:
+ raise AttributeError()
+ return self.node.nodes
+
+ @property
+ def files(self):
+ if not self.node.is_directory:
+ raise AttributeError()
+ return self.node.files
+
+ @property
+ def directories(self):
+ if not self.node.is_directory:
+ raise AttributeError()
+ return self.node.directories
+
+ @property
+ def nodes_dict(self):
+ if not self.node.is_directory:
+ raise AttributeError()
+ return self.node.nodes_dict
+
+ @property
+ def content(self):
+ if self.node.is_directory:
+ raise AttributeError()
+ return self.node.content
+
+ def truncate(self, content):
+ if self.node.is_directory:
+ raise AttributeError()
+ self.node.truncate(content)
+
+ def append(self, content):
+ if self.node.is_directory:
+ raise AttributeError()
+ self.node.append(content)
+
+
+class FileSystem(object):
+ def __init__(self, size):
+ if size < 1:
+ raise NotEnoughSpaceError()
+ self.root = DirectoryNode()
+ self.size = size
+
+ def __get_node(self, path):
+ node = self.root
+ for part in path:
+ if not node.is_directory:
+ raise NodeDoesNotExistError()
+ node = node.nodes_dict.get(part)
+ if node is None:
+ raise NodeDoesNotExistError()
+ return node
+
+ def _get_node(self, path):
+ node = self.__get_node(path)
+ if type(node) is MountPointNode:
+ return node.filesystem.root
+ return node
+
+ def _get_fs(self, path):
+ node = self.root
+ fs = self
+ for part in path:
+ if not node.is_directory:
+ raise NodeDoesNotExistError()
+ node = node._nodes.get(part)
+ if type(node) is MountPointNode:
+ fs = node.filesystem
+ if node is None:
+ raise NodeDoesNotExistError()
+ return fs
+
+ def validate_path(self, path):
+ if path == '':
+ raise NodeDoesNotExistError()
+ if path == '/':
+ return []
+ parts = path.split('/')
+ if len(parts) < 2 or parts[0] or not all(parts[1:]):
+ raise InvalidPathError()
+ return parts[1:]
+
+ def get_node(self, path):
+ return self._get_node(self.validate_path(path))
+
+ def create(self, path, directory=False, content=''):
+ parts = self.validate_path(path)
+ if len(parts) == 0:
+ raise DestinationNodeExistsError()
+ try:
+ node = self._get_node(parts[:-1])
+ node_fs = self._get_fs(parts[:-1])
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if directory:
+ required_space = 1
+ new_node = DirectoryNode()
+ else:
+ required_space = 1 + len(content)
+ new_node = FileNode(ContentNode(content))
+ if parts[-1] in node.nodes_dict:
+ raise DestinationNodeExistsError()
+ if node_fs.available_size < required_space:
+ raise NotEnoughSpaceError()
+ node.nodes_dict[parts[-1]] = new_node
+
+ def remove(self, path, directory=False, force=False):
+ parts = self.validate_path(path)
+ if len(parts) == 0:
+ raise FileSystemError()
+ try:
+ node = self._get_node(parts[:-1])
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ dest_node = node.nodes_dict.get(parts[-1])
+ if dest_node is None:
+ raise DestinationNodeDoesNotExistError()
+ if dest_node.is_directory:
+ if not directory:
+ raise NonExplicitDirectoryDeletionError()
+ if len(dest_node.nodes_dict) != 0 and not force:
+ raise NonEmptyDirectoryDeletionError()
+ del node.nodes_dict[parts[-1]]
+
+ def move(self, source, destination):
+ src_parts = self.validate_path(source)
+ dst_parts = self.validate_path(destination)
+ try:
+ src_dir = self._get_node(src_parts[:-1])
+ except NodeDoesNotExistError:
+ raise SourceNodeDoesNotExistError()
+ if src_parts[-1] not in src_dir.nodes_dict:
+ raise SourceNodeDoesNotExistError()
+ try:
+ dst_dir = self._get_node(dst_parts)
+ dst_dir_fs = self._get_fs(dst_parts)
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if not dst_dir.is_directory:
+ raise DestinationNotADirectoryError()
+ if src_parts[-1] in dst_dir.nodes_dict:
+ raise DestinationNodeExistsError()
+ dst_dir.nodes_dict[src_parts[-1]] = src_dir.nodes_dict[src_parts[-1]]
+ del src_dir.nodes_dict[src_parts[-1]]
+ if dst_dir_fs.available_size < 0:
+ src_dir.nodes_dict[src_parts[-1]] = \
+ dst_dir.nodes_dict[src_parts[-1]]
+ del dst_dir.nodes_dict[src_parts[-1]]
+
+ def link(self, source, destination, symbolic=True):
+ src_parts = self.validate_path(source)
+ dst_parts = self.validate_path(destination)
+ try:
+ src_node = self._get_node(src_parts)
+ except NodeDoesNotExistError:
+ if symbolic:
+ raise NodeDoesNotExistError()
+ else:
+ raise SourceNodeDoesNotExistError()
+ if not symbolic and src_node.is_directory:
+ raise DirectoryHardLinkError()
+ try:
+ dst_dir = self._get_node(dst_parts[:-1])
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if dst_parts[-1] in dst_dir.nodes_dict:
+ raise DestinationNodeExistsError()
+ if not symbolic:
+ dst_dir.nodes_dict[dst_parts[-1]] = FileNode(src_node.content_node)
+ else:
+ dst_dir.nodes_dict[dst_parts[-1]] = SymbolicLinkNode(self, source)
+
+ def mount(self, file_system, path):
+ parts = self.validate_path(path)
+ try:
+ node = self._get_node(parts)
+ node_dir = self._get_node(parts[:-1])
+ except NodeDoesNotExistError:
+ raise MountPointDoesNotExistError()
+ if node.is_directory is False:
+ raise MountPointNotADirectoryError()
+ if len(node.nodes) != 0:
+ raise MountPointNotEmptyError()
+ node_dir.nodes_dict[parts[-1]] = MountPointNode(file_system)
+
+ def unmount(self, path):
+ parts = self.validate_path(path)
+ node = self.__get_node(parts)
+ node_dir = self._get_node(parts[:-1])
+ if type(node) is not MountPointNode:
+ raise NotAMountpointError()
+ node_dir.nodes_dict[parts[-1]] = DirectoryNode()
+
+ @property
+ def available_size(self):
+ nodes = set()
+
+ def walk(node):
+ nodes.add(node)
+ if type(node) is FileNode:
+ nodes.add(node.content_node)
+ if type(node) is DirectoryNode:
+ for child in node.nodes:
+ walk(child)
+
+ walk(self.root)
+ return self.size - sum(n._size for n in nodes)