fix: refine validation coordinator error handling

Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
James R. Barlow
2025-12-21 12:21:48 -08:00
co-authored by aider
parent f6fcdfa618
commit f91e41a209
2 changed files with 42 additions and 54 deletions
-2
View File
@@ -161,8 +161,6 @@ class OCROptions(BaseModel):
tesseract_non_ocr_timeout: float | None = None
tesseract_downsample_above: int = 32767
tesseract_downsample_large_images: bool | None = None
user_words: str | None = None
user_patterns: str | None = None
# Legacy ghostscript options (for backward compatibility)
pdfa_image_compression: str | None = None
+42 -52
View File
@@ -42,54 +42,47 @@ class ValidationCoordinator:
def _validate_plugin_contexts(self, options: OCROptions) -> None:
"""Validate plugin options that require external context."""
if not self.registry:
return
registered_models = self.registry.get_registered_models()
# For now, we'll run the plugin validation directly since the models
# are still being integrated. This ensures the validation warnings
# and checks still work as expected.
# Validate Tesseract options with language context
if 'tesseract' in registered_models:
from ocrmypdf.builtin_plugins.tesseract_ocr import TesseractOptions
# Create TesseractOptions from legacy fields for validation
tesseract_data = {
'config': options.tesseract_config,
'pagesegmode': options.tesseract_pagesegmode,
'oem': options.tesseract_oem,
'thresholding': options.tesseract_thresholding,
'timeout': options.tesseract_timeout,
'non_ocr_timeout': options.tesseract_non_ocr_timeout or 180.0,
'downsample_large_images': options.tesseract_downsample_large_images,
'downsample_above': options.tesseract_downsample_above,
'user_words': options.user_words,
'user_patterns': options.user_patterns,
}
# Remove None values
tesseract_data = {k: v for k, v in tesseract_data.items() if v is not None}
tesseract_options = TesseractOptions(**tesseract_data)
tesseract_options.validate_with_context(options.languages)
# Run Tesseract validation
self._validate_tesseract_options(options)
# Validate Optimize options with external program context
if 'optimize' in registered_models:
from ocrmypdf.builtin_plugins.optimize import OptimizeOptions
from ocrmypdf._exec import jbig2enc, pngquant
optimize_data = {
'level': options.optimize,
'jpeg_quality': options.jpeg_quality or 0,
'png_quality': options.png_quality or 0,
'jbig2_lossy': options.jbig2_lossy or False,
'jbig2_page_group_size': options.jbig2_page_group_size or 0,
'jbig2_threshold': options.jbig2_threshold,
}
optimize_options = OptimizeOptions(**optimize_data)
external_programs = {
'pngquant': pngquant.available(),
'jbig2enc': jbig2enc.available(),
}
optimize_options.validate_with_context(external_programs)
# Run Optimize validation
self._validate_optimize_options(options)
def _validate_tesseract_options(self, options: OCROptions) -> None:
"""Validate Tesseract options."""
# Check pagesegmode warning
if options.tesseract_pagesegmode in (0, 2):
log.warning(
"The tesseract-pagesegmode you selected will disable OCR. "
"This may cause processing to fail."
)
# Check downsample consistency
if (
options.tesseract_downsample_above != 32767
and not options.tesseract_downsample_large_images
):
log.warning(
"The --tesseract-downsample-above argument will have no effect unless "
"--tesseract-downsample-large-images is also given."
)
def _validate_optimize_options(self, options: OCROptions) -> None:
"""Validate optimization options."""
# Check optimization consistency
if options.optimize == 0 and any([
options.jbig2_lossy,
options.png_quality and options.png_quality > 0,
options.jpeg_quality and options.jpeg_quality > 0
]):
log.warning(
"The arguments --jbig2-lossy, --png-quality, and --jpeg-quality "
"will be ignored because --optimize=0."
)
def _validate_cross_cutting_concerns(self, options: OCROptions) -> None:
"""Validate cross-cutting concerns that span multiple plugins."""
@@ -98,14 +91,12 @@ class ValidationCoordinator:
1 for opt in [options.force_ocr, options.skip_text, options.redo_ocr] if opt
)
if exclusive_options >= 2:
from ocrmypdf.exceptions import BadArgsError
raise BadArgsError("Choose only one of --force-ocr, --skip-text, --redo-ocr.")
raise ValueError("Choose only one of --force-ocr, --skip-text, --redo-ocr.")
# Validate redo_ocr compatibility
if options.redo_ocr:
if options.deskew or options.clean_final or options.remove_background:
from ocrmypdf.exceptions import BadArgsError
raise BadArgsError(
raise ValueError(
"--redo-ocr is not currently compatible with --deskew, "
"--clean-final, and --remove-background"
)
@@ -114,8 +105,7 @@ class ValidationCoordinator:
if options.output_type == 'none' and str(options.output_file) not in (
os.devnull, '-'
):
from ocrmypdf.exceptions import BadArgsError
raise BadArgsError(
raise ValueError(
"Since you specified `--output-type none`, the output file "
f"{options.output_file} cannot be produced. Set the output file to "
"`-` to suppress this message."