Skip to content

Commit af95e62

Browse files
author
techtonik
committed
patch.py now returns -1 on failure
Patch.apply() returns True on success * Suggested by: Wladimir J. van der Laan (laanwj) Update issue 14 Now it is possible to detect if patching of any file is failed.
1 parent 1ef1520 commit af95e62

4 files changed

Lines changed: 67 additions & 11 deletions

File tree

patch.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"""
1414

1515
__author__ = "techtonik.rainforce.org"
16-
__version__ = "10.11"
16+
__version__ = "10.12"
1717

1818
import copy
1919
import logging
@@ -403,19 +403,24 @@ def lineno(self):
403403

404404

405405
def apply(self):
406-
""" apply parsed patch """
406+
""" apply parsed patch
407+
return True on success
408+
"""
407409

408410
total = len(self.source)
411+
errors = 0
409412
for fileno, filename in enumerate(self.source):
410413

411414
f2patch = filename
412415
if not exists(f2patch):
413416
f2patch = self.target[fileno]
414417
if not exists(f2patch):
415418
warning("source/target file does not exist\n--- %s\n+++ %s" % (filename, f2patch))
419+
errors += 1
416420
continue
417421
if not isfile(f2patch):
418422
warning("not a file - %s" % f2patch)
423+
errors += 1
419424
continue
420425
filename = f2patch
421426

@@ -448,7 +453,14 @@ def apply(self):
448453
info(" hunk no.%d doesn't match source file at line %d" % (hunkno+1, lineno))
449454
info(" expected: %s" % hunkfind[hunklineno])
450455
info(" actual : %s" % line.rstrip("\r\n"))
451-
# file may be already patched, but we will check other hunks anyway
456+
# not counting this as error, because file may already be patched.
457+
# check if file is already patched is done after the number of
458+
# invalid hunks if found
459+
# TODO: check hunks against source/target file in one pass
460+
# API - check(stream, srchunks, tgthunks)
461+
# return tuple (srcerrs, tgterrs)
462+
463+
# continue to check other hunks for completeness
452464
hunkno += 1
453465
if hunkno < len(self.hunks[fileno]):
454466
hunk = self.hunks[fileno][hunkno]
@@ -471,6 +483,7 @@ def apply(self):
471483
else:
472484
if hunkno < len(self.hunks[fileno]):
473485
warning("premature end of source file %s at hunk %d" % (filename, hunkno+1))
486+
errors += 1
474487

475488
f2fp.close()
476489

@@ -479,6 +492,7 @@ def apply(self):
479492
warning("already patched %s" % filename)
480493
else:
481494
warning("source file is different - %s" % filename)
495+
errors += 1
482496
if canpatch:
483497
backupname = filename+".orig"
484498
if exists(backupname):
@@ -490,13 +504,15 @@ def apply(self):
490504
info("successfully patched %d/%d:\t %s" % (fileno+1, total, filename))
491505
unlink(backupname)
492506
else:
507+
errors += 1
493508
warning("error patching file %s" % filename)
494509
shutil.copy(filename, filename+".invalid")
495510
warning("invalid version is saved to %s" % filename+".invalid")
496511
# todo: proper rejects
497512
shutil.move(backupname, filename)
498513

499514
# todo: check for premature eof
515+
return (errors == 0)
500516

501517

502518
def can_patch(self, filename):
@@ -682,7 +698,7 @@ def _get_file_idx(self, filename, source=None):
682698

683699
patch = fromfile(patchfile)
684700
#pprint(patch)
685-
patch.apply()
701+
patch.apply() or sys.exit(-1)
686702

687703
# todo: document and test line ends handling logic - patch.py detects proper line-endings
688704
# for inserted hunks and issues a warning if patched file has incosistent line ends

tests/data/failing/reporting.patch

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Index: upload.py
2+
===================================================================
3+
--- upload.py (revision 623)
4+
+++ upload.py (working copy)
5+
@@ -393,6 +393,9 @@
6+
## elif e.code >= 500 and e.code < 600:
7+
## # Server Error - try again.
8+
## continue
9+
+ elif e.code == 404:
10+
+ ErrorExit("Error: RPC call to %s failed with status 404\n"
11+
+ "Check upload server is valid - %s" % (request_path, self.host))
12+
else:
13+
raise
14+
finally:

tests/data/failing/upload.py

Whitespace-only changes.

tests/run_tests.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,30 +176,30 @@ def tearDown(self):
176176
os.chdir(self.save_cwd)
177177

178178
def test_patched_multiline(self):
179-
pto = patch.fromfile(join(tests_dir, "01uni_multi.patch"))
179+
pto = patch.fromfile("01uni_multi.patch")
180180
os.chdir(join(tests_dir, "01uni_multi.to"))
181181
self.assert_(pto.can_patch("updatedlg.cpp"))
182182

183183
def test_can_patch_single_source(self):
184-
pto2 = patch.fromfile(join(tests_dir, "02uni_newline.patch"))
184+
pto2 = patch.fromfile("02uni_newline.patch")
185185
self.assert_(pto2.can_patch("02uni_newline.from"))
186186

187187
def test_can_patch_fails_on_target_file(self):
188-
pto3 = patch.fromfile(join(tests_dir, "03trail_fname.patch"))
188+
pto3 = patch.fromfile("03trail_fname.patch")
189189
self.assertEqual(None, pto3.can_patch("03trail_fname.to"))
190190
self.assertEqual(None, pto3.can_patch("not_in_source.also"))
191191

192192
def test_multiline_false_on_other_file(self):
193-
pto = patch.fromfile(join(tests_dir, "01uni_multi.patch"))
193+
pto = patch.fromfile("01uni_multi.patch")
194194
os.chdir(join(tests_dir, "01uni_multi.from"))
195195
self.assertFalse(pto.can_patch("updatedlg.cpp"))
196196

197197
def test_single_false_on_other_file(self):
198-
pto3 = patch.fromfile(join(tests_dir, "03trail_fname.patch"))
198+
pto3 = patch.fromfile("03trail_fname.patch")
199199
self.assertFalse(pto3.can_patch("03trail_fname.from"))
200200

201201
def test_can_patch_checks_source_filename_even_if_target_can_be_patched(self):
202-
pto2 = patch.fromfile(join(tests_dir, "04can_patch.patch"))
202+
pto2 = patch.fromfile("04can_patch.patch")
203203
self.assertFalse(pto2.can_patch("04can_patch.to"))
204204

205205
# ----------------------------------------------------------------------------
@@ -222,8 +222,34 @@ def test_header_for_second_file_in_svn_diff(self):
222222
pto = patch.fromfile(join(tests_dir, "01uni_multi.patch"))
223223
self.assertEqual(pto.header[1][:25], 'Index: updatedlg.h\r\n=====')
224224

225-
# ----------------------------------------------------------------------------
225+
class TestPatchApply(unittest.TestCase):
226+
def setUp(self):
227+
self.save_cwd = os.getcwdu()
228+
self.tmpdir = mkdtemp(prefix=self.__class__.__name__)
229+
os.chdir(self.tmpdir)
230+
231+
def tearDown(self):
232+
os.chdir(self.save_cwd)
233+
shutil.rmtree(self.tmpdir)
234+
235+
def tmpcopy(self, filenames):
236+
"""copy file(s) from test_dir to self.tmpdir"""
237+
for f in filenames:
238+
shutil.copy(join(tests_dir, f), self.tmpdir)
226239

240+
def test_apply_returns_false_of_failure(self):
241+
self.tmpcopy(['data/failing/reporting.patch',
242+
'data/failing/upload.py'])
243+
pto = patch.fromfile('reporting.patch')
244+
self.assertFalse(pto.apply())
245+
246+
def test_apply_returns_true_on_success(self):
247+
self.tmpcopy(['03trail_fname.patch',
248+
'03trail_fname.from'])
249+
pto = patch.fromfile('03trail_fname.patch')
250+
self.assert_(pto.apply())
251+
252+
# ----------------------------------------------------------------------------
227253

228254
if __name__ == '__main__':
229255
unittest.main()

0 commit comments

Comments
 (0)