Move install-time external program checks out of setup.py
We did runtime tests for several of them anyway, and it's better to do at runtime since config may change after installation.
This commit is contained in:
+58
-32
@@ -44,7 +44,14 @@ from .exceptions import (
|
||||
MissingDependencyError,
|
||||
OutputFileAccessError,
|
||||
)
|
||||
from .exec import ghostscript, qpdf, tesseract
|
||||
from .exec import (
|
||||
ghostscript,
|
||||
qpdf,
|
||||
tesseract,
|
||||
check_external_program,
|
||||
unpaper,
|
||||
pngquant,
|
||||
)
|
||||
from .helpers import available_cpu_count, is_file_writable, re_symlink
|
||||
from .pdfa import file_claims_pdfa
|
||||
|
||||
@@ -67,13 +74,6 @@ if 'IDE_PROJECT_ROOTS' in os.environ:
|
||||
|
||||
verify_python3_env()
|
||||
|
||||
if not tesseract.v4:
|
||||
complain(
|
||||
f"Please install tesseract 4.0.0 or newer "
|
||||
f"(currently installed version is {tesseract.version()})"
|
||||
)
|
||||
sys.exit(ExitCode.missing_dependency)
|
||||
|
||||
# -------------
|
||||
# Parser
|
||||
|
||||
@@ -633,9 +633,7 @@ 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"
|
||||
)
|
||||
raise argparse.ArgumentError(None, "--clean is required for --unpaper-args")
|
||||
if any((options.clean, options.clean_final)):
|
||||
from .exec import unpaper
|
||||
|
||||
@@ -930,9 +928,6 @@ def log_page_orientations(pdfinfo, _log):
|
||||
|
||||
def preamble(_log):
|
||||
_log.debug('ocrmypdf ' + VERSION)
|
||||
_log.debug('tesseract ' + tesseract.version())
|
||||
_log.debug('qpdf ' + qpdf.version())
|
||||
_log.debug('gs ' + ghostscript.version())
|
||||
|
||||
|
||||
def check_environ(options, _log):
|
||||
@@ -1032,6 +1027,54 @@ def report_output_file_size(options, _log, input_file, output_file):
|
||||
)
|
||||
|
||||
|
||||
def check_dependency_versions(log):
|
||||
check_external_program(
|
||||
log=log,
|
||||
program='tesseract',
|
||||
package={'darwin': 'tesseract', 'linux': 'tesseract-ocr'},
|
||||
version_checker=tesseract.version,
|
||||
need_version='4.0.0', # using backport for Travis CI
|
||||
)
|
||||
check_external_program(
|
||||
log=log,
|
||||
program='gs',
|
||||
package='ghostscript',
|
||||
version_checker=ghostscript.version,
|
||||
need_version='9.15', # limited by Travis CI / Ubuntu 14.04 backports
|
||||
)
|
||||
if ghostscript.version() == '9.24':
|
||||
complain(
|
||||
"Ghostscript 9.24 contains serious regressions and is not "
|
||||
"supported. Please upgrade to Ghostscript 9.25 or use an older "
|
||||
"version."
|
||||
)
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
def run_pipeline(args=None):
|
||||
options = parser.parse_args(args=args)
|
||||
options.verbose_abbreviated_path = 1
|
||||
@@ -1048,24 +1091,7 @@ def run_pipeline(args=None):
|
||||
)
|
||||
preamble(_log)
|
||||
check_options(options, _log)
|
||||
|
||||
# Complain about qpdf version < 7.0.0
|
||||
# Suppress the warning if in the test suite, since there are no PPAs
|
||||
# for qpdf 7.0.0 for Ubuntu trusty (i.e. Travis)
|
||||
if qpdf.version() < '7.0.0' and not os.environ.get('PYTEST_CURRENT_TEST'):
|
||||
complain(
|
||||
f"You are using qpdf version {qpdf.version()} which has known issues including "
|
||||
f"security vulnerabilities with certain malformed PDFs. Consider "
|
||||
f"upgrading to version 7.0.0 or newer."
|
||||
)
|
||||
|
||||
if ghostscript.version() == '9.24':
|
||||
complain(
|
||||
"Ghostscript 9.24 contains serious regressions and is not "
|
||||
"supported. Please upgrade to Ghostscript 9.25 or use an older "
|
||||
"version."
|
||||
)
|
||||
return ExitCode.missing_dependency
|
||||
check_dependency_versions(_log)
|
||||
|
||||
# Any changes to options will not take effect for options that are already
|
||||
# bound to function parameters in the pipeline. (For example
|
||||
|
||||
@@ -22,6 +22,7 @@ import re
|
||||
import sys
|
||||
from subprocess import run, STDOUT, PIPE, CalledProcessError
|
||||
from ..exceptions import MissingDependencyError
|
||||
from collections.abc import Mapping
|
||||
|
||||
|
||||
def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)'):
|
||||
@@ -58,3 +59,102 @@ def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)'):
|
||||
)
|
||||
|
||||
return version
|
||||
|
||||
|
||||
missing_program = '''
|
||||
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.
|
||||
'''
|
||||
|
||||
old_version = '''
|
||||
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.
|
||||
'''
|
||||
|
||||
osx_install_advice = '''
|
||||
If you have homebrew installed, try these command to install the missing
|
||||
packages:
|
||||
brew update
|
||||
brew upgrade
|
||||
brew install {package}
|
||||
'''
|
||||
|
||||
linux_install_advice = '''
|
||||
On systems with the aptitude package manager (Debian, Ubuntu), try these
|
||||
commands:
|
||||
sudo apt-get update
|
||||
sudo apt-get install {package}
|
||||
|
||||
On RPM-based systems (Red Hat, Fedora), search for instructions on
|
||||
installing the RPM for {program}.
|
||||
'''
|
||||
|
||||
|
||||
def get_platform():
|
||||
if sys.platform.startswith('freebsd'):
|
||||
return 'freebsd'
|
||||
elif sys.platform.startswith('linux'):
|
||||
return 'linux'
|
||||
return sys.platform
|
||||
|
||||
|
||||
def _error_trailer(log, program, package, optional, **kwargs):
|
||||
if optional:
|
||||
log.error(okay_its_optional.format(**locals()), file=sys.stderr)
|
||||
else:
|
||||
log.error(not_okay_its_required.format(**locals()), file=sys.stderr)
|
||||
|
||||
if isinstance(package, Mapping):
|
||||
package = package[get_platform()]
|
||||
|
||||
if get_platform() == 'darwin':
|
||||
log.error(osx_install_advice.format(**locals()), file=sys.stderr)
|
||||
elif get_platform() == 'linux':
|
||||
log.error(linux_install_advice.format(**locals()), file=sys.stderr)
|
||||
|
||||
|
||||
def error_missing_program(log, program, package, optional):
|
||||
log.error(missing_program.format(**locals()), file=sys.stderr)
|
||||
_error_trailer(log, **locals())
|
||||
|
||||
|
||||
def error_unknown_version(log, program, package, optional, need_version):
|
||||
log.error(unknown_version.format(**locals()), file=sys.stderr)
|
||||
_error_trailer(log, **locals())
|
||||
|
||||
|
||||
def error_old_version(log, program, package, optional, need_version, found_version):
|
||||
log.error(old_version.format(**locals()), file=sys.stderr)
|
||||
_error_trailer(log, **locals())
|
||||
|
||||
|
||||
def check_external_program(
|
||||
log, program, package, version_checker, need_version, optional=False
|
||||
):
|
||||
try:
|
||||
found_version = version_checker()
|
||||
except (CalledProcessError, FileNotFoundError, MissingDependencyError):
|
||||
error_missing_program(log, program, package, optional)
|
||||
if not optional:
|
||||
sys.exit(1)
|
||||
return
|
||||
|
||||
if found_version < need_version:
|
||||
error_old_version(log, program, package, optional, need_version, found_version)
|
||||
|
||||
log.debug(f'Found {program} {found_version}')
|
||||
|
||||
Reference in New Issue
Block a user