ar08's picture
Upload 1040 files
246d201 verified
# -*- coding: utf-8 -*-
import os.path
import subprocess
import tempfile
from .exceptions import HunkApplyException, SubprocessException
from .snippets import remove, which
def _apply_diff_with_subprocess(diff, lines, reverse=False):
# call out to patch program
patchexec = which('patch')
if not patchexec:
raise SubprocessException('cannot find patch program', code=-1)
tempdir = tempfile.gettempdir()
filepath = os.path.join(tempdir, 'wtp-' + str(hash(diff.header)))
oldfilepath = filepath + '.old'
newfilepath = filepath + '.new'
rejfilepath = filepath + '.rej'
patchfilepath = filepath + '.patch'
with open(oldfilepath, 'w') as f:
f.write('\n'.join(lines) + '\n')
with open(patchfilepath, 'w') as f:
f.write(diff.text)
args = [
patchexec,
'--reverse' if reverse else '--forward',
'--quiet',
'--no-backup-if-mismatch',
'-o',
newfilepath,
'-i',
patchfilepath,
'-r',
rejfilepath,
oldfilepath,
]
ret = subprocess.call(args)
with open(newfilepath) as f:
lines = f.read().splitlines()
try:
with open(rejfilepath) as f:
rejlines = f.read().splitlines()
except IOError:
rejlines = None
remove(oldfilepath)
remove(newfilepath)
remove(rejfilepath)
remove(patchfilepath)
# do this last to ensure files get cleaned up
if ret != 0:
raise SubprocessException('patch program failed', code=ret)
return lines, rejlines
def _reverse(changes):
def _reverse_change(c):
return c._replace(old=c.new, new=c.old)
return [_reverse_change(c) for c in changes]
def apply_diff(diff, text, reverse=False, use_patch=False):
try:
lines = text.splitlines()
except AttributeError:
lines = list(text)
if use_patch:
return _apply_diff_with_subprocess(diff, lines, reverse)
n_lines = len(lines)
changes = _reverse(diff.changes) if reverse else diff.changes
# check that the source text matches the context of the diff
for old, new, line, hunk in changes:
# might have to check for line is None here for ed scripts
if old is not None and line is not None:
if old > n_lines:
raise HunkApplyException(
'context line {n}, "{line}" does not exist in source'.format(
n=old, line=line
),
hunk=hunk,
)
if lines[old - 1] != line:
raise HunkApplyException(
'context line {n}, "{line}" does not match "{sl}"'.format(
n=old, line=line, sl=lines[old - 1]
),
hunk=hunk,
)
# for calculating the old line
r = 0
i = 0
for old, new, line, hunk in changes:
if old is not None and new is None:
del lines[old - 1 - r + i]
r += 1
elif old is None and new is not None:
lines.insert(new - 1, line)
i += 1
elif old is not None and new is not None:
# Sometimes, people remove hunks from patches, making these
# numbers completely unreliable. Because they're jerks.
pass
return lines