Refactor symlink usage to support Windows

This commit is contained in:
James R. Barlow 2019-11-17 15:56:45 -08:00
parent 17c419dfcb
commit 72d3ee3a87
6 changed files with 20 additions and 13 deletions

View File

@ -37,7 +37,7 @@ from .exceptions import (
UnsupportedImageFormatError,
)
from .exec import ghostscript, tesseract
from .helpers import re_symlink
from .helpers import safe_symlink
from .hocrtransform import HocrTransform
from .optimize import optimize
from .pdfa import generate_pdfa_ps
@ -132,7 +132,7 @@ def triage(input_file, output_file, options, log):
"input file is a PDF, not an image."
)
# Origin file is a pdf create a symlink with pdf extension
re_symlink(input_file, output_file)
safe_symlink(input_file, output_file)
return output_file
except EnvironmentError as e:
log.error(e)
@ -701,7 +701,7 @@ def convert_to_pdfa(input_pdf, input_ps_stub, context):
if modified:
pdf_file.save(fix_docinfo_file)
else:
os.symlink(input_pdf, fix_docinfo_file)
safe_symlink(input_pdf, fix_docinfo_file)
ghostscript.generate_pdfa(
pdf_version=input_pdfinfo.min_version,

View File

@ -42,7 +42,7 @@ from .exec import (
tesseract,
unpaper,
)
from .helpers import is_file_writable, is_iterable_notstr, monotonic, re_symlink
from .helpers import is_file_writable, is_iterable_notstr, monotonic, safe_symlink
# -------------
# External dependencies
@ -374,7 +374,7 @@ def create_input_file(options, work_folder):
else:
try:
target = os.path.join(work_folder, 'origin')
re_symlink(options.input_file, target)
safe_symlink(options.input_file, target)
return target
except FileNotFoundError:
raise InputFileError(f"File not found - {options.input_file}")

View File

@ -30,7 +30,7 @@ from ..exceptions import (
SubprocessOutputError,
TesseractConfigError,
)
from ..helpers import page_number
from ..helpers import page_number, safe_symlink
from . import get_version
OrientationConfidence = namedtuple('OrientationConfidence', ('angle', 'confidence'))
@ -324,7 +324,7 @@ def use_skip_page(text_only, skip_pdf, output_pdf, output_text):
# Substitute a "skipped page"
with suppress(FileNotFoundError):
os.remove(output_pdf) # In case it was partially created
os.symlink(skip_pdf, output_pdf)
safe_symlink(skip_pdf, output_pdf)
return
# Or normally, just write a 0 byte file to the output to indicate a skip

View File

@ -18,6 +18,7 @@
import logging
import multiprocessing
import os
import shutil
import warnings
from collections.abc import Iterable
from contextlib import suppress
@ -27,14 +28,14 @@ from pathlib import Path
log = logging.getLogger(__name__)
def re_symlink(input_file, soft_link_name, *args, **kwargs):
def safe_symlink(input_file, soft_link_name, *args, **kwargs):
"""
Helper function: relinks soft symbolic link if necessary
"""
if len(args) == 1 and isinstance(args[0], logging.Logger):
log.warning("Deprecated: re_symlink(,log)")
log.warning("Deprecated: safe_symlink(,log)")
if 'log' in kwargs:
log.warning('Deprecated: re_symlink(...log=)')
log.warning('Deprecated: safe_symlink(...log=)')
input_file = os.fspath(input_file)
soft_link_name = os.fspath(soft_link_name)
@ -60,6 +61,11 @@ def re_symlink(input_file, soft_link_name, *args, **kwargs):
if not os.path.exists(input_file):
raise FileNotFoundError(f"trying to create a broken symlink to {input_file}")
if os.name == 'nt':
# Don't actually use symlinks on Windows due to permission issues
shutil.copyfile(input_file, soft_link_name)
return
log.debug("os.symlink(%s, %s)", input_file, soft_link_name)
# Create symbolic link using absolute path

View File

@ -31,7 +31,7 @@ from . import leptonica
from ._jobcontext import PDFContext
from .exceptions import OutputFileAccessError
from .exec import jbig2enc, pngquant
from .helpers import re_symlink
from .helpers import safe_symlink
DEFAULT_JPEG_QUALITY = 75
DEFAULT_PNG_QUALITY = 70
@ -492,7 +492,7 @@ def optimize(input_file, output_file, context, save_settings):
log = context.log
options = context.options
if options.optimize == 0:
re_symlink(input_file, output_file)
safe_symlink(input_file, output_file)
return
if options.jpeg_quality == 0:
@ -538,7 +538,7 @@ def optimize(input_file, output_file, context, save_settings):
pike.remove_unreferenced_resources()
pike.save(output_file, **save_settings)
else:
re_symlink(target_file, output_file)
safe_symlink(target_file, output_file)
def main(infile, outfile, level, jobs=1):

View File

@ -946,6 +946,7 @@ def test_output_is_dir(spoof_tesseract_noop, resources, outdir):
assert 'is not a writable file' in err
@pytest.mark.skipif(os.name == 'nt', reason="symlink needs admin permissions")
def test_output_is_symlink(spoof_tesseract_noop, resources, outdir):
sym = Path(outdir / 'this_is_a_symlink')
sym.symlink_to(outdir / 'out.pdf')