diff --git a/phototag/cli.py b/phototag/cli.py index 98a7621..c90fa07 100644 --- a/phototag/cli.py +++ b/phototag/cli.py @@ -10,12 +10,14 @@ import re import shutil from typing import Tuple from glob import glob - +from rich.traceback import install import click from . import config, INPUT_PATH +from .exceptions import InvalidSelectionError from .helpers import get_extension, valid_extension +# install() logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -36,7 +38,6 @@ def run(files: Tuple[str], all: bool = False, regex: str = None, glob_pattern: s Run tagging on FILES. Files can also be selected using --all, --regex and --glob. - """ files = list(files) @@ -54,15 +55,17 @@ def run(files: Tuple[str], all: bool = False, regex: str = None, glob_pattern: s if glob_pattern: files.extend(glob(glob_pattern)) + # Format file selection into relative paths, filter down to 'valid' image files files = list(dict.fromkeys(os.path.relpath(file) for file in files)) select = list(filter(lambda filename: valid_extension(get_extension(filename)), files)) + if len(select) == 0: if len(files) == 0: - logger.info('No files selected.') + raise InvalidSelectionError('No files selected.') else: - logger.info('No valid images selected.') + raise InvalidSelectionError('No valid images selected.') else: - logger.debug(f'{len(select)} valid images out of {len(files)} files selected.') + logger.debug(f'Found {len(select)} valid images out of {len(files)} files selected.') print(files) # from .app import run diff --git a/phototag/exceptions.py b/phototag/exceptions.py new file mode 100644 index 0000000..ab1f1af --- /dev/null +++ b/phototag/exceptions.py @@ -0,0 +1,32 @@ +""" +exceptions.py + +Stores all exceptions used by the application in a single file for organization purposes. +""" + + +class PhototagException(Exception): + """Base class for exceptions in this project/module.""" + + +class UserError(PhototagException): + """Exception most likely caused by the user.""" + + +class InvalidSelectionError(UserError): + """File selection was invalid.""" + pass + + +class InvalidConfigurationError(UserError): + """The configuration presented to the application was not valid or workable.""" + pass + + +class NoSidecarFileError(PhototagException): + """ + The application is confused as a sidecar file was not found where it was expected. + + Most RAW files processed by Adobe are accompanied by a .xmp file with the same name. + """ + pass diff --git a/phototag/helpers.py b/phototag/helpers.py index 41fb508..6bd8ad3 100644 --- a/phototag/helpers.py +++ b/phototag/helpers.py @@ -9,6 +9,7 @@ import re import string from phototag import LOSSY_EXTS, RAW_EXTS +from phototag.exceptions import PhototagException ALL_EXTENSIONS = RAW_EXTS + LOSSY_EXTS @@ -58,7 +59,7 @@ def get_temp_directory(directory: str, start: str = "temp", append_random: int = while os.path.exists(temp): temp = os.path.join(directory, (start + "_" + random_characters(append_random)) if append_random > 0 else start) if append_random >= 128: - raise Exception( + raise PhototagException( "Could not find a valid temporary directory name. Please try again in a different directory." ) append_random += (3 if append_random == 0 else 1) diff --git a/phototag/process.py b/phototag/process.py index 62457e0..3b94c63 100644 --- a/phototag/process.py +++ b/phototag/process.py @@ -22,6 +22,7 @@ from google.cloud import vision from rich.progress import Progress from . import TEMP_PATH, INPUT_PATH, RAW_EXTS +from .exceptions import InvalidConfigurationError, NoSidecarFileError from .helpers import random_characters from .xmp import XMPParser @@ -84,12 +85,12 @@ class MasterFileProcessor(object): # Check that all files are under the set buffer limit for key, fp in self.waiting.keys(): if fp.size > self.buffer_size: - raise Exception("Invalid Configuration - the buffer size is too low. Please raise the buffer size " + raise InvalidConfigurationError("Invalid Configuration - the buffer size is too low. Please raise the buffer size " "or enable single_override.") # Check that image_count is at least 1 if self.image_count <= 0: - raise Exception("Invalid Configuration - the image_count is too low. Please set it to a positive " + raise InvalidConfigurationError("Invalid Configuration - the image_count is too low. Please set it to a positive " "non-zero integer or enable single_override.") def _start(self, key: int) -> None: @@ -229,7 +230,7 @@ class FileProcessor(object): self.xmp = self.base + ".xmp" self.input_xmp = os.path.join(INPUT_PATH, self.xmp) if not os.path.exists(self.input_xmp): - raise Exception("Sidecar file for '{}' does not exist.".format(self.xmp)) + raise NoSidecarFileError("Sidecar file for '{}' does not exist.".format(self.xmp)) @staticmethod def _optimize(file: AnyStr, size: Tuple[int, int] = (512, 512), quality: int = 85, @@ -322,7 +323,7 @@ class FileProcessor(object): # Copy dry-run # shutil.copy2(os.path.join(INPUT_PATH, self.file_name), os.path.join(OUTPUT_PATH, self.file_name)) # os.rename(os.path.join(INPUT_PATH, self.file_name), os.path.join(OUTPUT_PATH, self.file_name)) - except: + except Exception: raise finally: self._cleanup()