refactor: clean up deprecated code and update plugin docs
- Remove outdated Phase comments from _options.py and cli.py - Remove unused methods from PluginOptionRegistry: - get_extended_options_model() - replaced by __getattr__ in OCROptions - map_legacy_options() - unused - validate_plugin_options() - unused - Update plugin documentation to document register_options hook - Add documentation for nested plugin option access pattern 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
0ad7f5fc13
commit
a4ee513cd4
@@ -153,10 +153,7 @@ class OCROptions(BaseModel):
|
||||
fast_web_view: float = 1.0
|
||||
continue_on_soft_render_error: bool | None = None
|
||||
|
||||
# Plugin option namespaces (for backward compatibility, will be removed in Phase 5)
|
||||
# These will be populated dynamically based on loaded plugins
|
||||
|
||||
# Legacy tesseract options (for backward compatibility)
|
||||
# Tesseract options - also accessible via options.tesseract.<field>
|
||||
tesseract_config: list[str] = []
|
||||
tesseract_pagesegmode: int | None = None
|
||||
tesseract_oem: int | None = None
|
||||
@@ -166,11 +163,11 @@ class OCROptions(BaseModel):
|
||||
tesseract_downsample_above: int = 32767
|
||||
tesseract_downsample_large_images: bool | None = None
|
||||
|
||||
# Legacy ghostscript options (for backward compatibility)
|
||||
# Ghostscript options - also accessible via options.ghostscript.<field>
|
||||
pdfa_image_compression: str | None = None
|
||||
color_conversion_strategy: str = "LeaveColorUnchanged"
|
||||
|
||||
# Legacy jbig2 options (for backward compatibility)
|
||||
# Optimize/JBIG2 options - also accessible via options.optimize.<field>
|
||||
jbig2_lossy: bool | None = None
|
||||
jbig2_page_group_size: int | None = None
|
||||
jbig2_threshold: float = 0.85
|
||||
|
||||
@@ -7,17 +7,22 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pydantic import BaseModel, create_model
|
||||
from pydantic import BaseModel
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PluginOptionRegistry:
|
||||
"""Registry for plugin option models."""
|
||||
"""Registry for plugin option models.
|
||||
|
||||
This registry collects option models from plugins during initialization.
|
||||
Plugin options can be accessed via nested namespaces on OCROptions
|
||||
(e.g., options.tesseract.timeout) or via flat field names for backward
|
||||
compatibility (e.g., options.tesseract_timeout).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._option_models: dict[str, type[BaseModel]] = {}
|
||||
self._extended_model_cache: type[BaseModel] | None = None
|
||||
|
||||
def register_option_model(
|
||||
self, namespace: str, model_class: type[BaseModel]
|
||||
@@ -34,123 +39,12 @@ class PluginOptionRegistry:
|
||||
)
|
||||
|
||||
self._option_models[namespace] = model_class
|
||||
# Clear cache when new models are registered
|
||||
self._extended_model_cache = None
|
||||
|
||||
log.debug(
|
||||
f"Registered plugin option model for namespace '{namespace}': {model_class.__name__}"
|
||||
f"Registered plugin option model for namespace '{namespace}': "
|
||||
f"{model_class.__name__}"
|
||||
)
|
||||
|
||||
def get_registered_models(self) -> dict[str, type[BaseModel]]:
|
||||
"""Get all registered plugin option models."""
|
||||
return self._option_models.copy()
|
||||
|
||||
def get_extended_options_model(
|
||||
self, base_model: type[BaseModel]
|
||||
) -> type[BaseModel]:
|
||||
"""Create an extended options model that includes all registered plugin options.
|
||||
|
||||
Args:
|
||||
base_model: The base OCROptions model to extend
|
||||
|
||||
Returns:
|
||||
A new model class that includes the base model fields plus all plugin option fields
|
||||
"""
|
||||
if self._extended_model_cache is not None:
|
||||
return self._extended_model_cache
|
||||
|
||||
# Start with base model fields
|
||||
model_fields = {}
|
||||
|
||||
# Add plugin option models as nested fields
|
||||
for namespace, model_class in self._option_models.items():
|
||||
model_fields[namespace] = (model_class, model_class())
|
||||
|
||||
# Create the extended model
|
||||
self._extended_model_cache = create_model(
|
||||
'ExtendedOCROptions', __base__=base_model, **model_fields
|
||||
)
|
||||
|
||||
return self._extended_model_cache
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
"""Clear the extended model cache."""
|
||||
self._extended_model_cache = None
|
||||
|
||||
def map_legacy_options(self, options: dict) -> dict:
|
||||
"""Map legacy flat options to nested plugin options.
|
||||
|
||||
This method helps with backward compatibility by mapping flat option
|
||||
names (like 'tesseract_timeout') to nested plugin option structures.
|
||||
|
||||
Args:
|
||||
options: Dictionary of flat options
|
||||
|
||||
Returns:
|
||||
Dictionary with both legacy flat options and nested plugin options
|
||||
"""
|
||||
result = options.copy()
|
||||
|
||||
# Map tesseract options
|
||||
if 'tesseract' in self._option_models:
|
||||
tesseract_options = {}
|
||||
tesseract_fields = self._option_models['tesseract'].model_fields.keys()
|
||||
|
||||
for field in tesseract_fields:
|
||||
legacy_key = f'tesseract_{field}'
|
||||
if legacy_key in options:
|
||||
tesseract_options[field] = options[legacy_key]
|
||||
|
||||
if tesseract_options:
|
||||
result['tesseract'] = tesseract_options
|
||||
|
||||
# Map optimize options
|
||||
if 'optimize' in self._option_models:
|
||||
optimize_options = {}
|
||||
optimize_fields = self._option_models['optimize'].model_fields.keys()
|
||||
|
||||
for field in optimize_fields:
|
||||
if field in options:
|
||||
optimize_options[field] = options[field]
|
||||
# Handle special case for optimize level
|
||||
elif field == 'level' and 'optimize' in options:
|
||||
optimize_options[field] = options['optimize']
|
||||
|
||||
if optimize_options:
|
||||
result['optimize'] = optimize_options
|
||||
|
||||
# Map ghostscript options
|
||||
if 'ghostscript' in self._option_models:
|
||||
ghostscript_options = {}
|
||||
ghostscript_fields = self._option_models['ghostscript'].model_fields.keys()
|
||||
|
||||
for field in ghostscript_fields:
|
||||
if field in options:
|
||||
ghostscript_options[field] = options[field]
|
||||
|
||||
if ghostscript_options:
|
||||
result['ghostscript'] = ghostscript_options
|
||||
|
||||
return result
|
||||
|
||||
def validate_plugin_options(self, options: dict) -> dict:
|
||||
"""Validate plugin options using their registered models.
|
||||
|
||||
Args:
|
||||
options: Dictionary containing plugin options
|
||||
|
||||
Returns:
|
||||
Dictionary with validated plugin options
|
||||
|
||||
Raises:
|
||||
ValidationError: If any plugin options are invalid
|
||||
"""
|
||||
validated = {}
|
||||
|
||||
for namespace, model_class in self._option_models.items():
|
||||
if namespace in options:
|
||||
# Validate using the plugin's model
|
||||
plugin_options = model_class(**options[namespace])
|
||||
validated[namespace] = plugin_options.model_dump()
|
||||
|
||||
return validated
|
||||
|
||||
@@ -462,10 +462,6 @@ def namespace_to_options(ns) -> OCROptions:
|
||||
if 'work_folder' in extra_attrs and 'input_file' not in known_fields:
|
||||
known_fields['input_file'] = '/dev/null' # Placeholder
|
||||
|
||||
# Handle backward compatibility for plugin options
|
||||
# Map CLI arguments to the appropriate fields for now
|
||||
# In Phase 2, this will be handled by plugin option models
|
||||
|
||||
instance = OCROptions(**known_fields)
|
||||
instance.extra_attrs = extra_attrs
|
||||
return instance
|
||||
|
||||
Reference in New Issue
Block a user