From 9247ea00bf741b4e32493be50c7eaa1317f643ee Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Tue, 11 Aug 2015 02:19:46 -0700 Subject: [PATCH] Improve ruffus exception handling ruffus swallows the return code if the process of handling an exception we hit an error in ruffus' own code, which can happen. So pick through its error stack and find out if there's an interesting return code in there. Had to use eval() of all things. Also suppress the stack trace for normal error conditions that don't need one. --- ocrmypdf/main.py | 34 ++++++++++++++++++++++++++++++---- tests/resources/invalid.pdf | 3 +++ tests/test_main.py | 6 ++++++ 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/resources/invalid.pdf diff --git a/ocrmypdf/main.py b/ocrmypdf/main.py index 3841c5ac..17a058a7 100755 --- a/ocrmypdf/main.py +++ b/ocrmypdf/main.py @@ -17,7 +17,7 @@ import PyPDF2 as pypdf from PIL import Image from subprocess import Popen, check_call, PIPE, CalledProcessError, \ - TimeoutExpired, check_output + TimeoutExpired, check_output, STDOUT try: from subprocess import DEVNULL except ImportError: @@ -26,6 +26,7 @@ except ImportError: from ruffus import transform, suffix, merge, active_if, regex, jobs_limit, \ formatter, follows, split, collate, check_if_uptodate +import ruffus.ruffus_exceptions as ruffus_exceptions import ruffus.cmdline as cmdline from .hocrtransform import HocrTransform @@ -51,7 +52,7 @@ MINIMUM_TESS_VERSION = '3.02.02' def complain(message): - print(textwrap.wrap(message), file=sys.stderr) + print(*textwrap.wrap(message), file=sys.stderr) if tesseract.version() < MINIMUM_TESS_VERSION: @@ -319,7 +320,19 @@ def repair_pdf( args_qpdf = [ 'qpdf', input_file, output_file ] - check_call(args_qpdf) + try: + out = check_output(args_qpdf, stderr=STDOUT, universal_newlines=True) + except CalledProcessError as e: + if e.returncode == 2: + print("{0}: not a valid PDF, and could not repair it.".format( + options.input_file)) + print("Details:") + print(e.output) + else: + print(e.output) + sys.exit(ExitCode.input_file) + + log.debug(out) with pdfinfo_lock: pdfinfo.extend(pdf_get_all_pageinfo(output_file)) @@ -839,7 +852,20 @@ def run_pipeline(): if not options.jobs or options.jobs == 1: options.jobs = available_cpu_count() - cmdline.run(options) + try: + cmdline.run(options) + except ruffus_exceptions.RethrownJobError as e: + if options.verbose: + print(e) + + # Yuck. Hunt through the ruffus exception to find out what the + # return code is supposed to be. + for exc in e.args: + task_name, job_name, exc_name, exc_value, exc_stack = exc + if exc_name == 'builtins.SystemExit': + return eval( + exc_value, + {'ExitCode': ExitCode}, {'exc_value': exc_value}) pdf_is_valid, pdf_is_pdfa = validate_pdfa(options.output_file, _log) diff --git a/tests/resources/invalid.pdf b/tests/resources/invalid.pdf new file mode 100644 index 00000000..d3e7d96a --- /dev/null +++ b/tests/resources/invalid.pdf @@ -0,0 +1,3 @@ +%PDF-1.3 +This is not a valid PDF file +%%EOF diff --git a/tests/test_main.py b/tests/test_main.py index 1f36d263..c3ef42b3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -281,3 +281,9 @@ def test_tesseract_missing_tessdata(): 'graph_ocred.pdf', 'not_a_pdfa.pdf', '-v', '1', '--skip-text', env=env) assert p.returncode == ExitCode.missing_dependency, err + +def test_invalid_input_pdf(): + p, out, err = run_ocrmypdf_env( + 'invalid.pdf', 'wont_be_created.pdf') + assert p.returncode == ExitCode.input_file, err +