Rationalize optional dependencies vs dependency groups
Establish clear separation between user-facing optional dependencies and developer-only dependency groups: **Optional Dependencies (user features):** - watcher: File watching service for batch processing - webservice: Streamlit-based web UI - Installable via: uv sync --extra <name> or pip install ocrmypdf[name] **Dependency Groups (developer tools):** - test: Testing infrastructure (merged from test + extended_test) - docs: Documentation building tools - streamlit-dev: Enhanced Streamlit development tools - dev: General development tools (mypy, ipykernel) - Installable via: uv sync --group <name> (uv only, NOT pip) Breaking changes for developers: - pip install -e .[test] no longer works → use uv sync --group test - pip install -e .[docs] no longer works → use uv sync --group docs - pip install -e .[extended_test] removed → merged into test group No breaking changes for end users: - pip install ocrmypdf[watcher] still works - pip install ocrmypdf[webservice] still works Updated: - CI/CD workflows to use uv sync --group test - Docker images to exclude test dependencies - Documentation to recommend uv with pip as fallback - pyproject.toml with clear comments explaining both systems
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
+5
-10
@@ -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:
|
||||
|
||||
@@ -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 \
|
||||
|
||||
+56
-3
@@ -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
|
||||
|
||||
+28
-16
@@ -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 <name>` 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 <name>` (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",
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user