Skip to content

Commit

Permalink
Add typing for camelot/backends
Browse files Browse the repository at this point in the history
  • Loading branch information
foarsitter committed Mar 1, 2024
1 parent 567520b commit 3b9a81e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 29 deletions.
7 changes: 7 additions & 0 deletions camelot/backends/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ConversionBackend:

def installed(self) -> bool:
raise NotImplementedError

def convert(self, pdf_path: str, png_path: str, resolution: int = 300) -> None:
raise NotImplementedError
16 changes: 9 additions & 7 deletions camelot/backends/ghostscript_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,38 @@
import sys
from ctypes.util import find_library

from camelot.backends.base import ConversionBackend

def installed_posix():

def installed_posix() -> bool:
library = find_library("gs")
return library is not None


def installed_windows():
def installed_windows() -> bool:
library = find_library(
"".join(("gsdll", str(ctypes.sizeof(ctypes.c_voidp) * 8), ".dll"))
"".join(("gsdll", str(ctypes.sizeof(ctypes.c_voidp) * 8), ".dll")) # type: ignore[attr-defined]
)
return library is not None


class GhostscriptBackend:
def installed(self):
class GhostscriptBackend(ConversionBackend):
def installed(self) -> bool:
if sys.platform in ["linux", "darwin"]:
return installed_posix()
elif sys.platform == "win32":
return installed_windows()
else:
return installed_posix()

def convert(self, pdf_path, png_path, resolution=300):
def convert(self, pdf_path: str, png_path: str, resolution: int = 300) -> None:
if not self.installed():
raise OSError(
"Ghostscript is not installed. You can install it using the instructions"
" here: https://camelot-py.readthedocs.io/en/master/user/install-deps.html"
)

import ghostscript
import ghostscript # type: ignore[import-untyped]

gs_command = [
"gs",
Expand Down
44 changes: 27 additions & 17 deletions camelot/backends/image_conversion.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,49 @@
from typing import Dict
from typing import List
from typing import Type

from .base import ConversionBackend
from .ghostscript_backend import GhostscriptBackend
from .poppler_backend import PopplerBackend


BACKENDS = {"poppler": PopplerBackend, "ghostscript": GhostscriptBackend}
BACKENDS: Dict[str, Type[ConversionBackend]] = {
"poppler": PopplerBackend,
"ghostscript": GhostscriptBackend,
}


class ImageConversionError(ValueError):
pass


class ImageConversionBackend:
def __init__(self, backend="poppler", use_fallback=True):
def __init__(self, backend: str = "poppler", use_fallback: bool = True) -> None:
if backend not in BACKENDS.keys():
raise ValueError(f"Image conversion backend '{backend}' not supported")
msg = f"Image conversion backend {backend!r} not supported"
raise ValueError(msg)

self.backend = backend
self.use_fallback = use_fallback
self.fallbacks = list(filter(lambda x: x != backend, BACKENDS.keys()))
self.backend: str = backend
self.use_fallback: bool = use_fallback
self.fallbacks: List[str] = list(
filter(lambda x: x != backend, BACKENDS.keys())
)

def convert(self, pdf_path, png_path):
def convert(self, pdf_path: str, png_path: str) -> None:
try:
converter = BACKENDS[self.backend]()
converter.convert(pdf_path, png_path)
except Exception as e:
import sys

except Exception as f:
if self.use_fallback:
for fallback in self.fallbacks:
try:
converter = BACKENDS[fallback]()
converter.convert(pdf_path, png_path)
except Exception as e:
raise type(e)(
str(e) + f" with image conversion backend '{fallback}'"
).with_traceback(sys.exc_info()[2])
continue
msg = f"Image conversion failed with image conversion backend {fallback!r}"
raise ImageConversionError(msg) from e
else:
break
else:
raise type(e)(
str(e) + f" with image conversion backend '{self.backend}'"
).with_traceback(sys.exc_info()[2])
msg = f"Image conversion failed with image conversion backend {self.backend!r}"
raise ImageConversionError(msg) from f
14 changes: 9 additions & 5 deletions camelot/backends/poppler_backend.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import os
import sys
import shutil
import subprocess
import sys

from camelot.backends.base import ConversionBackend


path = os.path.dirname(sys.executable) + os.pathsep + os.environ["PATH"]

path = os.path.dirname(sys.executable) + os.pathsep + os.environ['PATH']

class PopplerBackend:
def convert(self, pdf_path, png_path):
class PopplerBackend(ConversionBackend):
def convert(self, pdf_path: str, png_path: str, resolution: int = 300) -> None:
pdftopng_executable = shutil.which("pdftopng", path=path)
if pdftopng_executable is None:
raise OSError(
Expand All @@ -20,4 +24,4 @@ def convert(self, pdf_path, png_path):
" ".join(pdftopng_command), stderr=subprocess.STDOUT, shell=True
)
except subprocess.CalledProcessError as e:
raise ValueError(e.output)
raise ValueError(e.output) from e

0 comments on commit 3b9a81e

Please sign in to comment.