Ensure specified destination is writable before starting pipeline process
This commit is contained in:
@@ -26,7 +26,7 @@ import ruffus.proxy_logger as proxy_logger
|
||||
from .pipeline import JobContext, JobContextManager, re_symlink, \
|
||||
cleanup_working_files, build_pipeline
|
||||
from .pdfa import file_claims_pdfa
|
||||
from .helpers import is_iterable_notstr, re_symlink
|
||||
from .helpers import is_iterable_notstr, re_symlink, is_file_writable
|
||||
from .exec import tesseract, qpdf
|
||||
from . import PROGRAM_NAME, VERSION
|
||||
|
||||
@@ -518,6 +518,10 @@ def run_pipeline():
|
||||
is connected to a terminal. Please redirect stdout to a
|
||||
file."""))
|
||||
return ExitCode.bad_args
|
||||
elif not is_file_writable(options.output_file):
|
||||
_log.error(textwrap.dedent("""\
|
||||
Cutput file location is not writable."""))
|
||||
return ExitCode.file_access_error
|
||||
|
||||
manager = JobContextManager()
|
||||
manager.register('JobContext', JobContext)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from functools import partial
|
||||
from collections.abc import Iterable
|
||||
from contextlib import suppress
|
||||
import sys
|
||||
import os
|
||||
|
||||
@@ -53,3 +54,26 @@ def is_iterable_notstr(thing):
|
||||
|
||||
def page_number(input_file):
|
||||
return int(os.path.basename(input_file)[0:6])
|
||||
|
||||
|
||||
def is_file_writable(test_file):
|
||||
"""Intentionally racy test if target is writable.
|
||||
|
||||
We intend to write to the output file if and only if we succeed and
|
||||
can replace it atomically. Before doing the OCR work, make sure
|
||||
the location is writable.
|
||||
"""
|
||||
if os.path.exists(test_file):
|
||||
return os.access(
|
||||
test_file, os.W_OK,
|
||||
effective_ids=(os.access in os.supports_effective_ids))
|
||||
else:
|
||||
try:
|
||||
fp = open(test_file, 'wb')
|
||||
except OSError as e:
|
||||
return False
|
||||
else:
|
||||
fp.close()
|
||||
with suppress(OSError):
|
||||
os.unlink(test_file)
|
||||
return True
|
||||
|
||||
@@ -680,3 +680,13 @@ def test_very_high_dpi(spoof_tesseract_cache, resources, outpdf):
|
||||
def test_overlay(spoof_tesseract_noop, resources, outpdf):
|
||||
check_ocrmypdf(resources / 'overlay.pdf', outpdf,
|
||||
env=spoof_tesseract_noop)
|
||||
|
||||
|
||||
def test_destination_not_writable(spoof_tesseract_noop, resources, outdir):
|
||||
protected_file = outdir / 'protected.pdf'
|
||||
protected_file.touch()
|
||||
protected_file.chmod(0o400) # Read-only
|
||||
p, out, err = run_ocrmypdf(
|
||||
resources / 'jbig2.pdf', protected_file,
|
||||
env=spoof_tesseract_noop)
|
||||
assert p.returncode == ExitCode.file_access_error, "Expected error"
|
||||
|
||||
+1
-8
@@ -15,20 +15,13 @@ from ocrmypdf.pdfa import file_claims_pdfa
|
||||
from ocrmypdf.exec import tesseract
|
||||
|
||||
|
||||
TESTS_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||
SPOOF_PATH = os.path.join(TESTS_ROOT, 'spoof')
|
||||
PROJECT_ROOT = os.path.dirname(TESTS_ROOT)
|
||||
TEST_RESOURCES = os.path.join(PROJECT_ROOT, 'tests', 'resources')
|
||||
OCRMYPDF = [sys.executable, '-m', 'ocrmypdf']
|
||||
|
||||
|
||||
# Skip all tests in this file if not tesseract 4
|
||||
pytestmark = pytest.mark.skipif(not tesseract.v4(),
|
||||
reason="tesseract 4.0 required")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not tesseract.has_textonly_pdf(),
|
||||
reason="requires textonly_pdf parameter")
|
||||
reason="requires textonly_pdf feature")
|
||||
def test_textonly_pdf(resources, outdir):
|
||||
pytest.helpers.check_ocrmypdf(
|
||||
resources / 'linn.pdf',
|
||||
|
||||
Reference in New Issue
Block a user