Allow arbitrary database type to be passed in preparation for Academon integration

This commit is contained in:
Kumi 2022-09-19 07:25:19 +00:00
parent b736226140
commit d5e560d945
Signed by: kumi
GPG key ID: ECBCC9082395383F
5 changed files with 32 additions and 20 deletions

View file

@ -5,15 +5,18 @@ from typing import Union
from classes.vessel import Vessel from classes.vessel import Vessel
from classes.directory import Directory from classes.directory import Directory
from classes.database import Database
class MonsterConfig: class MonsterConfig:
def readFile(self, path: Union[str, Path]) -> None: def readFile(self, path: Union[str, Path], dbclass: type = Database) -> None:
"""Read .ini file into MonsterConfig object """Read .ini file into MonsterConfig object
Args: Args:
path (str, pathlib.Path): Location of the .ini file to read path (str, pathlib.Path): Location of the .ini file to read
(absolute or relative to the working directory) (absolute or relative to the working directory)
dbclass (type): Class to use for database connections. Defaults to
built-in Database using sqlite3.
Raises: Raises:
ValueError: Raised if the passed file is not a ContentMonster .ini ValueError: Raised if the passed file is not a ContentMonster .ini
@ -43,12 +46,13 @@ class MonsterConfig:
# Read Vessels from the config file # Read Vessels from the config file
elif section.startswith("Vessel"): elif section.startswith("Vessel"):
self.vessels.append(Vessel.fromConfig(parser[section])) self.vessels.append(
Vessel.fromConfig(parser[section], dbclass))
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize a new (empty) MonsterConfig object """Initialize a new (empty) MonsterConfig object
""" """
self.directories = [] self.directories = []
self.vessels = [] self.vessels = []
self.chunksize = 10485760 # Default: 10 MiB self.chunksize = 10485760 # Default: 10 MiB
self.database = None # Default: "database.sqlite3" in base directory self.database = None # Default: "database.sqlite3" in base directory

View file

@ -11,7 +11,7 @@ class File:
"""Object representing a file found in a local Directory """Object representing a file found in a local Directory
""" """
def __init__(self, name: str, directory, uuid: Optional[str] = None) -> None: def __init__(self, name: str, directory, uuid: Optional[str] = None, dbclass: type = Database) -> None:
"""Initialize new File object """Initialize new File object
Args: Args:
@ -20,12 +20,15 @@ class File:
is located within is located within
uuid (str, optional): Unique identifier of this File object. Will uuid (str, optional): Unique identifier of this File object. Will
be retrieved from database if None. Defaults to None. be retrieved from database if None. Defaults to None.
dbclass (type): Class to use for database connections. Defaults to
built-in Database using sqlite3.
Raises: Raises:
FileNotFoundError: Raised if the specified File does not exist FileNotFoundError: Raised if the specified File does not exist
""" """
self.name = name self.name = name
self.directory = directory self.directory = directory
self.dbclass = dbclass
if not self.exists(): if not self.exists():
raise FileNotFoundError(f"File {self.name} does not exist in {self.directory.name}!") raise FileNotFoundError(f"File {self.name} does not exist in {self.directory.name}!")
@ -47,9 +50,9 @@ class File:
"""Return unique identifier for this File object """Return unique identifier for this File object
Returns: Returns:
str: File object's UUID retrieved from Database str: File object's UUID retrieved from database
""" """
db = Database() db = self.dbclass()
return db.getFileUUID(self) return db.getFileUUID(self)
def getFullPath(self) -> str: def getFullPath(self) -> str:

View file

@ -16,7 +16,7 @@ import os.path
class ShoreThread(Process): class ShoreThread(Process):
"""Thread handling the discovery of shore-side file changes """Thread handling the discovery of shore-side file changes
""" """
def __init__(self, state: dict) -> None: def __init__(self, state: dict, dbclass: type = Database) -> None:
"""Create a new ShoreThread object """Create a new ShoreThread object
Args: Args:
@ -27,6 +27,7 @@ class ShoreThread(Process):
self._state = state self._state = state
self.queue = Queue() self.queue = Queue()
self._logger = Logger() self._logger = Logger()
self._dbclass = dbclass
def getAllFiles(self) -> list: def getAllFiles(self) -> list:
"""Return File objects for all files in all Directories """Return File objects for all files in all Directories
@ -107,7 +108,7 @@ class ShoreThread(Process):
# Remove file from database # Remove file from database
self._logger.debug(f"Purging file {name} from database") self._logger.debug(f"Purging file {name} from database")
db = Database() db = self._dbclass()
db.removeFile(directory, name) db.removeFile(directory, name)
def addFile(self, fileobj): def addFile(self, fileobj):
@ -142,7 +143,7 @@ class ShoreThread(Process):
self._state["files"].append(f) self._state["files"].append(f)
def checkFileCompletion(self, fileobj: File) -> bool: def checkFileCompletion(self, fileobj: File) -> bool:
db = Database() db = self._dbclass()
complete = db.getCompletionByFileUUID(fileobj.uuid) complete = db.getCompletionByFileUUID(fileobj.uuid)
del(db) del(db)

View file

@ -14,12 +14,14 @@ class Vessel:
"""Class describing a Vessel (= a replication destination) """Class describing a Vessel (= a replication destination)
""" """
@classmethod @classmethod
def fromConfig(cls, config: SectionProxy): def fromConfig(cls, config: SectionProxy, dbclass: type = Database):
"""Create Vessel object from a Vessel section in the Config file """Create Vessel object from a Vessel section in the Config file
Args: Args:
config (configparser.SectionProxy): Vessel section defining a config (configparser.SectionProxy): Vessel section defining a
Vessel Vessel
dbclass (type): Class to use for database connections. Defaults to
built-in Database using sqlite3.
Raises: Raises:
ValueError: Raised if section does not contain Address parameter ValueError: Raised if section does not contain Address parameter
@ -60,7 +62,7 @@ class Vessel:
if "Address" in config.keys(): if "Address" in config.keys():
return cls(config.name.split()[1], config["Address"], username, return cls(config.name.split()[1], config["Address"], username,
password, passphrase, port, timeout, tempdir, ignoredirs) password, passphrase, port, timeout, tempdir, ignoredirs, dbclass)
else: else:
raise ValueError("Definition for Vessel " + raise ValueError("Definition for Vessel " +
config.name.split()[1] + " does not contain Address!") config.name.split()[1] + " does not contain Address!")
@ -69,7 +71,7 @@ class Vessel:
password: Optional[str] = None, passphrase: Optional[str] = None, password: Optional[str] = None, passphrase: Optional[str] = None,
port: Optional[int] = None, timeout: Optional[int] = None, port: Optional[int] = None, timeout: Optional[int] = None,
tempdir: Optional[Union[str, pathlib.Path]] = None, tempdir: Optional[Union[str, pathlib.Path]] = None,
ignoredirs: list[Optional[str]] = []) -> None: ignoredirs: list[Optional[str]] = [], dbclass: type = Database) -> None:
"""Initialize new Vessel object """Initialize new Vessel object
Args: Args:
@ -89,6 +91,7 @@ class Vessel:
self._connection = None self._connection = None
self._uploaded = self.getUploadedFromDB() # Files already uploaded self._uploaded = self.getUploadedFromDB() # Files already uploaded
self._ignoredirs = ignoredirs # Directories not replicated to this vessel self._ignoredirs = ignoredirs # Directories not replicated to this vessel
self._dbclass = dbclass
@property @property
def connection(self) -> Connection: def connection(self) -> Connection:
@ -116,7 +119,7 @@ class Vessel:
Returns: Returns:
list: List of UUIDs of Files that have been successfully uploaded list: List of UUIDs of Files that have been successfully uploaded
""" """
db = Database() db = self._dbclass()
return db.getCompletionForVessel(self) return db.getCompletionForVessel(self)
def currentUpload(self) -> Optional[tuple[str, str, str]]: def currentUpload(self) -> Optional[tuple[str, str, str]]:
@ -129,9 +132,9 @@ class Vessel:
checksum is the SHA256 hash of the file at the time of insertion checksum is the SHA256 hash of the file at the time of insertion
into the database. None is returned if no such record is found. into the database. None is returned if no such record is found.
""" """
self.assertTempDirectory() # After a reboot, the tempdir may be gone self.assertTempDirectory() # After a reboot, the tempdir may be gone
db = Database() db = self._dbclass()
output = db.getFileByUUID(self.connection.getCurrentUploadUUID()) output = db.getFileByUUID(self.connection.getCurrentUploadUUID())
del db del db

View file

@ -16,7 +16,7 @@ class VesselThread(Process):
"""Thread processing uploads to a single vessel """Thread processing uploads to a single vessel
""" """
def __init__(self, vessel: Vessel, state: dict) -> None: def __init__(self, vessel: Vessel, state: dict, dbclass: type = Database) -> None:
"""Initialize a new VesselThread """Initialize a new VesselThread
Args: Args:
@ -27,6 +27,7 @@ class VesselThread(Process):
self.vessel = vessel self.vessel = vessel
self._state = state self._state = state
self._logger = Logger() self._logger = Logger()
self._dbclass = dbclass
def run(self) -> NoReturn: def run(self) -> NoReturn:
"""Run thread and process uploads to the vessel """Run thread and process uploads to the vessel
@ -95,7 +96,7 @@ class VesselThread(Process):
f"Start processing file {fileobj.name} in directory {fileobj.directory.name} on vessel {self.vessel.name}") f"Start processing file {fileobj.name} in directory {fileobj.directory.name} on vessel {self.vessel.name}")
while True: while True:
db = Database() db = self._dbclass()
if not db.getFileByUUID(fileobj.uuid): if not db.getFileByUUID(fileobj.uuid):
self._logger.debug( self._logger.debug(
f"File {fileobj.name} in directory {fileobj.directory.name} does not exist anymore - deleting from {self.vessel.name}") f"File {fileobj.name} in directory {fileobj.directory.name} does not exist anymore - deleting from {self.vessel.name}")
@ -111,7 +112,7 @@ class VesselThread(Process):
f"File {fileobj.name} uploaded to vessel {self.vessel.name} completely - finalizing") f"File {fileobj.name} uploaded to vessel {self.vessel.name} completely - finalizing")
remotefile.finalizeUpload() remotefile.finalizeUpload()
db = Database() db = self._dbclass()
db.logCompletion(fileobj, self.vessel) db.logCompletion(fileobj, self.vessel)
del(db) del(db)
@ -143,7 +144,7 @@ class VesselThread(Process):
self.vessel.compileComplete(remotefile) self.vessel.compileComplete(remotefile)
def checkFileCompletion(self, fileobj: File) -> None: def checkFileCompletion(self, fileobj: File) -> None:
db = Database() db = self._dbclass()
complete = db.getCompletionByFileUUID(fileobj.uuid) complete = db.getCompletionByFileUUID(fileobj.uuid)
del(db) del(db)