Решение на In-memory файлова система от Никола Терзиев

Обратно към всички решения

Към профила на Никола Терзиев

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 14 успешни тест(а)
  • 4 неуспешни тест(а)

Код

class FSNode:
def __init__(self, relative_name, absolute_path, parent_node, file_system):
self.relative_name = relative_name
self.parent_node = parent_node
self.absolute_path = absolute_path
self.file_system = file_system
class FileContent:
def __init__(self, content):
self.content = content
self.size = len(content) + 1
self.ref_count = 1
def content_size(self):
return self.size
class File(FSNode):
def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
super().__init__(relative_name, absolute_path, parent_node, file_system)
self.file_content = file_content
self.is_directory = False
@property
def size(self):
return self.file_content.size
@property
def content(self):
return self.file_content.content
def append(self, content):
self.file_system.file_update(self, content, False)
def truncate(self, content):
self.file_system.file_update(self, content, True)
class SoftLink(File):
def __init__(self, link_path, relative_name, absolute_path, parent_node, file_system):
super().__init__(FileContent(''), relative_name, absolute_path, parent_node, file_system)
self.link_path = link_path
self.is_directory = False
def __getattr__(self, item):
try:
linked_file = self.file_system.get_node(self.link_path)
except NodeDoesNotExistError:
raise LinkPathError()
return getattr(linked_file, item)
@property
def content(self):
return self.__getattr__('content')
class HardLink(File):
def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
super().__init__(file_content, relative_name, absolute_path, parent_node, file_system)
class Directory(FSNode):
def __init__(self, relative_name, absolute_path, parent_node, file_system):
super().__init__(relative_name, absolute_path, parent_node, file_system)
self.files = []
self.directories = []
self.nodes = []
self.is_directory = True
class FileSystem:
def __init__(self, size):
self.dir_size = 1
self.nodes = [Directory('/', '/', None, self)]
self.size = size
self.available_size = size - 1
def get_node(self, path):
return self._find_node_by_path(path)
def create(self, path, directory=False, content=''):
try:
self._find_node_by_path(path)
raise DestinationNodeExistsError()
except NodeDoesNotExistError:
parent_absolute_path, node_name = self._get_parent_node_names_from_path(path)
try:
parent = self._find_node_by_path(parent_absolute_path) # TODO: check if parent is dir
except NodeDoesNotExistError:
raise DestinationNodeDoesNotExistError()
if directory is True:
return self._dir_create(node_name, path, parent)
else:
return self._file_create(content, node_name, path, parent)
def remove(self, path, directory=False, force=True): # TODO: somewhat undefined behaviour when / is deleted
node = self._find_node_by_path(path)
if isinstance(node, File):
self._remove_node(node, node.file_content.size)
elif isinstance(node, Directory):
if directory is False:
raise NonExplicitDirectoryDeletionError()
else:
if len(node.nodes) > 0 and force is False:
raise NonEmptyDirectoryDeletionError()
else:
for sub_node in list(node.nodes):
self.remove(sub_node.absolute_path, sub_node.is_directory)
self._remove_node(node, self.dir_size)
def move(self, source, destination):
try:
source_node = self._find_node_by_path(source)
except NodeDoesNotExistError:
raise SourceNodeDoesNotExistError()
try:
destination_dir = self._find_node_by_path(destination)
except NodeDoesNotExistError:
raise DestinationNodeDoesNotExistError()
if destination_dir.is_directory is not True:
raise DestinationNotADirectoryError()
parent_absolute_path, source_node_name = self._get_parent_node_names_from_path(source)
source_node_new_absolute_path = destination + ('' if destination[-1] is '/' else '/')\
+ source_node_name
try:
self._find_node_by_path(source_node_new_absolute_path)
raise DestinationNodeExistsError()
except NodeDoesNotExistError:
self._move_node(source_node, destination_dir, source_node_new_absolute_path)
def link(self, source, destination, symbolic=True):
try:
source_node = self.get_node(source)
except NodeDoesNotExistError:
if symbolic is True:
raise
else:
raise SourceNodeDoesNotExistError
if source_node.is_directory is True and symbolic is False:
raise DirectoryHardLinkError()
parent_absolute_path, link_node_name = self._get_parent_node_names_from_path(destination)
parent = self.get_node(parent_absolute_path)
if symbolic is True:
return self._soft_link_create(source, link_node_name, destination, parent)
else:
return self._hard_link_create(source_node.file_content, link_node_name, destination, parent)
def _move_node(self, node, destination_dir, node_new_absolute_path):
self.__remove_node_from_parent(node)
self.__add_node_to_parent(node, destination_dir)
node.parent_node = destination_dir
node.absolute_path = node_new_absolute_path
def _remove_node(self, node, node_content_size):
if isinstance(node, File):
self.__manage_file_content_on_remove(node)
else:
self.available_size += self.dir_size
self.nodes.remove(node)
self.__remove_node_from_parent(node)
@staticmethod
def __remove_node_from_parent(node):
node.parent_node.files.remove(node) if isinstance(node, File)\
else node.parent_node.directories.remove(node)
node.parent_node.nodes.remove(node)
def __manage_file_content_on_remove(self, file_node):
file_node.file_content.ref_count -= 1
if file_node.file_content.ref_count is 0:
file_size = file_node.file_content.size
self.available_size += file_size
else:
self.available_size += self.dir_size
@staticmethod
def __add_node_to_parent(node, destination_dir):
destination_dir.files.append(node) if isinstance(node, File)\
else destination_dir.directories.append(node)
destination_dir.nodes.append(node)
def file_update(self, file_node, content, trunc=False):
if trunc is True:
self.available_size += file_node.file_content.size - 1
file_node.file_content.content = ''
self.__check_content_insertion(len(content))
content_size = len(content)
self.available_size -= content_size
file_node.file_content.content += content
file_node.file_content.size = len(file_node.file_content.content) + 1
def _file_create(self, content, relative_name, absolute_path, parent):
file_size = len(content) + 1
self.__check_content_insertion(file_size)
self.available_size -= file_size
new_file = File(FileContent(content), relative_name, absolute_path, parent, self)
self.__append_file_to_fs(new_file, parent)
return new_file
def _dir_create(self, relative_name, absolute_path, parent):
self.__check_content_insertion(self.dir_size)
self.available_size -= self.dir_size
new_dir = Directory(relative_name, absolute_path, parent, self)
self.nodes.append(new_dir)
parent.directories.append(new_dir)
parent.nodes.append(new_dir)
return new_dir
def _soft_link_create(self, link_path, relative_name, absolute_path, parent):
link_size = self.dir_size
self.__check_content_insertion(link_size) # TODO: this is not explicitly defined
self.available_size -= link_size
new_link = SoftLink(link_path, relative_name, absolute_path, parent, self)
self.__append_file_to_fs(new_link, parent)
return new_link
def _hard_link_create(self, file_content, relative_name, absolute_path, parent):
hard_link_size = self.dir_size
self.__check_content_insertion(hard_link_size)
self.available_size -= 1
file_content.ref_count += 1
new_hard_link = HardLink(file_content, relative_name, absolute_path, parent, self)
self.__append_file_to_fs(new_hard_link, parent)
return new_hard_link
def __append_file_to_fs(self, new_file, parent):
self.nodes.append(new_file)
parent.files.append(new_file)
parent.nodes.append(new_file)
def __check_content_insertion(self, length):
if self.available_size - length < 0:
raise NotEnoughSpaceError
def _find_node_by_path(self, path):
try:
return next(filter(lambda x: x.absolute_path == path, self.nodes))
except StopIteration:
raise NodeDoesNotExistError()
@staticmethod
def _get_node_names_from_path(path):
if path == '/':
return ['/']
else:
node_names = path.split('/')
node_names[0] = '/'
return node_names
@staticmethod
def _get_parent_node_names_from_path(path):
if path == '/':
return None, None
else:
parent_path = path.rsplit('/', 1)
return '/' if parent_path[0] is '' else parent_path[0], parent_path[1]
class FileSystemError(Exception):
def __init__(self):
self.message = "Error"
def __str__(self):
return self.message
class NodeDoesNotExistError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Node does not exist"
class DestinationNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
def __init__(self):
super().__init__()
self.message = "Destination node does not exist"
class SourceNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
def __init__(self):
super().__init__()
self.message = "Source node does not exist"
class DestinationNodeExistsError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Destination node exists"
class DestinationNotADirectoryError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Destination node is not directory"
class NonExplicitDirectoryDeletionError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Non explicit directory deletion"
class NotEnoughSpaceError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Insufficient space"
class NonEmptyDirectoryDeletionError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Non empty directory deletion"
class LinkPathError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "No linked file"
class DirectoryHardLinkError(FileSystemError):
def __init__(self):
super().__init__()
self.message = "Directory Hard Link"

