Skip to content

Commit 02a5765

Browse files
committed
Add typehints
1 parent 0f59295 commit 02a5765

File tree

5 files changed

+145
-50
lines changed

5 files changed

+145
-50
lines changed

src/pdfbaker/__init__.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
1-
"""Core functionality for document generation."""
1+
"""PDF document generator."""
22

33
import logging
44

55
from .common import (
6+
# Exceptions
67
PDFBakeError,
78
PDFCombineError,
89
PDFCompressionError,
910
SVGConversionError,
11+
# Functions
1012
combine_pdfs,
1113
compress_pdf,
1214
convert_svg_to_pdf,
1315
deep_merge,
1416
load_pages,
1517
)
16-
from .render import (
17-
create_env,
18-
process_template_data,
19-
)
18+
from .render import create_env, process_template_data
2019

2120
logger = logging.getLogger(__name__)
2221

2322
__all__ = [
23+
# Logger
2424
"logger",
25-
# common
25+
# Exceptions
2626
"PDFBakeError",
2727
"PDFCombineError",
2828
"PDFCompressionError",
2929
"SVGConversionError",
30+
# Common functions
3031
"combine_pdfs",
3132
"compress_pdf",
3233
"convert_svg_to_pdf",
3334
"deep_merge",
3435
"load_pages",
35-
# render
36+
# Render functions
3637
"create_env",
3738
"process_template_data",
3839
]

src/pdfbaker/__main__.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import sys
77
from pathlib import Path
8+
from typing import Any
89

910
import click
1011
import yaml
@@ -17,7 +18,7 @@
1718

1819

1920
@click.group()
20-
def cli():
21+
def cli() -> None:
2122
"""Generate PDF documents from YAML-configured SVG templates."""
2223

2324

@@ -33,7 +34,9 @@ def cli():
3334
is_flag=True,
3435
help="Debug mode (implies --verbose, keeps build files)",
3536
)
36-
def bake(config_file, verbose=False, quiet=False, debug=False):
37+
def bake(
38+
config_file: Path, verbose: bool = False, quiet: bool = False, debug: bool = False
39+
) -> int:
3740
"""Parse config file and bake PDFs."""
3841
if debug:
3942
verbose = True
@@ -59,13 +62,13 @@ def bake(config_file, verbose=False, quiet=False, debug=False):
5962
return 0
6063

6164

62-
def _load_config(config_file):
65+
def _load_config(config_file: Path) -> dict[str, Any]:
6366
"""Load configuration from a YAML file."""
6467
with open(config_file, encoding="utf-8") as f:
6568
return yaml.safe_load(f)
6669

6770

68-
def _setup_output_directories(base_dir):
71+
def _setup_output_directories(base_dir: Path) -> tuple[Path, Path]:
6972
"""Create and return build and dist directories."""
7073
build_dir = base_dir / "build"
7174
dist_dir = base_dir / "dist"
@@ -74,9 +77,11 @@ def _setup_output_directories(base_dir):
7477
return build_dir, dist_dir
7578

7679

77-
def _get_document_paths(base_dir, documents):
80+
def _get_document_paths(
81+
base_dir: Path, documents: list[dict[str, str] | str]
82+
) -> dict[str, Path]:
7883
"""Resolve document paths to absolute paths."""
79-
document_paths = {}
84+
document_paths: dict[str, Path] = {}
8085

8186
for doc_name in documents:
8287
if isinstance(doc_name, dict):
@@ -92,7 +97,7 @@ def _get_document_paths(base_dir, documents):
9297
return document_paths
9398

9499

95-
def _validate_document_path(doc_name, doc_path):
100+
def _validate_document_path(doc_name: str, doc_path: Path) -> tuple[Path, Path] | bool:
96101
"""Validate that a document has all required files."""
97102
if not doc_path.is_dir():
98103
logger.warning('Directory missing for document "%s" at %s', doc_name, doc_path)
@@ -111,7 +116,13 @@ def _validate_document_path(doc_name, doc_path):
111116
return bake_path, config_yml_path
112117

113118

114-
def _process_document(doc_name, doc_path, config, build_dir, dist_dir):
119+
def _process_document(
120+
doc_name: str,
121+
doc_path: Path,
122+
config: dict[str, Any],
123+
build_dir: Path,
124+
dist_dir: Path,
125+
) -> None:
115126
"""Process an individual document."""
116127
validation_result = _validate_document_path(doc_name, doc_path)
117128
if not validation_result:
@@ -143,7 +154,7 @@ def _process_document(doc_name, doc_path, config, build_dir, dist_dir):
143154
)
144155

145156

