from io import BytesIO, SEEK_END
import os
from enum import Enum
[docs]class FileMode(Enum):
READONLY = 'r'
WRITEONLY = 'w'
READWRITE = 'r+'
WRITEREAD = 'w+'
READBYTES = 'rb'
WRITEBYTES = 'wb'
READWRITEBYTES = 'r+b'
WRITEREADBYTES = 'w+b'
APPENDONLY = 'a'
APPENDANDREAD = 'a+'
APPENDBYTES = 'ab'
APPENDREADBYTES = 'a+b'
# Class to ease the process of handling files.
[docs]class FileHandler:
# Class constructor
# file_path: Path to a file.
# file_mode: FileMode that determines the way we are accessing the file.
# create: If set to True, will create any directories not existing in the specified file path recursively.
# If create is set to False, the file and every subdirectory must exist.
def __init__(self, file_path: str, file_mode: FileMode = FileMode.READWRITEBYTES, create: bool = False):
try:
if file_path is None:
raise BadFilePathException(f"file_path was set to None type")
if file_path == "":
raise BadFilePathException(f"file_path string was empty")
self.path = os.path.normpath(os.path.dirname(file_path))
self.fullfilename = os.path.basename(file_path)
self.filename = os.path.splitext(self.fullfilename)[0]
self.extension = os.path.splitext(self.fullfilename)[1][1:]
if not create:
if not os.path.exists(file_path):
raise FileNotFoundException(f"The file handler could not find the specified file: {file_path}")
if not os.path.isfile(file_path):
raise NotAFileException(f"Attempting to open something that is not a file: {file_path}")
if not os.access(os.path.dirname(file_path), os.R_OK):
raise DirectoryLockedException(f"Could not access some of the firectories in {os.path.dirname} (Permission denied)")
if not os.access(file_path, os.F_OK):
raise FileLockedException(f"Could not access {file_path} (Permission denied)")
else:
path_tree = self.path.split(os.sep)
curpath = ""
for path in path_tree:
curpath += path + os.sep
if not os.path.exists(curpath):
os.mkdir(curpath)
else:
if not os.access(curpath, os.W_OK):
raise DirectoryLockedException(f"Could not access {curpath} (Permission denied)")
if not os.path.exists(os.path.dirname(file_path)):
os.mkdir(os.path.dirname(file_path))
self.file = open(file_path, file_mode.value)
self.file_size = self.__get_file_size()
self.buffer_size = 512
except Exception as exc:
raise exc
def __enter__(self):
return self
[docs] def setBufferSize(self, bufferSize: int) -> int:
self.buffer_size = bufferSize
return self.buffer_size
[docs] def read(self, amount: int = None, readonly: bool = True) -> BytesIO or bytearray or None:
if self.file is not None:
try:
if amount is None:
amount = self.buffer_size
buf_in = None
if readonly:
buf_in = BytesIO()
else:
buf_in = bytearray(amount)
buf_in = self.file.read(amount)
return buf_in
except Exception as exc:
raise exc
else:
return None
[docs] def readandrewind(self, amount: int = None, readonly: bool = True) -> BytesIO or bytearray or None:
if self.file is not None:
try:
if amount is None:
amount = self.buffer_size
buf_in = None
if readonly:
buf_in = BytesIO()
else:
buf_in = bytearray(amount)
buf_in = self.file.read(amount)
self.rewind()
return buf_in
except Exception as exc:
raise exc
else:
return None
[docs] def write(self, buffer_out: BytesIO or bytearray) -> int:
if self.file is not None:
try:
w = self.file.write(buffer_out)
return w
except Exception as exc:
raise exc
else:
return 0
[docs] def zero(self,length: int) -> int:
if self.file is not None:
try:
c = self.file.tell()
w = self.file.write(bytearray(length))
self.file.seek(c, 0)
return w
except Exception as exc:
raise exc
else:
return 0
[docs] def printblock(self, offset: int, length: int):
if self.file is not None:
try:
if length is None:
length = self.buffer_size
p = self.file.tell()
self.file.seek(offset, 0)
buf_in = self.file.read(length)
self.file.seek(p,0)
line = ""
for i in range(0, len(buf_in)):
line = str(f"{line} {buf_in[i]:02X}")
if i % 16 == 0 or i == len(buf_in)-1:
print(line)
line = ""
except Exception as exc:
raise exc
else:
return None
[docs] def seek(self, destination: int, from_offset: int = 0) -> int:
if self.file is not None:
try:
self.file.seek(int(destination), int(from_offset))
p = self.file.tell()
return p
except Exception as exc:
raise exc
else:
return -1
[docs] def rewind(self) -> bool:
if self.file is not None:
try:
self.file.seek(0)
return True if self.file.tell() == 0 else False
except Exception as exc:
raise exc
else:
return False
[docs] def seek_end(self) -> bool:
if self.file is not None:
try:
s = self.file.tell()
self.file.seek(0, SEEK_END)
e = self.file.tell()
return True if s < e else False
except Exception as exc:
raise exc
else:
return False
def __get_file_size(self) -> int:
if self.file is not None:
try:
self.seek_end()
sz = self.file.tell()
self.rewind()
return sz
except Exception as exc:
raise exc
else:
return -1
[docs] def tell(self) -> int:
if self.file is not None:
try:
return self.file.tell()
except Exception as exc:
raise exc
else:
return -1
[docs] def close(self) -> bool:
if self.file is not None:
try:
if self.file.closed != True:
self.file.close()
self.file = None
return True
except Exception as exc:
raise exc
else:
return False
[docs] def truncate(self, size: int = 0):
if self.file is not None:
try:
return self.file.truncate(size)
except Exception as exc:
raise exc
else:
return
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def __del__(self):
self.close()
[docs]class FileHandlerError(Exception):
"""Generic file handler error"""
pass
[docs]class BadFilePathException(FileHandlerError):
"""Raised when the file path has a wrong format"""
pass
[docs]class FileNotFoundException(FileHandlerError):
"""Raised when the file does not exists"""
pass
[docs]class NotAFileException(FileHandlerError):
"""Raised when attempting to open something that is not a file"""
pass
[docs]class FileLockedException(FileHandlerError):
"""Raised when the FileHandler can not open the file for some reason, mostly due to permission errors or the file being in use"""
pass
[docs]class DirectoryLockedException(FileHandlerError):
"""Raised when the FileHandler does not have access to a directory specified in the file path. Normally this is due to permission errors"""
pass