From 712986ee69f3ea41e3dfc5dc9c06329daff84517 Mon Sep 17 00:00:00 2001 From: Timmy Welch Date: Wed, 17 Aug 2022 15:53:19 -0700 Subject: [PATCH] Turn comicapi.archivers.* into plugins --- comicapi/archivers/__init__.py | 24 +++-- comicapi/archivers/archiver.py | 62 +++++++++++++ comicapi/archivers/folder.py | 17 ++-- comicapi/archivers/rar.py | 27 ++++-- comicapi/archivers/sevenzip.py | 26 ++++-- comicapi/archivers/unknown.py | 35 -------- comicapi/archivers/zip.py | 23 +++-- comicapi/comicarchive.py | 132 +++++++++------------------- comictaggerlib/cli.py | 16 +--- comictaggerlib/ctoptions/cmdline.py | 20 +++-- comictaggerlib/fileselectionlist.py | 16 +--- comictaggerlib/main.py | 3 + comictaggerlib/renamewindow.py | 11 +-- comictaggerlib/taggerwindow.py | 11 +-- setup.py | 14 ++- tests/comicarchive_test.py | 26 +++--- tests/conftest.py | 9 +- 17 files changed, 237 insertions(+), 235 deletions(-) create mode 100644 comicapi/archivers/archiver.py delete mode 100644 comicapi/archivers/unknown.py diff --git a/comicapi/archivers/__init__.py b/comicapi/archivers/__init__.py index 8758c55..c445eb5 100644 --- a/comicapi/archivers/__init__.py +++ b/comicapi/archivers/__init__.py @@ -1,19 +1,15 @@ from __future__ import annotations -from comicapi.archivers.unknown import UnknownArchiver - -__all__ = ["UnknownArchiver"] +from comicapi.archivers.archiver import Archiver from comicapi.archivers.folder import FolderArchiver -from comicapi.archivers.rar import RarArchiver, rar_support -from comicapi.archivers.sevenzip import SevenZipArchiver, z7_support +from comicapi.archivers.rar import RarArchiver +from comicapi.archivers.sevenzip import SevenZipArchiver from comicapi.archivers.zip import ZipArchiver -__all__ = [ - "UnknownArchiver", - "FolderArchiver", - "RarArchiver", - "rar_support", - "ZipArchiver", - "SevenZipArchiver", - "z7_support", -] + +class UnknownArchiver(Archiver): + def name(self) -> str: + return "Unknown" + + +__all__ = ["Archiver", "UnknownArchiver", "FolderArchiver", "RarArchiver", "ZipArchiver", "SevenZipArchiver"] diff --git a/comicapi/archivers/archiver.py b/comicapi/archivers/archiver.py new file mode 100644 index 0000000..dd11c1b --- /dev/null +++ b/comicapi/archivers/archiver.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import pathlib +from typing import Protocol, runtime_checkable + + +@runtime_checkable +class Archiver(Protocol): + + """Archiver Protocol""" + + path: pathlib.Path + enabled: bool = True + + def __init__(self): + self.path = pathlib.Path() + + def get_comment(self) -> str: + return "" + + def set_comment(self, comment: str) -> bool: + return False + + def supports_comment(self) -> bool: + return False + + def read_file(self, archive_file: str) -> bytes: + raise NotImplementedError + + def remove_file(self, archive_file: str) -> bool: + return False + + def write_file(self, archive_file: str, data: bytes) -> bool: + return False + + def get_filename_list(self) -> list[str]: + return [] + + def rebuild(self, exclude_list: list[str]) -> bool: + return False + + def copy_from_archive(self, other_archive: Archiver) -> bool: + return False + + def is_writable(self) -> bool: + return False + + def extension(self) -> str: + return "" + + def name(self) -> str: + return "" + + @classmethod + def is_valid(cls, path: pathlib.Path) -> bool: + return False + + @classmethod + def open(cls, path: pathlib.Path) -> Archiver: + archiver = cls() + archiver.path = path + return archiver diff --git a/comicapi/archivers/folder.py b/comicapi/archivers/folder.py index 4b0186f..c0292dc 100644 --- a/comicapi/archivers/folder.py +++ b/comicapi/archivers/folder.py @@ -4,17 +4,17 @@ import logging import os import pathlib -from comicapi.archivers import UnknownArchiver +from comicapi.archivers import Archiver logger = logging.getLogger(__name__) -class FolderArchiver(UnknownArchiver): +class FolderArchiver(Archiver): """Folder implementation""" - def __init__(self, path: pathlib.Path | str) -> None: - super().__init__(path) + def __init__(self) -> None: + super().__init__() self.comment_file_name = "ComicTaggerFolderComment.txt" def get_comment(self) -> str: @@ -70,7 +70,7 @@ class FolderArchiver(UnknownArchiver): logger.error("Error listing files in folder archive [%s]: %s", e, self.path) return [] - def copy_from_archive(self, other_archive: UnknownArchiver) -> bool: + def copy_from_archive(self, other_archive: Archiver) -> bool: """Replace the current zip with one copied from another archive""" try: for filename in other_archive.get_filename_list(): @@ -88,3 +88,10 @@ class FolderArchiver(UnknownArchiver): return False else: return True + + def name(self) -> str: + return "Folder" + + @classmethod + def is_valid(cls, path: pathlib.Path | str) -> bool: + return os.path.isdir(path) diff --git a/comicapi/archivers/rar.py b/comicapi/archivers/rar.py index db04b8b..84537ae 100644 --- a/comicapi/archivers/rar.py +++ b/comicapi/archivers/rar.py @@ -9,7 +9,7 @@ import subprocess import tempfile import time -from comicapi.archivers import UnknownArchiver +from comicapi.archivers import Archiver try: from unrar.cffi import rarfile @@ -25,11 +25,13 @@ if not rar_support: logger.error("unrar-cffi unavailable") -class RarArchiver(UnknownArchiver): +class RarArchiver(Archiver): """RAR implementation""" - def __init__(self, path: pathlib.Path | str, rar_exe_path: str = "rar") -> None: - super().__init__(path) + enabled = rar_support + + def __init__(self, rar_exe_path: str = "rar") -> None: + super().__init__() self.rar_exe_path = shutil.which(rar_exe_path) or "" # windows only, keeps the cmd.exe from popping up @@ -199,7 +201,7 @@ class RarArchiver(UnknownArchiver): return namelist return [] - def copy_from_archive(self, other_archive: UnknownArchiver) -> bool: + def copy_from_archive(self, other_archive: Archiver) -> bool: """Replace the current archive with one copied from another archive""" try: with tempfile.TemporaryDirectory() as tmp_dir: @@ -234,6 +236,21 @@ class RarArchiver(UnknownArchiver): else: return True + def is_writable(self) -> bool: + return bool(self.rar_exe_path and os.path.exists(self.rar_exe_path)) + + def extension(self) -> str: + return ".cbr" + + def name(self) -> str: + return "RAR" + + @classmethod + def is_valid(cls, path: pathlib.Path | str) -> bool: + if rar_support: + return rarfile.is_rarfile(str(path)) + return False + def get_rar_obj(self) -> rarfile.RarFile | None: if rar_support: try: diff --git a/comicapi/archivers/sevenzip.py b/comicapi/archivers/sevenzip.py index 9e0f3f0..a8b574c 100644 --- a/comicapi/archivers/sevenzip.py +++ b/comicapi/archivers/sevenzip.py @@ -6,7 +6,7 @@ import pathlib import shutil import tempfile -from comicapi.archivers import UnknownArchiver +from comicapi.archivers import Archiver try: import py7zr @@ -18,12 +18,13 @@ except ImportError: logger = logging.getLogger(__name__) -class SevenZipArchiver(UnknownArchiver): - +class SevenZipArchiver(Archiver): """7Z implementation""" - def __init__(self, path: pathlib.Path | str) -> None: - super().__init__(path) + enabled = z7_support + + def __init__(self) -> None: + super().__init__() # @todo: Implement Comment? def get_comment(self) -> str: @@ -100,7 +101,7 @@ class SevenZipArchiver(UnknownArchiver): return False return True - def copy_from_archive(self, other_archive: UnknownArchiver) -> bool: + def copy_from_archive(self, other_archive: Archiver) -> bool: """Replace the current zip with one copied from another archive""" try: with py7zr.SevenZipFile(self.path, "w") as zout: @@ -115,3 +116,16 @@ class SevenZipArchiver(UnknownArchiver): return False else: return True + + def is_writable(self) -> bool: + return True + + def extension(self) -> str: + return ".cb7" + + def name(self) -> str: + return "Seven Zip" + + @classmethod + def is_valid(cls, path: pathlib.Path | str) -> bool: + return py7zr.is_7zfile(path) diff --git a/comicapi/archivers/unknown.py b/comicapi/archivers/unknown.py deleted file mode 100644 index 5f79720..0000000 --- a/comicapi/archivers/unknown.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import pathlib - - -class UnknownArchiver: - - """Unknown implementation""" - - def __init__(self, path: pathlib.Path | str) -> None: - self.path = pathlib.Path(path) - - def get_comment(self) -> str: - return "" - - def set_comment(self, comment: str) -> bool: - return False - - def read_file(self, archive_file: str) -> bytes: - raise NotImplementedError - - def remove_file(self, archive_file: str) -> bool: - return False - - def write_file(self, archive_file: str, data: bytes) -> bool: - return False - - def get_filename_list(self) -> list[str]: - return [] - - def rebuild(self, exclude_list: list[str]) -> bool: - return False - - def copy_from_archive(self, other_archive: UnknownArchiver) -> bool: - return False diff --git a/comicapi/archivers/zip.py b/comicapi/archivers/zip.py index 8d3d976..0ab8941 100644 --- a/comicapi/archivers/zip.py +++ b/comicapi/archivers/zip.py @@ -9,17 +9,17 @@ import tempfile import zipfile from typing import cast -from comicapi.archivers import UnknownArchiver +from comicapi.archivers import Archiver logger = logging.getLogger(__name__) -class ZipArchiver(UnknownArchiver): +class ZipArchiver(Archiver): """ZIP implementation""" - def __init__(self, path: pathlib.Path | str) -> None: - super().__init__(path) + def __init__(self) -> None: + super().__init__() def get_comment(self) -> str: with zipfile.ZipFile(self.path, "r") as zf: @@ -99,7 +99,7 @@ class ZipArchiver(UnknownArchiver): return False return True - def copy_from_archive(self, other_archive: UnknownArchiver) -> bool: + def copy_from_archive(self, other_archive: Archiver) -> bool: """Replace the current zip with one copied from another archive""" try: with zipfile.ZipFile(self.path, mode="w", allowZip64=True) as zout: @@ -119,6 +119,19 @@ class ZipArchiver(UnknownArchiver): else: return True + def is_writable(self) -> bool: + return True + + def extension(self) -> str: + return ".cbz" + + def name(self) -> str: + return "ZIP" + + @classmethod + def is_valid(cls, path: pathlib.Path | str) -> bool: + return zipfile.is_zipfile(path) + def write_zip_comment(self, filename: pathlib.Path | str, comment: str) -> bool: """ This is a custom function for writing a comment to a zip file, diff --git a/comicapi/comicarchive.py b/comicapi/comicarchive.py index 1009b5d..05da647 100644 --- a/comicapi/comicarchive.py +++ b/comicapi/comicarchive.py @@ -19,31 +19,23 @@ import logging import os import pathlib import shutil -import zipfile +import sys from typing import cast import natsort import wordninja from comicapi import filenamelexer, filenameparser, utils -from comicapi.archivers import FolderArchiver, RarArchiver, SevenZipArchiver, UnknownArchiver, ZipArchiver +from comicapi.archivers import Archiver, UnknownArchiver, ZipArchiver from comicapi.comet import CoMet from comicapi.comicbookinfo import ComicBookInfo from comicapi.comicinfoxml import ComicInfoXml from comicapi.genericmetadata import GenericMetadata, PageType -try: - import py7zr - - z7_support = True -except ImportError: - z7_support = False -try: - from unrar.cffi import rarfile - - rar_support = True -except ImportError: - rar_support = False +if sys.version_info < (3, 10): + from importlib_metadata import entry_points +else: + from importlib.metadata import entry_points try: from PIL import Image @@ -52,12 +44,26 @@ try: except ImportError: pil_available = False - logger = logging.getLogger(__name__) if not pil_available: logger.error("PIL unavalable") +archivers: list[type[Archiver]] = [] + + +def load_archive_plugins() -> None: + for arch in entry_points(group="comicapi_archivers"): + try: + archiver: type[Archiver] = arch.load() + if archiver.enabled: + if not arch.module.startswith("comicapi"): + archivers.insert(0, archiver) + else: + archivers.append(archiver) + except Exception: + logger.warning("Failed to load talker: %s", arch.name) + class MetaDataStyle: CBI = 0 @@ -70,9 +76,6 @@ class MetaDataStyle: class ComicArchive: logo_data = b"" - class ArchiveType: - SevenZip, Zip, Rar, Folder, Pdf, Unknown = list(range(6)) - def __init__( self, path: pathlib.Path | str, @@ -96,36 +99,12 @@ class ComicArchive: self.reset_cache() self.default_image_path = default_image_path - # Use file extension to decide which archive test we do first - ext = self.path.suffix + self.archiver: Archiver = UnknownArchiver.open(self.path) - self.archive_type = self.ArchiveType.Unknown - self.archiver = UnknownArchiver(self.path) - - if ext in [".cbr", ".rar"]: - if self.rar_test(): - self.archive_type = self.ArchiveType.Rar - self.archiver = RarArchiver(self.path, rar_exe_path=self.rar_exe_path) - - elif self.zip_test(): - self.archive_type = self.ArchiveType.Zip - self.archiver = ZipArchiver(self.path) - else: - if self.sevenzip_test(): - self.archive_type = self.ArchiveType.SevenZip - self.archiver = SevenZipArchiver(self.path) - - elif self.zip_test(): - self.archive_type = self.ArchiveType.Zip - self.archiver = ZipArchiver(self.path) - - elif self.rar_test(): - self.archive_type = self.ArchiveType.Rar - self.archiver = RarArchiver(self.path, rar_exe_path=self.rar_exe_path) - - elif self.folder_test(): - self.archive_type = self.ArchiveType.Folder - self.archiver = FolderArchiver(self.path) + for archiver in archivers: + if archiver.is_valid(self.path): + self.archiver = archiver.open(self.path) + break if not ComicArchive.logo_data and self.default_image_path: with open(self.default_image_path, mode="rb") as fd: @@ -157,63 +136,33 @@ class ComicArchive: self.path = new_path self.archiver.path = pathlib.Path(path) - def sevenzip_test(self) -> bool: - return z7_support and py7zr.is_7zfile(self.path) - - def zip_test(self) -> bool: - return zipfile.is_zipfile(self.path) - - def rar_test(self) -> bool: - return rar_support and rarfile.is_rarfile(str(self.path)) - - def folder_test(self) -> bool: - return self.path.is_dir() - - def is_sevenzip(self) -> bool: - return self.archive_type == self.ArchiveType.SevenZip - - def is_zip(self) -> bool: - return self.archive_type == self.ArchiveType.Zip - - def is_rar(self) -> bool: - return self.archive_type == self.ArchiveType.Rar - - def is_pdf(self) -> bool: - return self.archive_type == self.ArchiveType.Pdf - - def is_folder(self) -> bool: - return self.archive_type == self.ArchiveType.Folder - - def is_writable(self, check_rar_status: bool = True) -> bool: - if self.archive_type == self.ArchiveType.Unknown: + def is_writable(self, check_archive_status: bool = True) -> bool: + if isinstance(self.archiver, UnknownArchiver): return False - if check_rar_status and self.is_rar() and not self.rar_exe_path: + if check_archive_status and not self.archiver.is_writable(): return False - if not os.access(self.path, os.W_OK): - return False - - if (self.archive_type != self.ArchiveType.Folder) and (not os.access(self.path.parent, os.W_OK)): + if not (os.access(self.path, os.W_OK) or os.access(self.path.parent, os.W_OK)): return False return True def is_writable_for_style(self, data_style: int) -> bool: + return not (data_style == MetaDataStyle.CBI and not self.archiver.supports_comment) - if (self.is_rar() or self.is_sevenzip()) and data_style == MetaDataStyle.CBI: - return False - - return self.is_writable() + def is_zip(self) -> bool: + return self.archiver.name() == "ZIP" def seems_to_be_a_comic_archive(self) -> bool: - if (self.is_zip() or self.is_rar() or self.is_sevenzip() or self.is_folder()) and ( - self.get_number_of_pages() > 0 - ): + if not (isinstance(self.archiver, UnknownArchiver)) and self.get_number_of_pages() > 0: return True return False + def extension(self) -> str: + return self.archiver.extension() + def read_metadata(self, style: int) -> GenericMetadata: if style == MetaDataStyle.CIX: @@ -334,7 +283,6 @@ class ComicArchive: # seems like some archive creators are on Windows, and don't know about case-sensitivity! if sort_list: - files = cast(list[str], natsort.os_sorted(files)) # make a sub-list of image files @@ -653,10 +601,10 @@ class ComicArchive: return metadata - def export_as_zip(self, zip_filename: pathlib.Path | str) -> bool: - if self.archive_type == self.ArchiveType.Zip: + def export_as_zip(self, zip_filename: pathlib.Path) -> bool: + if self.archiver.name() == "ZIP": # nothing to do, we're already a zip return True - zip_archiver = ZipArchiver(zip_filename) + zip_archiver = ZipArchiver.open(zip_filename) return zip_archiver.copy_from_archive(self.archiver) diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 9da4228..d5c9a13 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -217,14 +217,7 @@ class CLI: if self.batch_mode: brief = f"{ca.path}: " - if ca.is_sevenzip(): - brief += "7Z archive " - elif ca.is_zip(): - brief += "ZIP archive " - elif ca.is_rar(): - brief += "RAR archive " - elif ca.is_folder(): - brief += "Folder archive " + brief += ca.archiver.name() + " archive " brief += f"({page_count: >3} pages)" brief += " tags:[ " @@ -460,12 +453,7 @@ class CLI: new_ext = "" # default if self.options.filename_rename_set_extension_based_on_archive: - if ca.is_sevenzip(): - new_ext = ".cb7" - elif ca.is_zip(): - new_ext = ".cbz" - elif ca.is_rar(): - new_ext = ".cbr" + new_ext = ca.extension() renamer = FileRenamer( md, diff --git a/comictaggerlib/ctoptions/cmdline.py b/comictaggerlib/ctoptions/cmdline.py index f0c1c36..464e668 100644 --- a/comictaggerlib/ctoptions/cmdline.py +++ b/comictaggerlib/ctoptions/cmdline.py @@ -18,6 +18,7 @@ from __future__ import annotations import argparse import logging import os +import pathlib import platform import settngs @@ -325,20 +326,23 @@ def validate_commandline_options(options: settngs.Config[settngs.Values], parser else: options[0].runtime_file_list = options[0].runtime_files - # take a crack at finding rar exe, if not set already - if options[0].general_rar_exe_path.strip() in ("", "rar"): + rar_path = pathlib.Path(options[0].general_rar_exe_path) + if rar_path.is_absolute() and rar_path.exists(): + if rar_path.is_dir(): + utils.add_to_path(str(rar_path)) + else: + utils.add_to_path(str(rar_path.parent)) + + # take a crack at finding rar exe if it's not in the path + if not utils.which("rar"): if platform.system() == "Windows": # look in some likely places for Windows machines if os.path.exists(r"C:\Program Files\WinRAR\Rar.exe"): - options[0].general_rar_exe_path = r"C:\Program Files\WinRAR\Rar.exe" + utils.add_to_path(r"C:\Program Files\WinRAR") elif os.path.exists(r"C:\Program Files (x86)\WinRAR\Rar.exe"): - options[0].general_rar_exe_path = r"C:\Program Files (x86)\WinRAR\Rar.exe" + utils.add_to_path(r"C:\Program Files (x86)\WinRAR") else: if os.path.exists("/opt/homebrew/bin"): utils.add_to_path("/opt/homebrew/bin") - # see if it's in the path of unix user - rarpath = utils.which("rar") - if rarpath is not None: - options[0].general_rar_exe_path = "rar" return options diff --git a/comictaggerlib/fileselectionlist.py b/comictaggerlib/fileselectionlist.py index e5066a5..8e88f6f 100644 --- a/comictaggerlib/fileselectionlist.py +++ b/comictaggerlib/fileselectionlist.py @@ -193,7 +193,7 @@ class FileSelectionList(QtWidgets.QWidget): QtCore.QCoreApplication.processEvents() first_added = None - rar_added = False + rar_added_ro = False self.twList.setSortingEnabled(False) for idx, f in enumerate(filelist): QtCore.QCoreApplication.processEvents() @@ -206,8 +206,7 @@ class FileSelectionList(QtWidgets.QWidget): row = self.add_path_item(f) if row is not None: ca = self.get_archive_by_row(row) - if ca and ca.is_rar(): - rar_added = True + rar_added_ro = bool(ca and ca.archiver.name() == "RAR" and not ca.archiver.is_writable()) if first_added is None: first_added = row @@ -224,7 +223,7 @@ class FileSelectionList(QtWidgets.QWidget): else: QtWidgets.QMessageBox.information(self, "File/Folder Open", "No readable comic archives were found.") - if rar_added and not utils.which(self.options.general_rar_exe_path or "rar"): + if rar_added_ro: self.rar_ro_message() self.twList.setSortingEnabled(True) @@ -339,14 +338,7 @@ class FileSelectionList(QtWidgets.QWidget): filename_item.setText(item_text) filename_item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) - if fi.ca.is_sevenzip(): - item_text = "7Z" - elif fi.ca.is_zip(): - item_text = "ZIP" - elif fi.ca.is_rar(): - item_text = "RAR" - else: - item_text = "" + item_text = fi.ca.archiver.name() type_item.setText(item_text) type_item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) diff --git a/comictaggerlib/main.py b/comictaggerlib/main.py index 7ed6030..7299914 100644 --- a/comictaggerlib/main.py +++ b/comictaggerlib/main.py @@ -160,6 +160,9 @@ class App: f"Failed to load settings, check the log located in '{self.options[0].runtime_config.user_log_dir}' for more details", True, ) + + comicapi.comicarchive.load_archive_plugins() + if self.options[0].runtime_no_gui: if error and error[1]: print(f"A fatal error occurred please check the log for more information: {error[0]}") # noqa: T201 diff --git a/comictaggerlib/renamewindow.py b/comictaggerlib/renamewindow.py index c896d70..a856ef5 100644 --- a/comictaggerlib/renamewindow.py +++ b/comictaggerlib/renamewindow.py @@ -73,13 +73,8 @@ class RenameWindow(QtWidgets.QDialog): self.renamer.replacements = self.options[0].rename_replacements new_ext = ca.path.suffix # default - if self.options[0].rename_set_extension_based_on_archive: - if ca.is_sevenzip(): - new_ext = ".cb7" - elif ca.is_zip(): - new_ext = ".cbz" - elif ca.is_rar(): - new_ext = ".cbr" + if self.options[0].filename_rename_set_extension_based_on_archive: + new_ext = ca.extension() if md is None: md = ca.read_metadata(self.data_style) @@ -206,7 +201,7 @@ class RenameWindow(QtWidgets.QDialog): logger.info("%s: Filename is already good!", comic[1]) continue - if not comic[0].is_writable(check_rar_status=False): + if not comic[0].is_writable(check_archive_status=False): continue comic[0].rename(utils.unique_file(full_path)) diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index cd46aac..2f56890 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -696,16 +696,7 @@ Have fun! self.lblFilename.setText(filename) - if ca.is_sevenzip(): - self.lblArchiveType.setText("7Z archive") - elif ca.is_zip(): - self.lblArchiveType.setText("ZIP archive") - elif ca.is_rar(): - self.lblArchiveType.setText("RAR archive") - elif ca.is_folder(): - self.lblArchiveType.setText("Folder archive") - else: - self.lblArchiveType.setText("") + self.lblArchiveType.setText(ca.archiver.name() + " archive") page_count = f" ({ca.get_number_of_pages()} pages)" self.lblPageCount.setText(page_count) diff --git a/setup.py b/setup.py index e8d1c3d..014b996 100644 --- a/setup.py +++ b/setup.py @@ -59,12 +59,18 @@ setup( exclude=["tests", "testing"], ), package_data={"comictaggerlib": ["ui/*", "graphics/*"], "comicapi": ["data/*"]}, - entry_points=dict( - console_scripts=["comictagger=comictaggerlib.main:main"], - pyinstaller40=[ + entry_points={ + "console_scripts": ["comictagger=comictaggerlib.main:main"], + "pyinstaller40": [ "hook-dirs = comictaggerlib.__pyinstaller:get_hook_dirs", ], - ), + "comicapi.archivers": [ + "zip = comicapi.archivers.zip:ZipArchiver", + "sevenzip = comicapi.archivers.sevenzip:SevenZipArchiver", + "rar = comicapi.archivers.rar:RarArchiver", + "folder = comicapi.archivers.folder:FolderArchiver", + ], + }, classifiers=[ "Development Status :: 4 - Beta", "Environment :: Console", diff --git a/tests/comicarchive_test.py b/tests/comicarchive_test.py index b675431..2c939fe 100644 --- a/tests/comicarchive_test.py +++ b/tests/comicarchive_test.py @@ -4,15 +4,17 @@ import platform import shutil import pytest +from importlib_metadata import entry_points import comicapi.comicarchive import comicapi.genericmetadata from testing.filenames import datadir -@pytest.mark.xfail(not comicapi.comicarchive.rar_support, reason="rar support") -def test_getPageNameList(): +@pytest.mark.xfail(not comicapi.archivers.rar.rar_support, reason="rar support") +def test_getPageNameList(load_archive_plugins): c = comicapi.comicarchive.ComicArchive(datadir / "fake_cbr.cbr") + assert c.seems_to_be_a_comic_archive() pageNameList = c.get_page_name_list() assert pageNameList == [ @@ -56,24 +58,26 @@ def test_save_cbi(tmp_comic): md = tmp_comic.read_cbi() -@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support") +@pytest.mark.xfail(not (comicapi.archivers.rar.rar_support and shutil.which("rar")), reason="rar support") def test_save_cix_rar(tmp_path): cbr_path = datadir / "fake_cbr.cbr" shutil.copy(cbr_path, tmp_path) tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name) + assert tmp_comic.seems_to_be_a_comic_archive() assert tmp_comic.write_cix(comicapi.genericmetadata.md_test) md = tmp_comic.read_cix() assert md.replace(pages=[]) == comicapi.genericmetadata.md_test.replace(pages=[]) -@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support") +@pytest.mark.xfail(not (comicapi.archivers.rar.rar_support and shutil.which("rar")), reason="rar support") def test_save_cbi_rar(tmp_path): cbr_path = datadir / "fake_cbr.cbr" shutil.copy(cbr_path, tmp_path) tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name) + assert tmp_comic.seems_to_be_a_comic_archive() assert tmp_comic.write_cbi(comicapi.genericmetadata.md_test) md = tmp_comic.read_cbi() @@ -118,16 +122,8 @@ def test_invalid_zip(tmp_comic): archivers = [ - comicapi.comicarchive.ZipArchiver, - comicapi.comicarchive.FolderArchiver, - pytest.param( - comicapi.comicarchive.SevenZipArchiver, - marks=pytest.mark.xfail(not (comicapi.comicarchive.z7_support), reason="7z support"), - ), - pytest.param( - comicapi.comicarchive.RarArchiver, - marks=pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support"), - ), + pytest.param(x.load(), marks=pytest.mark.xfail(not (x.load().enabled), reason="archiver not enabled")) + for x in entry_points(group="comicapi_archivers") ] @@ -135,7 +131,7 @@ archivers = [ def test_copy_from_archive(archiver, tmp_path, cbz): comic_path = tmp_path / cbz.path.with_suffix("").name - archive = archiver(comic_path) + archive = archiver.open(comic_path) assert archive.copy_from_archive(cbz.archiver) diff --git a/tests/conftest.py b/tests/conftest.py index 7b20081..89fd3d7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,12 +23,17 @@ from testing.comicdata import all_seed_imprints, seed_imprints @pytest.fixture -def cbz(): +def cbz(load_archive_plugins): yield comicapi.comicarchive.ComicArchive(filenames.cbz_path) @pytest.fixture -def tmp_comic(tmp_path): +def load_archive_plugins(): + comicapi.comicarchive.load_archive_plugins() + + +@pytest.fixture +def tmp_comic(tmp_path, load_archive_plugins): shutil.copy(filenames.cbz_path, tmp_path) yield comicapi.comicarchive.ComicArchive(tmp_path / filenames.cbz_path.name)