From 95d9c3ed18849dd27f77779710c4558325d478b9 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Mon, 15 Dec 2025 01:19:34 -0800 Subject: [PATCH] fix: add cross-cutting validation to OCROptions model Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) --- src/ocrmypdf/_options.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/ocrmypdf/_options.py b/src/ocrmypdf/_options.py index 7364aeb5..2bf95e30 100644 --- a/src/ocrmypdf/_options.py +++ b/src/ocrmypdf/_options.py @@ -291,8 +291,40 @@ class OCROptions(BaseModel): data['output_file'] = '/dev/null' # Placeholder return data - # Note: Cross-cutting validation moved to ValidationCoordinator - # Basic field validation remains here, complex validation moved to coordinator + @model_validator(mode='after') + def validate_exclusive_ocr_options(self): + """Ensure only one of force_ocr, skip_text, redo_ocr is set.""" + exclusive_options = sum( + 1 for opt in [self.force_ocr, self.skip_text, self.redo_ocr] if opt + ) + if exclusive_options >= 2: + raise ValueError("Choose only one of --force-ocr, --skip-text, --redo-ocr.") + return self + + @model_validator(mode='after') + def validate_redo_ocr_options(self): + """Validate options compatible with redo_ocr.""" + if self.redo_ocr: + if self.deskew or self.clean_final or self.remove_background: + raise ValueError( + "--redo-ocr is not currently compatible with --deskew, " + "--clean-final, and --remove-background" + ) + return self + + @model_validator(mode='after') + def validate_output_type_compatibility(self): + """Validate output type is compatible with output file.""" + if self.output_type == 'none' and str(self.output_file) not in ( + os.devnull, + '-', + ): + raise ValueError( + "Since you specified `--output-type none`, the output file " + f"{self.output_file} cannot be produced. Set the output file to " + f"`-` to suppress this message." + ) + return self @property def lossless_reconstruction(self):