diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 3262616c..7c79cab6 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -55,7 +55,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ COPY . /app RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen \ - --extra test --extra webservice --extra watcher --no-dev \ + --extra webservice --extra watcher --no-dev \ --no-install-package pyarrow FROM base diff --git a/.docker/Dockerfile.alpine b/.docker/Dockerfile.alpine index 297f0295..fe1bdc69 100644 --- a/.docker/Dockerfile.alpine +++ b/.docker/Dockerfile.alpine @@ -39,7 +39,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ COPY . /app RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen \ - --extra test --extra webservice --extra watcher --no-dev \ + --extra webservice --extra watcher --no-dev \ --no-install-package pyarrow FROM base diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 518cebad..0142d381 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -74,7 +74,7 @@ jobs: - name: Install Python packages run: | - uv sync --extra test --no-dev + uv sync --group test - name: Report versions run: | @@ -137,7 +137,7 @@ jobs: - name: Install Python packages run: | - uv sync --extra test --no-dev + uv sync --group test - name: Report versions run: | @@ -192,7 +192,7 @@ jobs: - name: Install Python packages run: | - uv sync --extra test --no-dev + uv sync --group test - name: Test run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6145a772..5150ebe3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,17 +10,12 @@ repos: - id: check-toml - id: check-yaml - id: debug-statements - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: "v0.0.261" + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.14.11" hooks: - - id: ruff - files: "src/.*\\.pyi?$" - args: [--fix, --exit-non-zero-on-fix] - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - language_version: python + - id: ruff-check + args: [--fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.2.0 hooks: diff --git a/docs/batch.md b/docs/batch.md index a113a0dc..53bc6f15 100644 --- a/docs/batch.md +++ b/docs/batch.md @@ -117,6 +117,10 @@ tend to give better performance. watcher.py works on all platforms. Users may need to customize the script to meet their requirements. :::{code} bash +# Using uv (recommended) +uv sync --extra watcher + +# Or using pip pip3 install ocrmypdf[watcher] env OCR_INPUT_DIRECTORY=/mnt/input-pdfs \ diff --git a/docs/installation.md b/docs/installation.md index 16ed5167..77dc4483 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -686,18 +686,71 @@ need to be installed. The script requires specific versions of the dependencies. Older version than the ones mentioned in the release notes are likely not to be compatible to OCRmyPDF. +## Optional Features + +OCRmyPDF provides optional features and development tools. We recommend using `uv` as your package manager. + +### Installing User Features + +User features are available as optional dependencies. Install them with `uv` (recommended) or `pip`: + +```bash +# Using uv (recommended) +uv sync --extra watcher # File watching service +uv sync --extra webservice # Streamlit web UI +uv sync --extra watcher --extra webservice # Multiple features + +# Using pip (also works) +pip install ocrmypdf[watcher] +pip install ocrmypdf[webservice] +pip install ocrmypdf[watcher,webservice] +``` + +### Development Tools (uv only) + +Development tools use dependency groups and require `uv`: + +```bash +# Testing infrastructure +uv sync --group test + +# Documentation building +uv sync --group docs + +# Enhanced Streamlit development +uv sync --group streamlit-dev + +# All development groups +uv sync +``` + +:::{note} +**User features** (`watcher`, `webservice`) work with both `uv` and `pip`. +**Developer tools** (`test`, `docs`, `streamlit-dev`) require `uv` and use dependency groups (PEP 735). +::: + +**Why use uv?** + +- Modern, fast Python package manager +- Required for development (testing, docs) +- Better dependency resolution +- Consistent across all platforms + +Install uv: `pip install uv` or visit https://docs.astral.sh/uv/ + ### For development To install all of the development and test requirements: ```bash git clone -b main https://github.com/ocrmypdf/OCRmyPDF.git -python -m venv .venv -source .venv/bin/activate cd OCRmyPDF -pip install -e .[test] +pip install uv # Install uv if not already installed +uv sync --group test ``` +Note: Development requires `uv`. The old `pip install -e .[test]` method is no longer supported. + To add JBIG2 encoding, see {ref}`jbig2`. ## Shell completions diff --git a/pyproject.toml b/pyproject.toml index 9dde17d1..f40dcf9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,19 +52,7 @@ Tracker = "https://github.com/ocrmypdf/OCRmyPDF/issues" Changelog = "https://github.com/ocrmypdf/OCRmyPDF/docs/release_notes.md" [project.optional-dependencies] -docs = ["myst-parser>=4.0.1", "sphinx", "sphinx-issues", "sphinx-rtd-theme"] -extended_test = ["PyMuPDF>=1.19.1"] -test = [ - "coverage[toml]>=6.2", - "hypothesis>=6.36.0", - "pytest>=6.2.5", - "pytest-cov>=3.0.0", - "pytest-xdist>=2.5.0", - "python-xmp-toolkit==2.0.1", # also requires apt-get install libexempi3 - "reportlab>=3.6.8", - "types-Pillow", - "types-humanfriendly", -] +# User-installable features - use `uv sync --extra ` or `pip install ocrmypdf[name]` watcher = ["watchdog>=1.0.2", "typer-slim[standard]", "python-dotenv"] webservice = ["streamlit>=1.41.0"] @@ -157,10 +145,34 @@ convention = "google" quote-style = "preserve" [dependency-groups] +# Developer-only tools - use `uv sync --group ` (NOT pip-installable) dev = [ "mypy>=1.13.0", - "pymupdf>=1.24.14", - "streamlit-pdf-viewer>=0.0.19", - "streamlit>=1.40.2", "ipykernel>=6.29.5", ] +test = [ + # Core testing framework + "coverage[toml]>=6.2", + "hypothesis>=6.36.0", + "pytest>=6.2.5", + "pytest-cov>=3.0.0", + "pytest-xdist>=2.5.0", + # Test dependencies + "python-xmp-toolkit==2.0.1", # also requires apt-get install libexempi3 + "reportlab>=3.6.8", + # Type stubs for testing + "types-Pillow", + "types-humanfriendly", + # Extended test capabilities (merged from extended_test) + "pymupdf>=1.24.14", +] +docs = [ + "myst-parser>=4.0.1", + "sphinx", + "sphinx-issues", + "sphinx-rtd-theme", +] +streamlit-dev = [ + "streamlit>=1.40.2", + "streamlit-pdf-viewer>=0.0.19", +] diff --git a/uv.lock b/uv.lock index eb3460bc..2843ea1d 100644 --- a/uv.lock +++ b/uv.lock @@ -1414,27 +1414,6 @@ dependencies = [ ] [package.optional-dependencies] -docs = [ - { name = "myst-parser" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-issues" }, - { name = "sphinx-rtd-theme" }, -] -extended-test = [ - { name = "pymupdf" }, -] -test = [ - { name = "coverage", extra = ["toml"] }, - { name = "hypothesis" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "python-xmp-toolkit" }, - { name = "reportlab" }, - { name = "types-humanfriendly" }, - { name = "types-pillow" }, -] watcher = [ { name = "python-dotenv" }, { name = "typer-slim", extra = ["standard"] }, @@ -1448,19 +1427,36 @@ webservice = [ dev = [ { name = "ipykernel" }, { name = "mypy" }, - { name = "pymupdf" }, +] +docs = [ + { name = "myst-parser" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-issues" }, + { name = "sphinx-rtd-theme" }, +] +streamlit-dev = [ { name = "streamlit" }, { name = "streamlit-pdf-viewer" }, ] +test = [ + { name = "coverage", extra = ["toml"] }, + { name = "hypothesis" }, + { name = "pymupdf" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-xdist" }, + { name = "python-xmp-toolkit" }, + { name = "reportlab" }, + { name = "types-humanfriendly" }, + { name = "types-pillow" }, +] [package.metadata] requires-dist = [ - { name = "coverage", extras = ["toml"], marker = "extra == 'test'", specifier = ">=6.2" }, { name = "deprecation", specifier = ">=2.1.0" }, { name = "fpdf2", specifier = ">=2.8.0" }, - { name = "hypothesis", marker = "extra == 'test'", specifier = ">=6.36.0" }, { name = "img2pdf", specifier = ">=0.5" }, - { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=4.0.1" }, { name = "packaging", specifier = ">=20" }, { name = "pdfminer-six", specifier = ">=20220319" }, { name = "pi-heif" }, @@ -1468,35 +1464,43 @@ requires-dist = [ { name = "pillow", specifier = ">=10.0.1" }, { name = "pluggy", specifier = ">=1" }, { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pymupdf", marker = "extra == 'extended-test'", specifier = ">=1.19.1" }, { name = "pypdfium2", specifier = ">=5.0.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=6.2.5" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=3.0.0" }, - { name = "pytest-xdist", marker = "extra == 'test'", specifier = ">=2.5.0" }, { name = "python-dotenv", marker = "extra == 'watcher'" }, - { name = "python-xmp-toolkit", marker = "extra == 'test'", specifier = "==2.0.1" }, - { name = "reportlab", marker = "extra == 'test'", specifier = ">=3.6.8" }, { name = "rich", specifier = ">=13" }, - { name = "sphinx", marker = "extra == 'docs'" }, - { name = "sphinx-issues", marker = "extra == 'docs'" }, - { name = "sphinx-rtd-theme", marker = "extra == 'docs'" }, { name = "streamlit", marker = "extra == 'webservice'", specifier = ">=1.41.0" }, { name = "typer-slim", extras = ["standard"], marker = "extra == 'watcher'" }, - { name = "types-humanfriendly", marker = "extra == 'test'" }, - { name = "types-pillow", marker = "extra == 'test'" }, { name = "uharfbuzz", specifier = ">=0.53.2" }, { name = "watchdog", marker = "extra == 'watcher'", specifier = ">=1.0.2" }, ] -provides-extras = ["docs", "extended-test", "test", "watcher", "webservice"] +provides-extras = ["watcher", "webservice"] [package.metadata.requires-dev] dev = [ { name = "ipykernel", specifier = ">=6.29.5" }, { name = "mypy", specifier = ">=1.13.0" }, - { name = "pymupdf", specifier = ">=1.24.14" }, +] +docs = [ + { name = "myst-parser", specifier = ">=4.0.1" }, + { name = "sphinx" }, + { name = "sphinx-issues" }, + { name = "sphinx-rtd-theme" }, +] +streamlit-dev = [ { name = "streamlit", specifier = ">=1.40.2" }, { name = "streamlit-pdf-viewer", specifier = ">=0.0.19" }, ] +test = [ + { name = "coverage", extras = ["toml"], specifier = ">=6.2" }, + { name = "hypothesis", specifier = ">=6.36.0" }, + { name = "pymupdf", specifier = ">=1.24.14" }, + { name = "pytest", specifier = ">=6.2.5" }, + { name = "pytest-cov", specifier = ">=3.0.0" }, + { name = "pytest-xdist", specifier = ">=2.5.0" }, + { name = "python-xmp-toolkit", specifier = "==2.0.1" }, + { name = "reportlab", specifier = ">=3.6.8" }, + { name = "types-humanfriendly" }, + { name = "types-pillow" }, +] [[package]] name = "packaging"