Лог от изпълнението

........E....E.EE.
======================================================================
ERROR: test_mounting (test.TestFileSystem)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lib/language/python/runner.py", line 65, in thread
    raise TimeoutError
TimeoutError

======================================================================
ERROR: test_remove_empty_directory (test.TestFileSystem)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lib/language/python/runner.py", line 65, in thread
    raise TimeoutError
TimeoutError

======================================================================
ERROR: test_remove_nonempty_directory (test.TestFileSystem)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lib/language/python/runner.py", line 65, in thread
    raise TimeoutError
TimeoutError

======================================================================
ERROR: test_symlink_to_missing_file (test.TestFileSystem)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lib/language/python/runner.py", line 65, in thread
    raise TimeoutError
TimeoutError

----------------------------------------------------------------------
Ran 18 tests in 8.200s

FAILED (errors=4)

История (1 версия и 0 коментара)

Никола обнови решението на 30.04.2015 13:27 (преди почти 9 години)

+class FSNode:
+ def __init__(self, relative_name, absolute_path, parent_node, file_system):
+ self.relative_name = relative_name
+ self.parent_node = parent_node
+ self.absolute_path = absolute_path
+ self.file_system = file_system
+
+
+class FileContent:
+ def __init__(self, content):
+ self.content = content
+ self.size = len(content) + 1
+ self.ref_count = 1
+
+ def content_size(self):
+ return self.size
+
+
+class File(FSNode):
+ def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(relative_name, absolute_path, parent_node, file_system)
+ self.file_content = file_content
+ self.is_directory = False
+
+ @property
+ def size(self):
+ return self.file_content.size
+
+ @property
+ def content(self):
+ return self.file_content.content
+
+ def append(self, content):
+ self.file_system.file_update(self, content, False)
+
+ def truncate(self, content):
+ self.file_system.file_update(self, content, True)
+
+
+class SoftLink(File):
+ def __init__(self, link_path, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(FileContent(''), relative_name, absolute_path, parent_node, file_system)
+ self.link_path = link_path
+ self.is_directory = False
+
+ def __getattr__(self, item):
+ try:
+ linked_file = self.file_system.get_node(self.link_path)
+ except NodeDoesNotExistError:
+ raise LinkPathError()
+ return getattr(linked_file, item)
+
+ @property
+ def content(self):
+ return self.__getattr__('content')
+
+
+class HardLink(File):
+ def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(file_content, relative_name, absolute_path, parent_node, file_system)
+
+class Directory(FSNode):
+ def __init__(self, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(relative_name, absolute_path, parent_node, file_system)
+ self.files = []
+ self.directories = []
+ self.nodes = []
+ self.is_directory = True
+
+
+class FileSystem:
+ def __init__(self, size):
+ self.dir_size = 1
+ self.nodes = [Directory('/', '/', None, self)]
+ self.size = size
+ self.available_size = size - 1
+
+ def get_node(self, path):
+ return self._find_node_by_path(path)
+
+ def create(self, path, directory=False, content=''):
+ try:
+ self._find_node_by_path(path)
+ raise DestinationNodeExistsError()
+ except NodeDoesNotExistError:
+ parent_absolute_path, node_name = self._get_parent_node_names_from_path(path)
+ try:
+ parent = self._find_node_by_path(parent_absolute_path) # TODO: check if parent is dir
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if directory is True:
+ return self._dir_create(node_name, path, parent)
+ else:
+ return self._file_create(content, node_name, path, parent)
+
+ def remove(self, path, directory=False, force=True): # TODO: somewhat undefined behaviour when / is deleted
+ node = self._find_node_by_path(path)
+ if isinstance(node, File):
+ self._remove_node(node, node.file_content.size)
+ elif isinstance(node, Directory):
+ if directory is False:
+ raise NonExplicitDirectoryDeletionError()
+ else:
+ if len(node.nodes) > 0 and force is False:
+ raise NonEmptyDirectoryDeletionError()
+ else:
+ for sub_node in list(node.nodes):
+ self.remove(sub_node.absolute_path, sub_node.is_directory)
+ self._remove_node(node, self.dir_size)
+
+ def move(self, source, destination):
+ try:
+ source_node = self._find_node_by_path(source)
+ except NodeDoesNotExistError:
+ raise SourceNodeDoesNotExistError()
+ try:
+ destination_dir = self._find_node_by_path(destination)
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if destination_dir.is_directory is not True:
+ raise DestinationNotADirectoryError()
+ parent_absolute_path, source_node_name = self._get_parent_node_names_from_path(source)
+ source_node_new_absolute_path = destination + ('' if destination[-1] is '/' else '/')\
+ + source_node_name
+ try:
+ self._find_node_by_path(source_node_new_absolute_path)
+ raise DestinationNodeExistsError()
+ except NodeDoesNotExistError:
+ self._move_node(source_node, destination_dir, source_node_new_absolute_path)
+
+ def link(self, source, destination, symbolic=True):
+ try:
+ source_node = self.get_node(source)
+ except NodeDoesNotExistError:
+ if symbolic is True:
+ raise
+ else:
+ raise SourceNodeDoesNotExistError
+ if source_node.is_directory is True and symbolic is False:
+ raise DirectoryHardLinkError()
+ parent_absolute_path, link_node_name = self._get_parent_node_names_from_path(destination)
+ parent = self.get_node(parent_absolute_path)
+ if symbolic is True:
+ return self._soft_link_create(source, link_node_name, destination, parent)
+ else:
+ return self._hard_link_create(source_node.file_content, link_node_name, destination, parent)
+
+
+ def _move_node(self, node, destination_dir, node_new_absolute_path):
+ self.__remove_node_from_parent(node)
+ self.__add_node_to_parent(node, destination_dir)
+ node.parent_node = destination_dir
+ node.absolute_path = node_new_absolute_path
+
+ def _remove_node(self, node, node_content_size):
+ if isinstance(node, File):
+ self.__manage_file_content_on_remove(node)
+ else:
+ self.available_size += self.dir_size
+ self.nodes.remove(node)
+ self.__remove_node_from_parent(node)
+
+ @staticmethod
+ def __remove_node_from_parent(node):
+ node.parent_node.files.remove(node) if isinstance(node, File)\
+ else node.parent_node.directories.remove(node)
+ node.parent_node.nodes.remove(node)
+
+ def __manage_file_content_on_remove(self, file_node):
+ file_node.file_content.ref_count -= 1
+ if file_node.file_content.ref_count is 0:
+ file_size = file_node.file_content.size
+ self.available_size += file_size
+ else:
+ self.available_size += self.dir_size
+
+ @staticmethod
+ def __add_node_to_parent(node, destination_dir):
+ destination_dir.files.append(node) if isinstance(node, File)\
+ else destination_dir.directories.append(node)
+ destination_dir.nodes.append(node)
+
+ def file_update(self, file_node, content, trunc=False):
+ if trunc is True:
+ self.available_size += file_node.file_content.size - 1
+ file_node.file_content.content = ''
+ self.__check_content_insertion(len(content))
+ content_size = len(content)
+ self.available_size -= content_size
+ file_node.file_content.content += content
+ file_node.file_content.size = len(file_node.file_content.content) + 1
+
+ def _file_create(self, content, relative_name, absolute_path, parent):
+ file_size = len(content) + 1
+ self.__check_content_insertion(file_size)
+ self.available_size -= file_size
+ new_file = File(FileContent(content), relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_file, parent)
+ return new_file
+
+ def _dir_create(self, relative_name, absolute_path, parent):
+ self.__check_content_insertion(self.dir_size)
+ self.available_size -= self.dir_size
+ new_dir = Directory(relative_name, absolute_path, parent, self)
+ self.nodes.append(new_dir)
+ parent.directories.append(new_dir)
+ parent.nodes.append(new_dir)
+ return new_dir
+
+ def _soft_link_create(self, link_path, relative_name, absolute_path, parent):
+ link_size = self.dir_size
+ self.__check_content_insertion(link_size) # TODO: this is not explicitly defined
+ self.available_size -= link_size
+ new_link = SoftLink(link_path, relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_link, parent)
+ return new_link
+
+ def _hard_link_create(self, file_content, relative_name, absolute_path, parent):
+ hard_link_size = self.dir_size
+ self.__check_content_insertion(hard_link_size)
+ self.available_size -= 1
+ file_content.ref_count += 1
+ new_hard_link = HardLink(file_content, relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_hard_link, parent)
+ return new_hard_link
+
+ def __append_file_to_fs(self, new_file, parent):
+ self.nodes.append(new_file)
+ parent.files.append(new_file)
+ parent.nodes.append(new_file)
+
+ def __check_content_insertion(self, length):
+ if self.available_size - length < 0:
+ raise NotEnoughSpaceError
+
+ def _find_node_by_path(self, path):
+ try:
+ return next(filter(lambda x: x.absolute_path == path, self.nodes))
+ except StopIteration:
+ raise NodeDoesNotExistError()
+
+ @staticmethod
+ def _get_node_names_from_path(path):
+ if path == '/':
+ return ['/']
+ else:
+ node_names = path.split('/')
+ node_names[0] = '/'
+ return node_names
+
+ @staticmethod
+ def _get_parent_node_names_from_path(path):
+ if path == '/':
+ return None, None
+ else:
+ parent_path = path.rsplit('/', 1)
+ return '/' if parent_path[0] is '' else parent_path[0], parent_path[1]
+
+
+class FileSystemError(Exception):
+ def __init__(self):
+ self.message = "Error"
+
+ def __str__(self):
+ return self.message
+
+
+class NodeDoesNotExistError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Node does not exist"
+
+
+class DestinationNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node does not exist"
+
+
+class SourceNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Source node does not exist"
+
+
+class DestinationNodeExistsError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node exists"
+
+
+class DestinationNotADirectoryError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node is not directory"
+
+
+class NonExplicitDirectoryDeletionError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Non explicit directory deletion"
+
+
+class NotEnoughSpaceError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Insufficient space"
+
+
+class NonEmptyDirectoryDeletionError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Non empty directory deletion"
+
+
+class LinkPathError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "No linked file"
+
+
+class DirectoryHardLinkError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Directory Hard Link"