From 1b4542aa7785469d5575adb2815aed6a5ed95a57 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Thu, 7 Mar 2019 14:27:16 -0800 Subject: [PATCH] Further fixes to external program version testing --- docs/advanced.rst | 1 - docs/release_notes.rst | 7 ++- src/ocrmypdf/__main__.py | 97 +++++++++++------------------------ src/ocrmypdf/exec/__init__.py | 97 ++++++++++++++++++++--------------- 4 files changed, 91 insertions(+), 111 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 50d717e3..da1f1675 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -85,7 +85,6 @@ For example, if you have a development build of Tesseract don't wish to use the In this example ``TESSDATA_PREFIX`` is required to redirect Tesseract to an alternate folder for its "tessdata" files. - Overriding other support programs """"""""""""""""""""""""""""""""" diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 48c8f88a..70f4c434 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -13,13 +13,18 @@ Note that it is licensed under GPLv3, so scripts that ``import ocrmypdf`` and ar find: [^`]\#([0-9]{1,3})[^0-9] replace: `#$1 `_ -v8.2.1 +v8.2.2 ------ - Fixed a regression from v8.2.0, an exception that occurred while attempting to report that ``unpaper`` or another optional dependency was unavailable. - In some cases, ``ocrmypdf [-c|--clean]`` failed to exit with an error when ``unpaper`` is not installed. +v8.2.1 +------ + +- This release was canceled. + v8.2.0 ------ diff --git a/src/ocrmypdf/__main__.py b/src/ocrmypdf/__main__.py index f1d1c80f..3f2042df 100755 --- a/src/ocrmypdf/__main__.py +++ b/src/ocrmypdf/__main__.py @@ -46,6 +46,7 @@ from .exceptions import ( ) from .exec import ( ghostscript, + jbig2enc, qpdf, tesseract, check_external_program, @@ -602,47 +603,19 @@ def check_options_sidecar(options, log): options.sidecar = options.output_file + '.txt' -def _optional_program_required(name, version_fn, min_version, for_argument): - try: - version = version_fn() - except (FileNotFoundError, MissingDependencyError): - raise MissingDependencyError( - f"Install the '{name}' program to use {for_argument}." - ) - else: - if version < min_version: - raise MissingDependencyError( - f"The installed '{name}' is not supported. " - f"Install version {min_version} or newer." - ) - - -def _optional_program_recommended(name, version_fn, min_version, for_argument): - try: - version = version_fn() - except (FileNotFoundError, MissingDependencyError): - complain( - f"For best results, install the optional program '{name}' to use the " - f"argument {for_argument}." - ) - else: - if version < min_version: - raise MissingDependencyError( - f"The installed '{name}' is not supported. " - f"Install version {min_version} or newer." - ) - - def check_options_preprocessing(options, log): if options.clean_final: options.clean = True if options.unpaper_args and not options.clean: raise argparse.ArgumentError(None, "--clean is required for --unpaper-args") if options.clean: - from .exec import unpaper - - _optional_program_required( - 'unpaper', unpaper.version, '6.1', '--clean, --clean-final' + check_external_program( + log=log, + program='unpaper', + package='unpaper', + version_checker=unpaper.version, + need_version='6.1', + required_for=['--clean, --clean-final'], ) try: if options.unpaper_args: @@ -668,19 +641,26 @@ def check_options_ocr_behavior(options, log): def check_options_optimizing(options, log): if options.optimize >= 2: - from .exec import pngquant, jbig2enc - - _optional_program_required( - 'pngquant', pngquant.version, '2.0.1', '--optimize {2,3}' + check_external_program( + log=log, + program='pngquant', + package='pngquant', + version_checker=pngquant.version, + need_version='2.0.1', + required_for='--optimize {2,3}', ) - if options.jbig2_lossy: - _optional_program_required('jbig2', jbig2enc.version, '0.28', '--jbig2-lossy') - elif options.optimize >= 2: + if options.optimize >= 2: # Although we use JBIG2 for optimize=1, don't nag about it unless the # user is asking for more optimization - _optional_program_recommended( - 'jbig2', jbig2enc.version, '0.28', '--optimize {2,3}' + check_external_program( + log=log, + program='jbig2', + package='jbig2enc', + version_checker=jbig2enc.version, + need_version='0.28', + required_for='--optimize {2,3} | --jbig2-lossy', + recommended=True if not options.jbig2_lossy else False, ) if options.optimize == 0 and any( @@ -1031,7 +1011,7 @@ def report_output_file_size(options, _log, input_file, output_file): ) -def check_dependency_versions(log): +def check_dependency_versions(options, log): check_external_program( log=log, program='tesseract', @@ -1055,27 +1035,10 @@ def check_dependency_versions(log): return ExitCode.missing_dependency check_external_program( log=log, - program='unpaper', - package='unpaper', - version_checker=unpaper.version, - need_version='6.1', # latest sane version - optional=True, - ) - if os.environ.get('TRAVIS') != 'true': # Suppress for Ubuntu trusty - check_external_program( - log=log, - program='qpdf', - package='qpdf', - version_checker=qpdf.version, - need_version='8.0.2', - ) - check_external_program( - log=log, - program='pngquant', - package='pngquant', - version_checker=pngquant.version, - need_version='2.0.0', - optional=True, + program='qpdf', + package='qpdf', + version_checker=qpdf.version, + need_version='8.0.2', ) @@ -1095,7 +1058,7 @@ def run_pipeline(args=None): ) preamble(_log) check_options(options, _log) - check_dependency_versions(_log) + check_dependency_versions(options, _log) # Any changes to options will not take effect for options that are already # bound to function parameters in the pipeline. (For example diff --git a/src/ocrmypdf/exec/__init__.py b/src/ocrmypdf/exec/__init__.py index e94c387c..31d3603f 100644 --- a/src/ocrmypdf/exec/__init__.py +++ b/src/ocrmypdf/exec/__init__.py @@ -21,7 +21,7 @@ import os import re import sys from subprocess import run, STDOUT, PIPE, CalledProcessError -from ..exceptions import MissingDependencyError +from ..exceptions import MissingDependencyError, ExitCode from collections.abc import Mapping @@ -43,7 +43,7 @@ def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)'): f"Could not find program '{program}' on the PATH" ) from e except CalledProcessError as e: - if e.returncode < 0: + if e.returncode != 0: raise MissingDependencyError( f"Ran program '{program}' but it exited with an error:\n{e.output}" ) from e @@ -66,10 +66,17 @@ The program '{program}' could not be executed or was not found on your system PATH. ''' -unknown_version = ''' -OCRmyPDF requires '{program}' {need_version} or higher. Your system has -'{program}' but we cannot tell what version is installed. Contact the -package maintainer. +missing_optional_program = ''' +The program '{program}' could not be executed or was not found on your +system PATH. This program is required when you use the +{required_for} arguments. You could try omitting these arguments, or install +the package. +''' + +missing_recommend_program = ''' +The program '{program}' could not be executed or was not found on your +system PATH. This program is recommended when using the {required_for} arguments, +but not required, so we will proceed. For best results, install the program. ''' old_version = ''' @@ -77,20 +84,15 @@ OCRmyPDF requires '{program}' {need_version} or higher. Your system appears to have {found_version}. Please update this program. ''' -okay_its_optional = ''' -This program is OPTIONAL, so installation of OCRmyPDF can proceed, but -some functionality may be missing. -''' - -not_okay_its_required = ''' -This program is REQUIRED for OCRmyPDF to work. Installation will abort. +old_version_required_for = ''' +OCRmyPDF requires '{program}' {need_version} or higher when run with the +{required_for} arguments. If you omit these arguments, OCRmyPDF may be able to +proceed. For best results, install the program. ''' osx_install_advice = ''' If you have homebrew installed, try these command to install the missing -packages: - brew update - brew upgrade +package: brew install {package} ''' @@ -105,7 +107,7 @@ installing the RPM for {program}. ''' -def get_platform(): +def _get_platform(): if sys.platform.startswith('freebsd'): return 'freebsd' elif sys.platform.startswith('linux'): @@ -113,48 +115,59 @@ def get_platform(): return sys.platform -def _error_trailer(log, program, package, optional, **kwargs): - if optional: - log.error(okay_its_optional.format(**locals())) - else: - log.error(not_okay_its_required.format(**locals())) - +def _error_trailer(log, program, package, **kwargs): if isinstance(package, Mapping): - package = package[get_platform()] + package = package[_get_platform()] - if get_platform() == 'darwin': - log.error(osx_install_advice.format(**locals())) - elif get_platform() == 'linux': - log.error(linux_install_advice.format(**locals())) + if _get_platform() == 'darwin': + log.info(osx_install_advice.format(**locals())) + elif _get_platform() == 'linux': + log.info(linux_install_advice.format(**locals())) -def error_missing_program(log, program, package, optional): - log.error(missing_program.format(**locals())) +def _error_missing_program(log, program, package, required_for, recommended): + if required_for: + log.error(missing_optional_program.format(**locals())) + elif recommended: + log.info(missing_recommend_program.format(**locals())) + else: + log.error(missing_program.format(**locals())) _error_trailer(**locals()) -def error_unknown_version(log, program, package, optional, need_version): - log.error(unknown_version.format(**locals())) - _error_trailer(**locals()) - - -def error_old_version(log, program, package, optional, need_version, found_version): - log.error(old_version.format(**locals())) +def _error_old_version( + log, program, package, need_version, found_version, required_for +): + if required_for: + log.error(old_version_required_for.format(**locals())) + else: + log.error(old_version.format(**locals())) _error_trailer(**locals()) def check_external_program( - log, program, package, version_checker, need_version, optional=False + *, + log, + program, + package, + version_checker, + need_version, + required_for=None, + recommended=False, ): try: found_version = version_checker() except (CalledProcessError, FileNotFoundError, MissingDependencyError): - error_missing_program(log, program, package, optional) - if not optional: - sys.exit(1) + _error_missing_program(log, program, package, required_for, recommended) + if not recommended: + sys.exit(ExitCode.missing_dependency) return if found_version < need_version: - error_old_version(log, program, package, optional, need_version, found_version) + _error_old_version( + log, program, package, need_version, found_version, required_for + ) + if not recommended: + sys.exit(ExitCode.missing_dependency) log.debug(f'Found {program} {found_version}')