Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 2
updates:
- package-ecosystem: "pip"
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
Expand Down
53 changes: 39 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,68 @@ name: Build
on:
push:
pull_request:
workflow_dispatch:

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
# Tests compatibility across Ubuntu/Windows and Python/package version extremes
os: [ubuntu-latest, windows-latest]
python-version: ["3.10", "3.x"]
dependencies: [locked, oldest]

steps:
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
# When searching for a system Python version, uv will use the first compatible version - not the newest version.
# Therefore, make sure the newest version is available on the runner.
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
python-version: 3.x

# At the time of writing there is no way to force uv select the lowest version of Python.
# Therefore, extract this from pyproject.toml
# More info: https://github.com/astral-sh/uv/issues/16333
- name: Extract minimum Python version from pyproject.toml
if: matrix.dependencies == 'oldest'
id: python-version
shell: bash
run: |
MIN_PY=$(grep 'requires-python' pyproject.toml | sed -E 's/[^0-9.]//g')
echo "min=$MIN_PY" >> $GITHUB_OUTPUT

- name: Install package with dev dependencies
run: pip install -e .[dev]
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"

- name: Install locked dependencies
if: matrix.dependencies == 'locked'
run: uv sync --locked

- name: Install oldest dependencies
if: matrix.dependencies == 'oldest'
run: uv sync --resolution lowest --python ${{ steps.python-version.outputs.min }}

- name: Run ruff (check)
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
run: ruff check
if: matrix.dependencies == 'locked'
run: uv run ruff check

- name: Run ruff (format)
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
run: ruff format --check
if: matrix.dependencies == 'locked'
run: uv run ruff format --check

- name: Run mypy
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
run: mypy .
- name: Run ty
if: matrix.dependencies == 'locked'
run: uv run ty check --output-format github

- name: Run pytest
run: pytest tests/ -v --cov --cov-branch --cov-report=xml
run: uv run pytest

- name: Upload coverage reports to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
if: matrix.os == 'ubuntu-latest' && matrix.dependencies == 'locked'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
1 change: 0 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-python.python",
"matangover.mypy",
"charliermarsh.ruff",
"ryanluker.vscode-coverage-gutters",
"astral-sh.ty"
Expand Down
8 changes: 1 addition & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@
"source.organizeImports.ruff": "explicit"
},

// Enable MyPy type checker
"mypy.runUsingActiveInterpreter": true,

