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.
This commit is contained in:
deeplow 2022-11-02 11:12:05 +00:00
parent 1bdbb1959c
commit 93f17b3166
No known key found for this signature in database
GPG key ID: 577982871529A52A
3 changed files with 57 additions and 23 deletions

View file

@ -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

View file

@ -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."""

View file

@ -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)