refactor: reorganize CLI and options initialization
Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
@@ -14,10 +14,9 @@ import sys
|
||||
from contextlib import suppress
|
||||
|
||||
from ocrmypdf import __version__
|
||||
from ocrmypdf._options import OCROptions
|
||||
from ocrmypdf._pipelines.ocr import run_pipeline_cli
|
||||
from ocrmypdf._plugin_manager import get_parser_options_plugins
|
||||
from ocrmypdf._validation import check_options
|
||||
from ocrmypdf.cli import get_options_and_plugins
|
||||
from ocrmypdf.api import Verbosity, configure_logging
|
||||
from ocrmypdf.exceptions import (
|
||||
BadArgsError,
|
||||
@@ -40,10 +39,7 @@ def sigbus(*args):
|
||||
|
||||
def run(args=None):
|
||||
"""Run the ocrmypdf command line interface."""
|
||||
_parser, namespace_options, plugin_manager = get_parser_options_plugins(args=args)
|
||||
|
||||
# Convert Namespace to OCROptions
|
||||
options = OCROptions.from_namespace(namespace_options)
|
||||
options, plugin_manager = get_options_and_plugins(args=args)
|
||||
|
||||
with suppress(AttributeError, PermissionError):
|
||||
os.nice(5)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from argparse import Namespace
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
|
||||
@@ -26,19 +25,13 @@ class PdfContext:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: OCROptions | Namespace,
|
||||
options: OCROptions,
|
||||
work_folder: Path,
|
||||
origin: Path,
|
||||
pdfinfo: PdfInfo,
|
||||
plugin_manager,
|
||||
):
|
||||
# Handle both OCROptions and Namespace during transition
|
||||
if isinstance(options, OCROptions):
|
||||
self.options = options
|
||||
else:
|
||||
# Convert Namespace to OCROptions
|
||||
self.options = OCROptions.from_namespace(options)
|
||||
|
||||
self.options = options
|
||||
self.work_folder = work_folder
|
||||
self.origin = origin
|
||||
self.pdfinfo = pdfinfo
|
||||
|
||||
@@ -9,7 +9,6 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import unicodedata
|
||||
from argparse import Namespace
|
||||
from collections.abc import Sequence
|
||||
from io import IOBase
|
||||
from pathlib import Path
|
||||
@@ -165,30 +164,6 @@ class OCROptions(BaseModel):
|
||||
default_factory=dict, exclude=True, alias='_extra_attrs'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_namespace(cls, ns: Namespace) -> OCROptions:
|
||||
"""Convert argparse.Namespace to OCROptions."""
|
||||
# Extract known fields
|
||||
known_fields = {}
|
||||
extra_attrs = {}
|
||||
|
||||
for key, value in vars(ns).items():
|
||||
if key in cls.model_fields:
|
||||
known_fields[key] = value
|
||||
else:
|
||||
extra_attrs[key] = value
|
||||
|
||||
# Handle special cases for hOCR API
|
||||
if 'output_folder' in extra_attrs and 'output_file' not in known_fields:
|
||||
known_fields['output_file'] = '/dev/null' # Placeholder
|
||||
|
||||
# Handle case where input_file is missing (e.g., in _hocr_to_ocr_pdf)
|
||||
if 'work_folder' in extra_attrs and 'input_file' not in known_fields:
|
||||
known_fields['input_file'] = '/dev/null' # Placeholder
|
||||
|
||||
instance = cls(**known_fields)
|
||||
instance.extra_attrs = extra_attrs
|
||||
return instance
|
||||
|
||||
@field_validator('languages')
|
||||
@classmethod
|
||||
|
||||
@@ -17,7 +17,7 @@ import pluggy
|
||||
|
||||
import ocrmypdf.builtin_plugins
|
||||
from ocrmypdf import pluginspec
|
||||
from ocrmypdf.cli import get_parser, plugins_only_parser
|
||||
from ocrmypdf.cli import get_parser
|
||||
|
||||
|
||||
class OcrmypdfPluginManager(pluggy.PluginManager):
|
||||
@@ -101,20 +101,4 @@ def get_plugin_manager(
|
||||
)
|
||||
|
||||
|
||||
def get_parser_options_plugins(
|
||||
args: Sequence[str],
|
||||
) -> tuple[argparse.ArgumentParser, argparse.Namespace, pluggy.PluginManager]:
|
||||
pre_options, _unused = plugins_only_parser.parse_known_args(args=args)
|
||||
plugin_manager = get_plugin_manager(pre_options.plugins)
|
||||
|
||||
parser = get_parser()
|
||||
plugin_manager.hook.initialize( # pylint: disable=no-member
|
||||
plugin_manager=plugin_manager
|
||||
)
|
||||
plugin_manager.hook.add_options(parser=parser) # pylint: disable=no-member
|
||||
|
||||
options = parser.parse_args(args=args)
|
||||
return parser, options, plugin_manager
|
||||
|
||||
|
||||
__all__ = ['OcrmypdfPluginManager', 'get_plugin_manager', 'get_parser_options_plugins']
|
||||
__all__ = ['OcrmypdfPluginManager', 'get_plugin_manager']
|
||||
|
||||
+68
-1
@@ -9,7 +9,7 @@ import argparse
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from ocrmypdf._defaults import DEFAULT_ROTATE_PAGES_THRESHOLD
|
||||
from ocrmypdf._defaults import DEFAULT_LANGUAGE, DEFAULT_ROTATE_PAGES_THRESHOLD
|
||||
from ocrmypdf._defaults import PROGRAM_NAME as _PROGRAM_NAME
|
||||
from ocrmypdf._version import __version__ as _VERSION
|
||||
|
||||
@@ -465,3 +465,70 @@ plugins_only_parser.add_argument(
|
||||
default=[],
|
||||
help="Name of plugin to import.",
|
||||
)
|
||||
|
||||
|
||||
def namespace_to_options(ns) -> 'OCROptions':
|
||||
"""Convert argparse.Namespace to OCROptions.
|
||||
|
||||
This function encapsulates CLI-specific knowledge of how command line
|
||||
arguments map to our internal options model.
|
||||
"""
|
||||
from ocrmypdf._options import OCROptions
|
||||
|
||||
# Extract known fields
|
||||
known_fields = {}
|
||||
extra_attrs = {}
|
||||
|
||||
for key, value in vars(ns).items():
|
||||
if key in OCROptions.model_fields:
|
||||
known_fields[key] = value
|
||||
else:
|
||||
extra_attrs[key] = value
|
||||
|
||||
# Handle special cases for hOCR API
|
||||
if 'output_folder' in extra_attrs and 'output_file' not in known_fields:
|
||||
known_fields['output_file'] = '/dev/null' # Placeholder
|
||||
|
||||
# Handle case where input_file is missing (e.g., in _hocr_to_ocr_pdf)
|
||||
if 'work_folder' in extra_attrs and 'input_file' not in known_fields:
|
||||
known_fields['input_file'] = '/dev/null' # Placeholder
|
||||
|
||||
instance = OCROptions(**known_fields)
|
||||
instance.extra_attrs = extra_attrs
|
||||
return instance
|
||||
|
||||
|
||||
def get_options_and_plugins(
|
||||
args=None,
|
||||
) -> tuple['OCROptions', 'pluggy.PluginManager']:
|
||||
"""Parse command line arguments and return OCROptions and plugin manager.
|
||||
|
||||
This is the main entry point for CLI argument processing. It handles
|
||||
plugin discovery, argument parsing, and conversion to our internal
|
||||
options model.
|
||||
|
||||
Args:
|
||||
args: Command line arguments. If None, uses sys.argv.
|
||||
|
||||
Returns:
|
||||
Tuple of (OCROptions, PluginManager)
|
||||
"""
|
||||
import pluggy
|
||||
from ocrmypdf._plugin_manager import get_plugin_manager
|
||||
|
||||
# First pass: get plugins so we can register their options
|
||||
pre_options, _unused = plugins_only_parser.parse_known_args(args=args)
|
||||
plugin_manager = get_plugin_manager(pre_options.plugins)
|
||||
|
||||
# Get parser and let plugins add their options
|
||||
parser = get_parser()
|
||||
plugin_manager.hook.initialize(plugin_manager=plugin_manager) # pylint: disable=no-member
|
||||
plugin_manager.hook.add_options(parser=parser) # pylint: disable=no-member
|
||||
|
||||
# Parse all arguments
|
||||
namespace = parser.parse_args(args=args)
|
||||
|
||||
# Convert to OCROptions
|
||||
options = namespace_to_options(namespace)
|
||||
|
||||
return options, plugin_manager
|
||||
|
||||
Reference in New Issue
Block a user