146-
def _load_document_bake_module(doc_name, bake_path):
157+
def _load_document_bake_module(doc_name: str, bake_path: Path) -> Any:
147158
"""Load the document's bake.py module."""
148159
doc_bake = importlib.util.spec_from_file_location(
149160
f"documents.{doc_name}.bake", bake_path
@@ -153,7 +164,9 @@ def _load_document_bake_module(doc_name, bake_path):
153164
return module
154165

155166

156-
def _setup_document_output_directories(build_dir, dist_dir, doc_name):
167+
def _setup_document_output_directories(
168+
build_dir: Path, dist_dir: Path, doc_name: str
169+
) -> tuple[Path, Path]:
157170
"""Set up and clean document-specific build and dist directories."""
158171
doc_build_dir = build_dir / doc_name
159172
doc_dist_dir = dist_dir / doc_name
@@ -169,7 +182,7 @@ def _setup_document_output_directories(build_dir, dist_dir, doc_name):
169182
return doc_build_dir, doc_dist_dir
170183

171184

172-
def _teardown_build_directories(build_dir, doc_names):
185+
def _teardown_build_directories(build_dir: Path, doc_names: list[str]) -> None:
173186
"""Clean up build directories after successful processing.
174187
175188
Args:

src/pdfbaker/common.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
import os
55
import select
66
import subprocess
7+
from collections.abc import Sequence
8+
from pathlib import Path
9+
from typing import Any
710

811
import pypdf
912
import yaml
1013
from cairosvg import svg2pdf
1114

15+
from .types import PageConfig
16+
1217
logger = logging.getLogger(__name__)
1318

1419

@@ -19,7 +24,9 @@ class PDFBakeError(Exception):
1924
class SVGConversionError(PDFBakeError):
2025
"""Failed to convert SVG to PDF."""
2126

22-
def __init__(self, svg_path, backend, cause=None):
27+
def __init__(
28+
self, svg_path: str | Path, backend: str, cause: str | None = None
29+
) -> None:
2330
self.svg_path = svg_path
2431
self.backend = backend
2532
self.cause = cause
@@ -34,7 +41,7 @@ class PDFCompressionError(PDFBakeError):
3441
"""Failed to compress PDF."""
3542

3643

37-
def deep_merge(base, update):
44+
def deep_merge(base: dict[str, Any], update: dict[str, Any]) -> dict[str, Any]:
3845
"""Recursively merge two dictionaries.
3946
4047
Values in update will override those in base, except for dictionaries
@@ -49,22 +56,24 @@ def deep_merge(base, update):
4956
return merged
5057

5158

52-
def load_pages(pages_dir):
59+
def load_pages(pages_dir: Path) -> dict[str, dict[str, Any]]:
5360
"""Load page configurations from a specific subdirectory."""
54-
pages = {}
61+
pages: dict[str, dict[str, Any]] = {}
5562

5663
if pages_dir.exists():
5764
for page in pages_dir.iterdir():
5865
if not page.name.endswith(".yml"):
5966
continue
6067
with open(page, encoding="utf-8") as f:
61-
yaml_config = yaml.safe_load(f)
68+
yaml_config: PageConfig = yaml.safe_load(f)
6269
pages[yaml_config["name"]] = yaml_config["config"]
6370

6471
return pages
6572

6673

67-
def combine_pdfs(pdf_files, output_file):
74+
def combine_pdfs(
75+
pdf_files: Sequence[Path], output_file: Path
76+
) -> Path | PDFCombineError:
6877
"""Combine multiple PDF files into a single PDF.
6978
7079
Args:
@@ -108,7 +117,7 @@ def combine_pdfs(pdf_files, output_file):
108117
return output_file
109118

110119

111-
def _run_subprocess_logged(cmd, env=None):
120+
def _run_subprocess_logged(cmd: list[str], env: dict[str, str] | None = None) -> int:
112121
"""Run a subprocess with output redirected to logging.
113122
114123
Args:
@@ -157,7 +166,9 @@ def _run_subprocess_logged(cmd, env=None):
157166
return 0
158167

159168

160-
def compress_pdf(input_pdf, output_pdf, dpi=300):
169+
def compress_pdf(
170+
input_pdf: Path, output_pdf: Path, dpi: int = 300
171+
) -> Path | PDFCompressionError:
161172
"""Compress a PDF file using Ghostscript.
162173
163174
Args:
@@ -183,15 +194,19 @@ def compress_pdf(input_pdf, output_pdf, dpi=300):
183194
"-dQUIET",
184195
"-dBATCH",
185196
f"-sOutputFile={output_pdf}",
186-
input_pdf,
197+
str(input_pdf),
187198
]
188199
)
189200
return output_pdf
190201
except subprocess.SubprocessError as exc:
191202
raise PDFCompressionError(f"Ghostscript compression failed: {exc}") from exc
192203

193204

194-
def convert_svg_to_pdf(svg_path, pdf_path, backend="cairosvg"):
205+
def convert_svg_to_pdf(
206+
svg_path: Path,
207+
pdf_path: Path,
208+
backend: str = "cairosvg",
209+
) -> Path | SVGConversionError:
195210
"""Convert an SVG file to PDF.
196211
197212
Args:
@@ -213,7 +228,7 @@ def convert_svg_to_pdf(svg_path, pdf_path, backend="cairosvg"):
213228
[
214229
"inkscape",
215230
f"--export-filename={pdf_path}",
216-
svg_path,
231+
str(svg_path),
217232
]
218233
)
219234
except subprocess.SubprocessError as exc:

0 commit comments

Comments
 (0)