Use _OCRMYPDF_TEST_PATH for testing and .py stubs to simulate symlinks

This commit is contained in:
James R. Barlow
2019-12-04 21:01:06 -08:00
parent e51e21c6b6
commit 06a1f987d4
14 changed files with 70 additions and 29 deletions
+1
View File
@@ -18,6 +18,7 @@
import logging
import os
import sys
import warnings
from enum import IntEnum
from pathlib import Path
+22 -1
View File
@@ -21,14 +21,35 @@ import logging
import os
import re
import sys
import shutil
from collections.abc import Mapping
from subprocess import PIPE, STDOUT, CalledProcessError, run
from subprocess import PIPE, STDOUT, CalledProcessError, run as subprocess_run
from ..exceptions import ExitCode, MissingDependencyError
log = logging.Logger(__name__)
def _get_program(args, env=None):
program = args[0]
test_path = env.get('_OCRMYPDF_TEST_PATH', '')
if test_path:
program = shutil.which(program, path=test_path)
return program
def run(args, *, env=None, **kwargs):
if not env:
env = os.environ
program = _get_program(args, env)
if os.name == 'nt' and program.lower().endswith('.py'):
args = [sys.executable, program] + args[1:]
else:
args = [program] + args[1:]
log.debug(args)
return subprocess_run(args, env=env, **kwargs)
def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)', env=None):
"Get the version of the specified program"
args_prog = [program, version_arg]
+3 -2
View File
@@ -26,13 +26,13 @@ from functools import lru_cache
from io import BytesIO
from os import fspath
from pathlib import Path
from subprocess import PIPE, run, CalledProcessError
from subprocess import PIPE, CalledProcessError
from shutil import which
from PIL import Image
from ..exceptions import SubprocessOutputError, MissingDependencyError
from . import get_version
from . import get_version, run
gslog = logging.getLogger()
@@ -43,6 +43,7 @@ if os.name == 'nt':
GS = which('gswin32c')
if not GS:
raise MissingDependencyError("Ghostscript (gswin64c or gswin32c)")
GS = Path(GS).stem
@lru_cache(maxsize=1)
+2 -2
View File
@@ -18,10 +18,10 @@
"""Interface to jbig2 executable"""
from functools import lru_cache
from subprocess import PIPE, run
from subprocess import PIPE
from ..exceptions import MissingDependencyError
from . import get_version
from . import get_version, run
@lru_cache(maxsize=1)
+2 -2
View File
@@ -19,9 +19,9 @@
from functools import lru_cache
from os import fspath
from subprocess import PIPE, STDOUT, CalledProcessError, run
from subprocess import PIPE, STDOUT, CalledProcessError
from . import get_version
from . import get_version, run
@lru_cache(maxsize=1)
+2 -2
View File
@@ -23,7 +23,7 @@ from collections import namedtuple
from contextlib import suppress
import logging
from os import fspath
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired, run
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired
from ..exceptions import (
MissingDependencyError,
@@ -31,7 +31,7 @@ from ..exceptions import (
TesseractConfigError,
)
from ..helpers import page_number, safe_symlink
from . import get_version
from . import get_version, run
OrientationConfidence = namedtuple('OrientationConfidence', ('angle', 'confidence'))
+2 -3
View File
@@ -22,7 +22,6 @@
import os
import shlex
import subprocess
from functools import lru_cache
from subprocess import PIPE, STDOUT, CalledProcessError
from tempfile import TemporaryDirectory
@@ -30,7 +29,7 @@ from tempfile import TemporaryDirectory
from PIL import Image
from ..exceptions import MissingDependencyError, SubprocessOutputError
from . import get_version
from . import get_version, run as external_run
@lru_cache(maxsize=1)
@@ -77,7 +76,7 @@ def run(input_file, output_file, dpi, log, mode_args):
# their unpaper arguments (whether intentionally or otherwise)
args_unpaper.extend([input_pnm, output_pnm])
try:
proc = subprocess.run(
proc = external_run(
args_unpaper,
check=True,
close_fds=True,
+31 -6
View File
@@ -80,6 +80,19 @@ PROJECT_ROOT = os.path.dirname(TESTS_ROOT)
OCRMYPDF = [sys.executable, '-m', 'ocrmypdf']
PY_FILE_TEMPLATE = """
import os
import subprocess
import sys
args = [sys.executable, {spoofer}, *sys.argv[1:]]
p = subprocess.run(args, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
sys.stdout.buffer.write(p.stdout)
sys.stderr.buffer.write(p.stderr)
sys.exit(p.returncode)
"""
@pytest.helpers.register
def spoof(tmp_path_factory, **kwargs):
"""Modify PATH to override subprocess executables
@@ -97,12 +110,24 @@ def spoof(tmp_path_factory, **kwargs):
for replace_program, with_spoof in kwargs.items():
spoofer = Path(SPOOF_PATH) / with_spoof
spoofer.chmod(0o755)
(tmpdir / replace_program).symlink_to(spoofer)
env['_OCRMYPDF_SAVE_PATH'] = env['PATH']
env['PATH'] = str(tmpdir) + ":" + env['PATH']
if os.name != 'nt':
spoofer.chmod(0o755)
(tmpdir / replace_program).symlink_to(spoofer)
else:
py_file = PY_FILE_TEMPLATE.format(
python=sys.executable, spoofer=repr(os.fspath(spoofer.absolute()))
)
if replace_program == 'gs':
programs = ['gswin64c', 'gswin32c']
else:
programs = [replace_program]
for prog in programs:
(tmpdir / f'{prog}.py').write_text(py_file, encoding='utf-8')
env['_OCRMYPDF_TEST_PATH'] = str(tmpdir) + os.pathsep + env['PATH']
if os.name == 'nt':
if '.py' not in env['PATHEXT'].lower():
raise EnvironmentError("PATHEXT is not configured to support .py")
return env
@@ -178,7 +203,7 @@ def run_ocrmypdf_api(input_file, output_file, *args, env=None):
)
api.check_options(options)
if env:
options.tesseract_env = env
options.tesseract_env = env.copy()
options.tesseract_env['_OCRMYPDF_TEST_INFILE'] = os.fspath(input_file)
if options.tesseract_env:
assert all(isinstance(v, (str, bytes)) for v in options.tesseract_env.values())
-1
View File
@@ -38,7 +38,6 @@ not permitted in PDF/A-2, overprint mode not set"""
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
-1
View File
@@ -31,7 +31,6 @@ from gs import real_ghostscript
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
-1
View File
@@ -31,7 +31,6 @@ from gs import real_ghostscript
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
-1
View File
@@ -30,7 +30,6 @@ from gs import real_ghostscript
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
-2
View File
@@ -59,8 +59,6 @@ import subprocess
import sys
from pathlib import Path
if '_OCRMYPDF_SAVE_PATH' in os.environ:
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
__version__ = subprocess.check_output(
['tesseract', '--version'], stderr=subprocess.STDOUT
+5 -5
View File
@@ -297,10 +297,10 @@ def test_force_ocr_on_pdf_with_no_images(spoof_tesseract_crash, resources, no_ou
# As a correctness test, make sure that --force-ocr on a PDF with no
# content still triggers tesseract. If tesseract crashes, then it was
# called.
result = run_ocrmypdf_api(
p, _, _ = run_ocrmypdf(
resources / 'blank.pdf', no_outpdf, '--force-ocr', env=spoof_tesseract_crash
)
assert result == ExitCode.child_process_error
assert p.returncode == ExitCode.child_process_error
assert not os.path.exists(no_outpdf)
@@ -389,7 +389,7 @@ def test_pagesegmode(renderer, spoof_tesseract_cache, resources, outpdf):
@pytest.mark.parametrize('renderer', RENDERERS)
def test_tesseract_crash(renderer, spoof_tesseract_crash, resources, no_outpdf, caplog):
result = run_ocrmypdf_api(
p, _, err = run_ocrmypdf(
resources / 'ccitt.pdf',
no_outpdf,
'-v',
@@ -398,9 +398,9 @@ def test_tesseract_crash(renderer, spoof_tesseract_crash, resources, no_outpdf,
renderer,
env=spoof_tesseract_crash,
)
assert result == ExitCode.child_process_error
assert p.returncode == ExitCode.child_process_error
assert not os.path.exists(no_outpdf)
assert "SubprocessOutputError" in caplog.text
assert "SubprocessOutputError" in err
def test_tesseract_crash_autorotate(spoof_tesseract_crash, resources, no_outpdf):