Issue #11747: Fix range formatting in context and unified diffs.

This commit is contained in:
Raymond Hettinger 2011-04-11 12:42:59 -07:00
commit 2498c9f06e
3 changed files with 42 additions and 11 deletions

View File

@ -1144,6 +1144,17 @@ def IS_CHARACTER_JUNK(ch, ws=" \t"):
return ch in ws return ch in ws
def _format_range(start, stop):
'Convert range to the "ed" format'
# Per the diff spec at http://www.unix.org/single_unix_specification/
beginning = start + 1 # lines start numbering with one
length = stop - start
if length == 1:
return '{}'.format(beginning)
if not length:
beginning -= 1 # empty ranges begin at line just before the range
return '{},{}'.format(beginning, length)
def unified_diff(a, b, fromfile='', tofile='', fromfiledate='', def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
tofiledate='', n=3, lineterm='\n'): tofiledate='', n=3, lineterm='\n'):
r""" r"""
@ -1193,9 +1204,12 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
todate = '\t{}'.format(tofiledate) if tofiledate else '' todate = '\t{}'.format(tofiledate) if tofiledate else ''
yield '--- {}{}{}'.format(fromfile, fromdate, lineterm) yield '--- {}{}{}'.format(fromfile, fromdate, lineterm)
yield '+++ {}{}{}'.format(tofile, todate, lineterm) yield '+++ {}{}{}'.format(tofile, todate, lineterm)
first, last = group[0], group[-1] first, last = group[0], group[-1]
i1, i2, j1, j2 = first[1], last[2], first[3], last[4] file1_range = _format_range(first[1], last[2])
yield '@@ -{},{} +{},{} @@{}'.format(i1+1, i2-i1, j1+1, j2-j1, lineterm) file2_range = _format_range(first[3], last[4])
yield '@@ -{} +{} @@{}'.format(file1_range, file2_range, lineterm)
for tag, i1, i2, j1, j2 in group: for tag, i1, i2, j1, j2 in group:
if tag == 'equal': if tag == 'equal':
for line in a[i1:i2]: for line in a[i1:i2]:
@ -1264,22 +1278,20 @@ def context_diff(a, b, fromfile='', tofile='',
yield '--- {}{}{}'.format(tofile, todate, lineterm) yield '--- {}{}{}'.format(tofile, todate, lineterm)
first, last = group[0], group[-1] first, last = group[0], group[-1]
yield '***************{}'.format(lineterm) yield '***************' + lineterm
file1_range = _format_range(first[1], last[2])
yield '*** {} ****{}'.format(file1_range, lineterm)
if last[2] - first[1] > 1:
yield '*** {},{} ****{}'.format(first[1]+1, last[2], lineterm)
else:
yield '*** {} ****{}'.format(last[2], lineterm)
if any(tag in {'replace', 'delete'} for tag, _, _, _, _ in group): if any(tag in {'replace', 'delete'} for tag, _, _, _, _ in group):
for tag, i1, i2, _, _ in group: for tag, i1, i2, _, _ in group:
if tag != 'insert': if tag != 'insert':
for line in a[i1:i2]: for line in a[i1:i2]:
yield prefix[tag] + line yield prefix[tag] + line
if last[4] - first[3] > 1: file2_range = _format_range(first[3], last[4])
yield '--- {},{} ----{}'.format(first[3]+1, last[4], lineterm) yield '--- {} ----{}'.format(file2_range, lineterm)
else:
yield '--- {} ----{}'.format(last[4], lineterm)
if any(tag in {'replace', 'insert'} for tag, _, _, _, _ in group): if any(tag in {'replace', 'insert'} for tag, _, _, _, _ in group):
for tag, _, _, j1, j2 in group: for tag, _, _, j1, j2 in group:
if tag != 'delete': if tag != 'delete':

View File

@ -236,6 +236,22 @@ class TestOutputFormat(unittest.TestCase):
cd = difflib.context_diff(*args, lineterm='') cd = difflib.context_diff(*args, lineterm='')
self.assertEqual(list(cd)[0:2], ["*** Original", "--- Current"]) self.assertEqual(list(cd)[0:2], ["*** Original", "--- Current"])
def test_range_format(self):
# Per the diff spec at http://www.unix.org/single_unix_specification/
spec = '''\
Each <range> field shall be of the form:
%1d", <beginning line number> if the range contains exactly one line,
and:
"%1d,%1d", <beginning line number>, <number of lines> otherwise.
If a range is empty, its beginning line number shall be the number of
the line just before the range, or 0 if the empty range starts the file.
'''
fmt = difflib._format_range
self.assertEqual(fmt(3,3), '3,0')
self.assertEqual(fmt(3,4), '4')
self.assertEqual(fmt(3,5), '4,2')
self.assertEqual(fmt(3,6), '4,3')
self.assertEqual(fmt(0,0), '0,0')
def test_main(): def test_main():
difflib.HtmlDiff._default_prefix = 0 difflib.HtmlDiff._default_prefix = 0

View File

@ -109,6 +109,9 @@ Library
- Issue #11814: Fix likely typo in multiprocessing.Pool._terminate(). - Issue #11814: Fix likely typo in multiprocessing.Pool._terminate().
- Issue #11747: Fix range formatting in difflib.context_diff() and
difflib.unified_diff().
- Issue #8428: Fix a race condition in multiprocessing.Pool when terminating - Issue #8428: Fix a race condition in multiprocessing.Pool when terminating
worker processes: new processes would be spawned while the pool is being worker processes: new processes would be spawned while the pool is being
shut down. Patch by Charles-François Natali. shut down. Patch by Charles-François Natali.