Ангел обнови решението на 30.04.2015 00:32 (преди над 9 години)
+from os.path import basename, dirname
+from collections import defaultdict
+
+class FileSystemError(RuntimeError):
+ pass
+
+class NotEnoughSpaceError(FileSystemError):
+ pass
+
+class NodeDoesNotExistError(FileSystemError):
+ pass
+
+class SourceNodeDoesNotExistError(NodeDoesNotExistError):
+ pass
+
+class DestinationNodeDoesNotExistError(NodeDoesNotExistError):
+ pass
+
+class DestinationNodeExistsError(FileSystemError):
+ pass
+
+class FileSystemMountError(FileSystemError):
+ pass
+
+class MountPointDoesNotExistError(FileSystemMountError):
+ pass
+
+class MountPointNotADirectoryError(FileSystemMountError):
+ pass
+
+class MountPointNotEmptyError(FileSystemMountError):
+ pass
+
+class NotAMountpointError(FileSystemMountError):
+ pass
+
+class NonExplicitDirectoryDeletionError(FileSystemError):
+ pass
+
+class NonEmptyDirectoryDeletionError(FileSystemError):
+ pass
+
+class DestinationNotADirectoryError(FileSystemError):
+ pass
+
+class DirectoryHardLinkError(FileSystemError):
+ pass
+
+class LinkPathError(FileSystemError):
+ pass
+
+
+class Node:
+ def __init__(self, directory=False, content='', filesystem=None):
+ self.is_directory = directory
+ self.real_content = content
+ self.real_children = {}
+ self.mountpoint = None
+ self.link_path = None
+ self.filesystem = filesystem
+
+ def get_path(self, path):
+ if self.mountpoint:
+ return self.mountpoint.root.get_path(path)
+
+ if not path:
+ return self
+
+ if not self.is_directory:
+ raise NodeDoesNotExistError
+
+ try:
+ if '/' in path:
+ root, rest = path.split('/', 1)
+ return self.children[root].get_path(rest)
+ else:
+ return self.children[path].get_path('')
+ except KeyError:
+ raise NodeDoesNotExistError
+
+ def _linked_node(self):
+ try:
+ return self.filesystem.get_node(self.link_path)
+ except NodeDoesNotExistError:
+ raise LinkPathError
+
+ @property
+ def content(self):
+ if self.link_path:
+ return self._linked_node().content
+ else:
+ return self.real_content
+
+ @property
+ def children(self):
+ if self.link_path:
+ return self._linked_node().children
+ else:
+ return self.real_children
+
+ @property
+ def nodes(self):
+ return self.children.values()
+
+ @property
+ def directories(self):
+ return [node for node in self.nodes if node.is_directory]
+
+ @property
+ def files(self):
+ return [node for node in self.nodes if not node.is_directory]
+
+ @property
+ def size(self):
+ if self.link_path:
+ return 1
+ elif self.is_directory:
+ return 1 + sum([node.size for node in self.nodes])
+ else:
+ if self.filesystem.nlink[id(self.content)] > 1:
+ return 1
+ else:
+ return 1 + len(self.content)
+
+class FileSystem:
+ def __init__(self, size):
+ self.size = size
+ self.available_size = size - 1
+ self.root = Node(directory=True, filesystem=self)
+ self.nlink = defaultdict(int)
+
+ def get_node(self, path):
+ if not path:
+ raise FileSystemError('empty path')
+ if path[0] != '/':
+ raise FileSystemError('invalid absolute path')
+ path = path[1:]
+
+ return self.root.get_path(path)
+
+ def create(self, path, directory=False, content=''):
+ node = Node(directory, content, filesystem=self)
+ try:
+ parent = self.get_node(dirname(path))
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError
+
+ if parent.filesystem.available_size < node.size:
+ raise NotEnoughSpaceError
+
+ if not parent.is_directory:
+ raise FileSystemError('parent not a directory')
+
+ filename = basename(path)
+ if filename in parent.children:
+ raise DestinationNodeExistsError
+
+ parent.children[filename] = node
+ parent.filesystem.nlink[id(content)] += 1
+ parent.filesystem.available_size -= node.size
+
+ def remove(self, path, directory=False, force=True):
+ parent = self.get_node(dirname(path))
+ node = self.get_node(path)
+
+ if node.is_directory:
+ if not directory:
+ raise NonExplicitDirectoryDeletionError
+ if node.children and not force:
+ raise NonEmptyDirectoryDeletionError
+
+ del parent.children[basename(path)]
+ node.filesystem.available_size += node.size
+ node.filesystem.nlink[id(node.content)] -= 1
+
+ def move(self, source, destination):
+ try:
+ source_node = self.get_node(source)
+ except NodeDoesNotExistError:
+ raise SourceNodeDoesNotExistError
+
+ try:
+ destination_node = self.get_node(destination)
+ except NodeDoesNotExistError as exception:
+ raise DestinationNodeDoesNotExistError
+
+ if not destination_node.is_directory:
+ raise DestinationNotADirectoryError
+
+ if basename(source) in destination_node.children:
+ raise DestinationNodeExistsError
+
+ destination_node.children[basename(source)] = source_node
+ del self.get_node(dirname(source)).children[basename(source)]
+
+ def hardlink(self, source, destination):
+ try:
+ source_node = self.get_node(source)
+ except NodeDoesNotExistError:
+ raise SourceNodeDoesNotExistError
+
+ if source_node.is_directory:
+ raise DirectoryHardLinkError
+
+ self.create(destination, content=source_node.content)
+
+ def symlink(self, source, destination):
+ source_node = self.get_node(source)
+ self.create(destination, directory=source_node.is_directory)
+ destination_node = self.get_node(destination)
+ destination_node.link_path = source
+
+ def link(self, source, destination, symbolic=True):
+ if symbolic:
+ self.symlink(source, destination)
+ else:
+ self.hardlink(source, destination)
+
+ def mount(self, file_system, path):
+ try:
+ parent = self.get_node(path)
+ except NodeDoesNotExistError:
+ raise MountPointDoesNotExistError
+ if not parent.is_directory:
+ raise MountPointNotADirectoryError
+ if parent.children:
+ raise MountPointNotEmptyError
+
+ parent.mountpoint = file_system
+
+ def unmount(self, path):
+ parent = self.get_node(dirname(path))
+ try:
+ node = parent.children[basename(path)]
+ except KeyError:
+ raise NodeDoesNotExistError
+
+ if not node.mountpoint:
+ raise NotAMountpointError
+
+ node.mountpoint = None