From 93f17b3166609d0cece3da5b0bdade08686330e8 Mon Sep 17 00:00:00 2001 From: deeplow Date: Wed, 2 Nov 2022 11:12:05 +0000 Subject: [PATCH] Specialize DocumentFilenameException() for disambiguation All filename-related exceptions were of class DocumentFilenameException. This made it difficult to disambiguate them. Specializing them makes it it easier for tests to detect which exception in particular we want to verify. --- dangerzone/document.py | 18 +++++++----------- dangerzone/errors.py | 43 ++++++++++++++++++++++++++++++++++++++++++ tests/test_document.py | 19 +++++++------------ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/dangerzone/document.py b/dangerzone/document.py index f825b78..b49b512 100644 --- a/dangerzone/document.py +++ b/dangerzone/document.py @@ -9,7 +9,7 @@ from typing import Optional import appdirs -from .errors import DocumentFilenameException +from . import errors SAFE_EXTENSION = "-safe.pdf" @@ -54,28 +54,24 @@ class Document: try: open(filename, "rb") except FileNotFoundError as e: - raise DocumentFilenameException( - "Input file not found: make sure you typed it correctly." - ) from e + raise errors.InputFileNotFoundException() from e except PermissionError as e: - raise DocumentFilenameException( - "You don't have permission to open the input file." - ) from e + raise errors.InputFileNotReadableException() from e @staticmethod def validate_output_filename(filename: str) -> None: if not filename.endswith(".pdf"): - raise DocumentFilenameException("Safe PDF filename must end in '.pdf'") + raise errors.NonPDFOutputFileException() try: with open(filename, "wb"): pass except PermissionError as e: - raise DocumentFilenameException("Safe PDF filename is not writable") from e + raise errors.UnwriteableOutputFileException() from e @property def input_filename(self) -> str: if self._input_filename is None: - raise DocumentFilenameException("Input filename has not been set yet.") + raise errors.NotSetInputFilenameException() else: return self._input_filename @@ -92,7 +88,7 @@ class Document: if self._input_filename is not None: return self.default_output_filename else: - raise DocumentFilenameException("Output filename has not been set yet.") + raise errors.NotSetOutputFilenameException() else: return self._output_filename diff --git a/dangerzone/errors.py b/dangerzone/errors.py index a26d213..00eb702 100644 --- a/dangerzone/errors.py +++ b/dangerzone/errors.py @@ -14,6 +14,49 @@ class DocumentFilenameException(Exception): """Exception for document-related filename errors.""" +class InputFileNotFoundException(DocumentFilenameException): + """Exception for when an input file does not exist.""" + + def __init__(self) -> None: + super().__init__("Input file not found: make sure you typed it correctly.") + + +class InputFileNotReadableException(DocumentFilenameException): + """Exception for when an input file exists but is not readable.""" + + def __init__(self) -> None: + super().__init__("You don't have permission to open the input file.") + + +class NonPDFOutputFileException(DocumentFilenameException): + """Exception for when the output file is not a PDF.""" + + def __init__(self) -> None: + super().__init__("Safe PDF filename must end in '.pdf'") + + +class UnwriteableOutputFileException(DocumentFilenameException): + """Exception for when the output file is not writeable.""" + + def __init__(self) -> None: + super().__init__("Safe PDF filename is not writable") + + +class NotSetInputFilenameException(DocumentFilenameException): + """Exception for when the output filename is set before having an + associated input file.""" + + def __init__(self) -> None: + super().__init__("Input filename has not been set yet.") + + +class NotSetOutputFilenameException(DocumentFilenameException): + """Exception for when the output filename is read before it has been set.""" + + def __init__(self) -> None: + super().__init__("Output filename has not been set yet.") + + def handle_document_errors(func: F) -> F: """Log document-related errors and exit gracefully.""" diff --git a/tests/test_document.py b/tests/test_document.py index 61621ad..9e9d23b 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -5,8 +5,8 @@ from pathlib import Path import pytest +from dangerzone import errors from dangerzone.document import Document -from dangerzone.errors import DocumentFilenameException from . import sample_doc, unreadable_pdf, unwriteable_pdf @@ -25,15 +25,13 @@ def test_input_file_none() -> None: Attempts to read a document's filename when no doc has been set """ d = Document() - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.NotSetInputFilenameException) as e: d.input_filename - assert "Input filename has not been set yet" in str(e.value) def test_input_file_non_existing() -> None: - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.InputFileNotFoundException) as e: Document("non-existing-file.pdf") - assert "Input file not found" in str(e.value) # XXX: This is not easy to test on Windows, as the file owner can always read it. @@ -41,14 +39,13 @@ def test_input_file_non_existing() -> None: # https://stackoverflow.com/questions/72528318/what-file-permissions-make-a-file-unreadable-by-owner-in-windows @pytest.mark.skipif(platform.system() == "Windows", reason="Unix-specific") def test_input_file_unreadable(unreadable_pdf: str) -> None: - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.InputFileNotReadableException) as e: Document(unreadable_pdf) - assert "don't have permission to open the input file" in str(e.value) def test_output_file_unwriteable(unwriteable_pdf: str) -> None: d = Document() - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.UnwriteableOutputFileException) as e: d.output_filename = unwriteable_pdf assert "Safe PDF filename is not writable" in str(e.value) @@ -64,18 +61,16 @@ def test_output_file_none() -> None: Attempts to read a document's filename when no doc has been set """ d = Document() - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.NotSetOutputFilenameException) as e: d.output_filename - assert "Output filename has not been set yet" in str(e.value) def test_output_file_not_pdf(tmp_path: Path) -> None: docx_file = str(tmp_path.joinpath("document.docx")) d = Document() - with pytest.raises(DocumentFilenameException) as e: + with pytest.raises(errors.NonPDFOutputFileException) as e: d.output_filename = docx_file - assert "Safe PDF filename must end in '.pdf'" in str(e.value) assert not os.path.exists(docx_file)