// Configure Pytest
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
}
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![codecov](https://codecov.io/gh/JustusRijke/DiffPDF/graph/badge.svg?token=O3ZJFG6X7A)](https://codecov.io/gh/JustusRijke/DiffPDF)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![PyPI - Version](https://img.shields.io/pypi/v/DiffPDF)](https://pypi.org/project/DiffPDF/)
[![PyPI - Downloads](https://img.shields.io/pypi/dw/DiffPDF)](https://pypi.org/project/DiffPDF/)

CLI tool for detecting structural, textual, and visual differences between PDF files, for use in automatic regression tests.
Expand All @@ -21,6 +22,8 @@ Each stage only runs if all previous stages pass.

## Installation

Install Python (v3.10 or higher) and install the package:

```bash
pip install diffpdf
```
Expand Down Expand Up @@ -54,16 +57,37 @@ from diffpdf import diffpdf
# Basic usage (no diff images saved)
diffpdf("reference.pdf", "actual.pdf")

# With options (save diff images to ./output directory)
diffpdf("reference.pdf", "actual.pdf", output_dir="./output", threshold=0.2, dpi=150, verbose=True)
# With options (save diff images to ./output directory, extract higher quality images)
diffpdf("reference.pdf", "actual.pdf", output_dir="./output", dpi=300)
```

## Development

Install [uv](https://github.com/astral-sh/uv?tab=readme-ov-file#installation). Then, install dependencies & activate the automatically generated virtual environment:

```bash
uv sync --locked
source .venv/bin/activate
```

Skip `--locked` to use the newest dependencies (this might modify `uv.lock`)

Run tests:
```bash
pytest
```

Check code quality:
```bash
ruff check
ruff format --check
ty check
```

Better yet, install the [pre-commit](.git/hooks/pre-commit) hook, which runs code quality checks before every commit:
```bash
pip install -e .[dev]
pytest tests/ -v
ruff check .
cp hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

## Acknowledgements
Expand Down
30 changes: 22 additions & 8 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,36 @@ EOF
exit 1
fi

# Ruff checks
ruff check -q
# Check if uv is installed
uv help -q
if [ $? -ne 0 ]; then
echo "'uv' is not installed or not in your PATH"
exit 1
fi

# Check if the project is synced
uv lock --check -q
if [ $? -ne 0 ]; then
echo "Ruff found linting errors. Run: ruff check --fix"
echo "Error: Environment is out of sync. Please run 'uv sync'."
exit 1
fi

ruff format --check -q
# Ruff checks
uv run ruff check -q
if [ $? -ne 0 ]; then
echo "Ruff found linting errors. Please run 'ruff check --fix'"
exit 1
fi

uv run ruff format --check -q
if [ $? -ne 0 ]; then
echo "Ruff found formatting issues. Run: ruff format"
echo "Ruff found formatting issues. Please run 'ruff format'"
exit 1
fi

# Mypy checks
mypy .
# Type annotation checks
uv run ty check -q
if [ $? -ne 0 ]; then
echo "Mypy found type errors. Fix them before committing."
echo "Found type annotation errors. Please run 'ty check'"
exit 1
fi
4 changes: 0 additions & 4 deletions mypy.ini

This file was deleted.

43 changes: 31 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ dynamic = ["version"]
description = "A tool for comparing PDF files"
readme = "README.md"
license = "MIT"
license-files = ["LICEN[CS]E*"]
license-files = ["LICENSE"]
authors = [{name = "Justus Rijke", email="justusrijke@gmail.com"}]
requires-python = ">=3.10"
requires-python = ">=3.10.0"
classifiers = [
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
Expand All @@ -22,22 +22,22 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"click",
"click>=8",
"pymupdf>=1.23.0",
"pixelmatch-fast>=1.3.0",
"pixelmatch-fast>=1.3.1",
"Pillow>=10.0.0",
]

[project.urls]
Homepage = "https://github.com/JustusRijke/DiffPDF"
Issues = "https://github.com/JustusRijke/DiffPDF/issues"

[project.optional-dependencies]
[dependency-groups]
dev = [
"pytest",
"pytest-cov",
"ruff",
"mypy"
"pytest>=9",
"pytest-cov>=6",
"ruff>=0.10",
"ty>=0.0.8",
]

[project.scripts]
Expand All @@ -49,8 +49,27 @@ source = "vcs"
[tool.hatch.version.raw-options]
local_scheme = "no-local-version"

[tool.ruff]
target-version = "py310"
[tool.pytest]
strict = true
testpaths = ["tests"]
filterwarnings = ["error"] # Treat all warnings as errors (e.g., deprecation warnings)
addopts = [
"-v", # Verbose output
"--cov", # Enable coverage
"--cov-branch", # Make sure to cover every decision branch
"--cov-report=term-missing", # Report which lines aren't covered
"--cov-report=xml", # Dump to XML for Codecov
]

[tool.ruff.lint]
extend-select = ["I"] # Sort imports
extend-select = [
"I", # Sort imports
"ANN", # Enforce type annotations
"PT", # Common style issues or inconsistencies with pytest-based tests
]

[tool.ruff.lint.isort]
combine-as-imports = true # Combines "as" imports on the same line

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["ANN"] # Do not enforce type annotations for tests
3 changes: 1 addition & 2 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# type: ignore
from pathlib import Path

import pytest
Expand All @@ -9,7 +8,7 @@


@pytest.mark.parametrize(
"ref_pdf_rel,actual_pdf_rel,should_pass",
("ref_pdf_rel", "actual_pdf_rel", "should_pass"),
[
# Pass cases
("pass/identical-A.pdf", "pass/identical-B.pdf", True),
Expand Down
1 change: 0 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# type: ignore
from pathlib import Path

from click.testing import CliRunner
Expand Down
Loading