From e8ae370ceb9c22798955c0db33f46ed30f07cd65 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Fri, 13 Oct 2023 02:19:08 -0700 Subject: [PATCH] Eliminate api= kwarg and implicit creation of pluginmanager --- src/ocrmypdf/__main__.py | 4 +-- src/ocrmypdf/_pipelines/common.py | 14 ++++---- src/ocrmypdf/_pipelines/hocr_to_ocr_pdf.py | 4 +-- src/ocrmypdf/_pipelines/pdf_to_hocr.py | 4 +-- src/ocrmypdf/_pipelines/standard.py | 37 ++++++++++++---------- src/ocrmypdf/_sync.py | 9 ++++-- src/ocrmypdf/api.py | 5 +-- tests/conftest.py | 14 ++++---- 8 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/ocrmypdf/__main__.py b/src/ocrmypdf/__main__.py index eb570d18..85086c78 100755 --- a/src/ocrmypdf/__main__.py +++ b/src/ocrmypdf/__main__.py @@ -14,7 +14,7 @@ from contextlib import suppress from ocrmypdf import __version__ from ocrmypdf._plugin_manager import get_parser_options_plugins -from ocrmypdf._sync import run_pipeline +from ocrmypdf._sync import run_pipeline_cli from ocrmypdf._validation import check_options from ocrmypdf.api import Verbosity, configure_logging from ocrmypdf.exceptions import ( @@ -71,7 +71,7 @@ def run(args=None): with suppress(AttributeError, OSError): signal.signal(signal.SIGBUS, sigbus) - result = run_pipeline(options=options, plugin_manager=plugin_manager) + result = run_pipeline_cli(options=options, plugin_manager=plugin_manager) return result diff --git a/src/ocrmypdf/_pipelines/common.py b/src/ocrmypdf/_pipelines/common.py index db823ada..5fef133c 100644 --- a/src/ocrmypdf/_pipelines/common.py +++ b/src/ocrmypdf/_pipelines/common.py @@ -203,10 +203,12 @@ def manage_work_folder(*, work_folder: Path, retain: bool, print_location: bool) def cli_exception_handler( - fn: Callable[[Any], ExitCode], *args, options: argparse.Namespace + fn: Callable[[argparse.Namespace, OcrmypdfPluginManager], ExitCode], + options: argparse.Namespace, + plugin_manager: OcrmypdfPluginManager, ) -> ExitCode: try: - return fn(*args) + return fn(options, plugin_manager) except KeyboardInterrupt: if options.verbose >= 1: log.exception("KeyboardInterrupt") @@ -247,19 +249,17 @@ def cli_exception_handler( def setup_pipeline( options: argparse.Namespace, - plugin_manager: OcrmypdfPluginManager | None, -) -> tuple[Executor, OcrmypdfPluginManager]: + plugin_manager: OcrmypdfPluginManager, +) -> Executor: # Any changes to options will not take effect for options that are already # bound to function parameters in the pipeline. (For example # options.input_file, options.pdf_renderer are already bound.) if not options.jobs: options.jobs = available_cpu_count() - if not plugin_manager: - plugin_manager = get_plugin_manager(options.plugins) pikepdf_enable_mmap() executor = setup_executor(plugin_manager) - return executor, plugin_manager + return executor def preprocess( diff --git a/src/ocrmypdf/_pipelines/hocr_to_ocr_pdf.py b/src/ocrmypdf/_pipelines/hocr_to_ocr_pdf.py index 6f23425b..542789b3 100644 --- a/src/ocrmypdf/_pipelines/hocr_to_ocr_pdf.py +++ b/src/ocrmypdf/_pipelines/hocr_to_ocr_pdf.py @@ -111,12 +111,12 @@ def exec_hocr_to_ocr_pdf(context: PdfContext, executor: Executor) -> Sequence[st def run_hocr_to_ocr_pdf_pipeline( options: argparse.Namespace, *, - plugin_manager: OcrmypdfPluginManager | None, + plugin_manager: OcrmypdfPluginManager, ) -> ExitCode: with manage_work_folder( work_folder=options.input_folder, retain=True, print_location=False ) as work_folder: - executor, plugin_manager = setup_pipeline(options, plugin_manager) + executor = setup_pipeline(options, plugin_manager) origin_pdf = work_folder / 'origin.pdf' shutil.copy2(options.input_file, origin_pdf) diff --git a/src/ocrmypdf/_pipelines/pdf_to_hocr.py b/src/ocrmypdf/_pipelines/pdf_to_hocr.py index 9925a846..fb250a16 100644 --- a/src/ocrmypdf/_pipelines/pdf_to_hocr.py +++ b/src/ocrmypdf/_pipelines/pdf_to_hocr.py @@ -94,12 +94,12 @@ def exec_pdf_to_hocr(context: PdfContext, executor: Executor) -> None: def run_hocr_pipeline( options: argparse.Namespace, *, - plugin_manager: OcrmypdfPluginManager | None, + plugin_manager: OcrmypdfPluginManager, ) -> None: with manage_work_folder( work_folder=options.output_folder, retain=True, print_location=False ) as work_folder: - executor, plugin_manager = setup_pipeline(options, plugin_manager) + executor = setup_pipeline(options, plugin_manager) shutil.copy2(options.input_file, work_folder / 'origin.pdf') # Gather pdfinfo and create context diff --git a/src/ocrmypdf/_pipelines/standard.py b/src/ocrmypdf/_pipelines/standard.py index fb602c59..7a5bf488 100644 --- a/src/ocrmypdf/_pipelines/standard.py +++ b/src/ocrmypdf/_pipelines/standard.py @@ -161,7 +161,7 @@ def exec_concurrent(context: PdfContext, executor: Executor) -> Sequence[str]: def _run_pipeline( options: argparse.Namespace, - plugin_manager: OcrmypdfPluginManager | None, + plugin_manager: OcrmypdfPluginManager, ) -> ExitCode: with manage_work_folder( work_folder=Path(mkdtemp(prefix="ocrmypdf.io.")), @@ -170,7 +170,7 @@ def _run_pipeline( ) as work_folder, manage_debug_log_handler( options=options, work_folder=work_folder ): - executor, plugin_manager = setup_pipeline(options, plugin_manager) + executor = setup_pipeline(options, plugin_manager) check_requested_output_file(options) start_input_file, original_filename = create_input_file(options, work_folder) @@ -201,26 +201,31 @@ def _run_pipeline( return exitcode -def run_pipeline( +def run_pipeline_cli( options: argparse.Namespace, *, - plugin_manager: OcrmypdfPluginManager | None, - api: bool = False, + plugin_manager: OcrmypdfPluginManager, ) -> ExitCode: - """Run the OCR pipeline. + """Run the OCR pipeline with command line exception handling. Args: options: The parsed command line options. plugin_manager: The plugin manager to use. If not provided, one will be created. - api: If ``True``, the pipeline is being run from the API. This is used - to manage exceptions in a way appropriate for API or CLI usage. - For CLI (``api=False``), exceptions are printed and described; - for API use, they are propagated to the caller. """ - if api: - return _run_pipeline(options, plugin_manager) - else: - return cli_exception_handler( - _run_pipeline, options, plugin_manager, options=options - ) + return cli_exception_handler(_run_pipeline, options, plugin_manager) + + +def run_pipeline( + options: argparse.Namespace, + *, + plugin_manager: OcrmypdfPluginManager, +) -> ExitCode: + """Run the OCR pipeline without command line exception handling. + + Args: + options: The parsed command line options. + plugin_manager: The plugin manager to use. If not provided, one will be + created. + """ + return _run_pipeline(options, plugin_manager) diff --git a/src/ocrmypdf/_sync.py b/src/ocrmypdf/_sync.py index 5698f859..ef7f42fa 100644 --- a/src/ocrmypdf/_sync.py +++ b/src/ocrmypdf/_sync.py @@ -14,9 +14,14 @@ from ocrmypdf._pipelines.common import ( configure_debug_logging, ) from ocrmypdf._pipelines.pdf_to_hocr import run_hocr_pipeline -from ocrmypdf._pipelines.standard import run_pipeline +from ocrmypdf._pipelines.standard import run_pipeline, run_pipeline_cli log = logging.getLogger(__name__) -__all__ = ['run_pipeline', 'run_hocr_pipeline', 'configure_debug_logging'] +__all__ = [ + 'run_pipeline', + 'run_pipeline_cli', + 'run_hocr_pipeline', + 'configure_debug_logging', +] diff --git a/src/ocrmypdf/api.py b/src/ocrmypdf/api.py index d2a71b08..bd527bce 100644 --- a/src/ocrmypdf/api.py +++ b/src/ocrmypdf/api.py @@ -21,7 +21,7 @@ import pluggy from ocrmypdf._logging import PageNumberFilter from ocrmypdf._plugin_manager import get_plugin_manager -from ocrmypdf._sync import run_hocr_pipeline, run_pipeline +from ocrmypdf._sync import run_hocr_pipeline, run_pipeline, run_pipeline_cli from ocrmypdf._validation import check_options from ocrmypdf.cli import ArgumentParser, get_parser from ocrmypdf.helpers import is_iterable_notstr @@ -371,7 +371,7 @@ def ocr( # noqa: D417 **create_options_kwargs, ) check_options(options, plugin_manager) - return run_pipeline(options=options, plugin_manager=plugin_manager, api=True) + return run_pipeline(options=options, plugin_manager=plugin_manager) def pdf_to_hocr( @@ -465,4 +465,5 @@ __all__ = [ 'ocr', 'pdf_to_hocr', 'run_pipeline', + 'run_pipeline_cli', ] diff --git a/tests/conftest.py b/tests/conftest.py index 893cfb34..f662bd23 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -92,7 +92,7 @@ def check_ocrmypdf(input_file: Path, output_file: Path, *args) -> Path: _parser, options, plugin_manager = get_parser_options_plugins(args=api_args) api.check_options(options, plugin_manager) - result = api.run_pipeline(options, plugin_manager=plugin_manager, api=True) + result = api.run_pipeline(options, plugin_manager=plugin_manager) assert result == 0 assert output_file.exists(), "Output file not created" @@ -102,14 +102,14 @@ def check_ocrmypdf(input_file: Path, output_file: Path, *args) -> Path: def run_ocrmypdf_api(input_file: Path, output_file: Path, *args) -> ExitCode: - """Run ocrmypdf via its API in-process, and let test deal with results. + """Run ocrmypdf via its API in-process, but return CLI-style ExitCode. - This simulates calling the command line interface in a subprocess, but - is easier for debuggers and code coverage to follow. + This simulates calling the command line interface in a subprocess and allows us + to check that the command line interface is working correctly, but since it is + in-process it is easier to trace with a debugger or coverage tool. Any exception raised will be trapped and converted to an exit code. - The return code must always be checked or the test may declare a failure - to be pass. + The return code must be checked by the caller to determine if the test passed. """ api_args = [str(input_file), str(output_file)] + [ str(arg) for arg in args if arg is not None @@ -117,7 +117,7 @@ def run_ocrmypdf_api(input_file: Path, output_file: Path, *args) -> ExitCode: _parser, options, plugin_manager = get_parser_options_plugins(args=api_args) api.check_options(options, plugin_manager) - return api.run_pipeline(options, plugin_manager=None, api=False) + return api.run_pipeline_cli(options, plugin_manager=plugin_manager) def run_ocrmypdf(