Start pluggy-based plugin system
This commit is contained in:
@@ -15,10 +15,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import helpers, hocrtransform, leptonica, pdfa, pdfinfo
|
||||
from ._version import PROGRAM_NAME, __version__
|
||||
from .api import Verbosity, configure_logging, ocr
|
||||
from .exceptions import (
|
||||
|
||||
from pluggy import HookimplMarker
|
||||
|
||||
from ocrmypdf import helpers, hocrtransform, leptonica, pdfa, pdfinfo
|
||||
from ocrmypdf._version import PROGRAM_NAME, __version__
|
||||
from ocrmypdf.api import Verbosity, configure_logging, ocr
|
||||
from ocrmypdf.exceptions import (
|
||||
BadArgsError,
|
||||
DpiError,
|
||||
EncryptedPdfError,
|
||||
@@ -33,3 +36,5 @@ from .exceptions import (
|
||||
TesseractConfigError,
|
||||
UnsupportedImageFormatError,
|
||||
)
|
||||
|
||||
hookimpl = HookimplMarker('ocrmypdf')
|
||||
|
||||
@@ -24,11 +24,12 @@ import sys
|
||||
class PDFContext:
|
||||
"""Holds our context for a particular run of the pipeline"""
|
||||
|
||||
def __init__(self, options, work_folder, origin, pdfinfo):
|
||||
def __init__(self, options, work_folder, origin, pdfinfo, plugin_manager):
|
||||
self.options = options
|
||||
self.work_folder = work_folder
|
||||
self.origin = origin
|
||||
self.pdfinfo = pdfinfo
|
||||
self.plugin_manager = plugin_manager
|
||||
if options:
|
||||
self.name = os.path.basename(options.input_file)
|
||||
else:
|
||||
|
||||
@@ -194,6 +194,7 @@ def validate_pdfinfo_options(context):
|
||||
"form and all filled form fields. The output PDF will be "
|
||||
"'flattened' and will no longer be fillable."
|
||||
)
|
||||
context.plugin_manager.hook.prepare(options=options)
|
||||
|
||||
|
||||
def get_page_dpi(pageinfo, options):
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# © 2020 James R. Barlow: github.com/jbarlow83
|
||||
#
|
||||
# This file is part of OCRmyPDF.
|
||||
#
|
||||
# OCRmyPDF is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# OCRmyPDF is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from argparse import Namespace
|
||||
|
||||
import pluggy
|
||||
from PIL import Image
|
||||
|
||||
from ocrmypdf.pdfinfo import PdfInfo
|
||||
|
||||
hookspec = pluggy.HookspecMarker('ocrmypdf')
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
|
||||
@hookspec
|
||||
def prepare(options: Namespace) -> None:
|
||||
"""Called to notify a plugin that a file will be processed.
|
||||
|
||||
The plugin may modify the options. All objects that are in options must
|
||||
be picklable so they can be marshalled to child worker processes.
|
||||
|
||||
Typically, a plugin will call ``registry.register_plugin(__name__)`` to register
|
||||
all of its public functions with the plugin registry. Functions that are
|
||||
not intended for registration should be prefixed with an underscore.
|
||||
Functions that imported from other modules will be ignored by
|
||||
``.register_plugin()``. For example if you use ``from os import basename``,
|
||||
``basename`` will not be registered.
|
||||
"""
|
||||
|
||||
|
||||
@hookspec
|
||||
def validate(pdfinfo: PdfInfo, options: Namespace) -> None:
|
||||
"""Called to give a plugin an opportunity to review options and pdfinfo.
|
||||
|
||||
options contains the "work order" to process a particular file. pdfinfo
|
||||
contains information about the input file obtained after loading and
|
||||
parsing.
|
||||
|
||||
The plugin may raise InputFileError or any ExitCodeException to request
|
||||
normal termination. If the plugin raises another exception type, ocrmypdf
|
||||
will abort with an error and hold the plugin responsible.
|
||||
"""
|
||||
|
||||
|
||||
@hookspec
|
||||
def filter_ocr_image(image: Image) -> Image:
|
||||
"""Called to filter the image before it is sent to OCR.
|
||||
|
||||
This is the image that OCR sees, not what the user sees when they view the
|
||||
PDF.
|
||||
"""
|
||||
+39
-10
@@ -15,6 +15,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import logging.handlers
|
||||
import multiprocessing
|
||||
@@ -28,12 +29,14 @@ from pathlib import Path
|
||||
from tempfile import mkdtemp
|
||||
|
||||
import PIL
|
||||
import pluggy
|
||||
|
||||
from ._concurrent import exec_progress_pool
|
||||
from ._graft import OcrGrafter
|
||||
from ._jobcontext import PDFContext, cleanup_working_files
|
||||
from ._logging import PageNumberFilter
|
||||
from ._pipeline import (
|
||||
from ocrmypdf import _pluginspec
|
||||
from ocrmypdf._concurrent import exec_progress_pool
|
||||
from ocrmypdf._graft import OcrGrafter
|
||||
from ocrmypdf._jobcontext import PDFContext, cleanup_working_files
|
||||
from ocrmypdf._logging import PageNumberFilter
|
||||
from ocrmypdf._pipeline import (
|
||||
convert_to_pdfa,
|
||||
copy_final,
|
||||
create_ocr_image,
|
||||
@@ -58,14 +61,14 @@ from ._pipeline import (
|
||||
triage,
|
||||
validate_pdfinfo_options,
|
||||
)
|
||||
from ._validation import (
|
||||
from ocrmypdf._validation import (
|
||||
check_requested_output_file,
|
||||
create_input_file,
|
||||
report_output_file_size,
|
||||
)
|
||||
from .exceptions import ExitCode, ExitCodeException
|
||||
from .helpers import available_cpu_count, check_pdf
|
||||
from .pdfa import file_claims_pdfa
|
||||
from ocrmypdf.exceptions import ExitCode, ExitCodeException
|
||||
from ocrmypdf.helpers import available_cpu_count, check_pdf
|
||||
from ocrmypdf.pdfa import file_claims_pdfa
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -298,6 +301,24 @@ def configure_debug_logging(log_filename, prefix=''):
|
||||
return log_file_handler
|
||||
|
||||
|
||||
def _load_object_from_module(location):
|
||||
"""Load a object given a module location
|
||||
|
||||
For location=a.b.c, will effectively run "from a.b import c"
|
||||
|
||||
Example:
|
||||
_load_object_from_module("a.b.c")
|
||||
|
||||
"""
|
||||
module_parts = location.split('.')
|
||||
module_name = '.'.join(module_parts[:-1])
|
||||
object_name = module_parts[-1]
|
||||
module = importlib.import_module(module_name)
|
||||
obj = getattr(module, object_name)
|
||||
log.debug(f"Loaded object: from {module_name} import {object_name}")
|
||||
return obj
|
||||
|
||||
|
||||
def run_pipeline(options, api=False):
|
||||
# Any changes to options will not take effect for options that are already
|
||||
# bound to function parameters in the pipeline. (For example
|
||||
@@ -312,6 +333,14 @@ def run_pipeline(options, api=False):
|
||||
):
|
||||
debug_log_handler = configure_debug_logging(Path(work_folder) / "debug.log")
|
||||
|
||||
pm = pluggy.PluginManager('ocrmypdf')
|
||||
pm.add_hookspecs(_pluginspec)
|
||||
|
||||
for name in options.plugins:
|
||||
# module = _load_object_from_module(name)
|
||||
module = importlib.import_module(name)
|
||||
pm.register(module)
|
||||
|
||||
try:
|
||||
check_requested_output_file(options)
|
||||
start_input_file, original_filename = create_input_file(options, work_folder)
|
||||
@@ -331,7 +360,7 @@ def run_pipeline(options, api=False):
|
||||
max_workers=options.jobs if not options.use_threads else 1, # To help debug
|
||||
)
|
||||
|
||||
context = PDFContext(options, work_folder, origin_pdf, pdfinfo)
|
||||
context = PDFContext(options, work_folder, origin_pdf, pdfinfo, pm)
|
||||
|
||||
# Validate options are okay for this pdf
|
||||
validate_pdfinfo_options(context)
|
||||
|
||||
@@ -480,6 +480,12 @@ advanced.add_argument(
|
||||
"which do not benefit. If the threshold is 0 it will be apply to all files. "
|
||||
"Set the threshold very high to disable.",
|
||||
)
|
||||
advanced.add_argument(
|
||||
'--plugins',
|
||||
action='append',
|
||||
default=[],
|
||||
help="Path to a folder than contains plugins.",
|
||||
)
|
||||
|
||||
debugging = parser.add_argument_group(
|
||||
"Debugging", "Arguments to help with troubleshooting and debugging"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import ocrmypdf
|
||||
|
||||
|
||||
@ocrmypdf.hookimpl
|
||||
def prepare(options):
|
||||
raise ValueError('foo')
|
||||
Reference in New Issue
Block a user