..\latest\difflib.py
..\patched\difflib.py
2 f 2
3""" 3"""
4Module�difflib�--�helpers�for�computing�deltas�between�objects. 4Module�difflib�--�helpers�for�computing�deltas�between�objects.
5 5
6Function�get_close_matches(word,�possibilities,�n=3,�cutoff=0.6): 6Function�get_close_matches(word,�possibilities,�n=3,�cutoff=0.6):
7����Use�SequenceMatcher�to�return�list�of�the�best�"good�enough"�matches. 7����Use�SequenceMatcher�to�return�list�of�the�best�"good�enough"�matches.
8 8
9Function�context_diff(a,�b): 9Function�context_diff(a,�b):
10����For�two�lists�of�strings,�return�a�delta�in�context�diff�format. 10����For�two�lists�of�strings,�return�a�delta�in�context�diff�format.
11 11
n 12Function�mdiff(fromlines,�tolines,�chgfmt,�linefmt,�context=None,�sep=None,�
13���������������linejunk=None,�charjunk=IS_CHARACTER_JUNK):
14����Returns�generator�yielding�marked�up�from/to�side�by�side�difference�lines.
15�
12Function�ndiff(a,�b): 16Function�ndiff(a,�b):
13����Return�a�delta:�the�difference�between�`a`�and�`b`�(lists�of�strings). 17����Return�a�delta:�the�difference�between�`a`�and�`b`�(lists�of�strings).
14 18
15Function�restore(delta,�which): 19Function�restore(delta,�which):
16����Return�one�of�the�two�sequences�that�generated�an�ndiff�delta. 20����Return�one�of�the�two�sequences�that�generated�an�ndiff�delta.
17 21
18Function�unified_diff(a,�b): 22Function�unified_diff(a,�b):
19����For�two�lists�of�strings,�return�a�delta�in�unified�diff�format. 23����For�two�lists�of�strings,�return�a�delta�in�unified�diff�format.
20 24
21Class�SequenceMatcher: 25Class�SequenceMatcher:
22����A�flexible�class�for�comparing�pairs�of�sequences�of�any�type. 26����A�flexible�class�for�comparing�pairs�of�sequences�of�any�type.
23 27
24Class�Differ: 28Class�Differ:
25����For�producing�human-readable�deltas�from�sequences�of�lines�of�text. 29����For�producing�human-readable�deltas�from�sequences�of�lines�of�text.
n 30�
31Class�HtmlDiff:
32����For�producing�HTML�side�by�side�comparison�with�change�highlights.
26""" 33"""
27 34
28__all__�=�['get_close_matches',�'ndiff',�'restore',�'SequenceMatcher', 35__all__�=�['get_close_matches',�'ndiff',�'restore',�'SequenceMatcher',
29�����������'Differ','IS_CHARACTER_JUNK',�'IS_LINE_JUNK',�'context_diff', 36�����������'Differ','IS_CHARACTER_JUNK',�'IS_LINE_JUNK',�'context_diff',
30�����������'unified_diff'] n 37�����������'unified_diff',�'mdiff',�'HtmlDiff']
31 38
32import�heapq 39import�heapq
33 40
34def�_calculate_ratio(matches,�length): 41def�_calculate_ratio(matches,�length):
35����if�length: 42����if�length:
36��������return�2.0�*�matches�/�length 43��������return�2.0�*�matches�/�length
37����return�1.0 44����return�1.0
38 45
39class�SequenceMatcher: 46class�SequenceMatcher:
40 47
41����""" 48����"""
1094����>>>�IS_CHARACTER_JUNK('\t') 1101����>>>�IS_CHARACTER_JUNK('\t')
1095����True 1102����True
1096����>>>�IS_CHARACTER_JUNK('\n') 1103����>>>�IS_CHARACTER_JUNK('\n')
1097����False 1104����False
1098����>>>�IS_CHARACTER_JUNK('x') 1105����>>>�IS_CHARACTER_JUNK('x')
1099����False 1106����False
1100����""" 1107����"""
1101 1108
1102����return�ch�in�ws 1109����return�ch�in�ws
1103 1110
1104del�re n
1105�
1106 1111
1107def�unified_diff(a,�b,�fromfile='',�tofile='',�fromfiledate='', 1112def�unified_diff(a,�b,�fromfile='',�tofile='',�fromfiledate='',
1108�����������������tofiledate='',�n=3,�lineterm='\n'): 1113�����������������tofiledate='',�n=3,�lineterm='\n'):
1109����r""" 1114����r"""
1110����Compare�two�sequences�of�lines;�generate�the�delta�as�a�unified�diff. 1115����Compare�two�sequences�of�lines;�generate�the�delta�as�a�unified�diff.
1111 1116
1112����Unified�diffs�are�a�compact�way�of�showing�line�changes�and�a�few 1117����Unified�diffs�are�a�compact�way�of�showing�line�changes�and�a�few
1113����lines�of�context.��The�number�of�context�lines�is�set�by�'n'�which 1118����lines�of�context.��The�number�of�context�lines�is�set�by�'n'�which
1114����defaults�to�three. 1119����defaults�to�three.
1115 1120
1116����By�default,�the�diff�control�lines�(those�with�---,�+++,�or�@@)�are 1121����By�default,�the�diff�control�lines�(those�with�---,�+++,�or�@@)�are
1269����?��^ 1274����?��^
1270����+�ore 1275����+�ore
1271����?��^ 1276����?��^
1272����-�two 1277����-�two
1273����-�three 1278����-�three
1274����?��- 1279����?��-
1275����+�tree 1280����+�tree
1276����+�emu 1281����+�emu
1277����""" 1282����"""
1278����return�Differ(linejunk,�charjunk).compare(a,�b) 1283����return�Differ(linejunk,�charjunk).compare(a,�b)
t 1284�
1285def�mdiff(fromlines,�tolines,�chgfmt,�linefmt,�context=None,�sep=None,�
1286����������linejunk=None,�charjunk=IS_CHARACTER_JUNK):
1287����"""Returns�generator�yielding�marked�up�from/to�side�by�side�difference�lines.
1288�
1289����Arguments:
1290����fromlines�--�text�lines�which�will�be�iterated�over�and�compared�to�tolines
1291����tolines�--�text�lines�which�will�be�iterated�over�and�compared�to�fromlines
1292����chgfmt�--�function�to�markup�add/delete/change�differences�in�text�lines
1293��������������(see�example�below)
1294����linefmt�--�function�to�format�line�of�text�for�display�(see�example�below)
1295����context�--�number�of�context�lines�to�display�on�each�side�of�difference,
1296���������������if�None�or�less�that�1,�the�all�from/to�text�lines�will�be
1297���������������generated.
1298����sep�--�separator�string�to�use�between�context�differences.
1299����linejunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
1300����charjunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
1301����
1302����This�function�returns�an�interator�which�returns�a�tuple�of�a�"from"
1303����line,�a�corresponding�"to"�line�and�a�boolean�indicating�if�either�the
1304����"from"�or�"to"�line�contains�a�difference.
1305�
1306����This�function/iterator�was�originally�developed�to�generate�side�by�side
1307����file�difference�for�making�HTML�pages.��The�function�requires�functions�to
1308����be�passed�in�as�arguments�to�allow�it�to�be�configurable�to�generate�any
1309����type�of�markup�such�as�HTML�or�XHTML.��The�function�supports�generating�a
1310����full�file�difference�report�or�just�contextual�differences.
1311�
1312����See�HtmlDiff�class�for�an�example�usage�of�this�function.��Note,�this
1313����function�utilizes�the�ndiff�function�to�generate�the�side�by�side
1314����difference�markup.��Optional�ndiff�arguments�may�be�passed�to�this�function
1315����and�they�in�turn�will�be�passed�to�ndiff.����
1316����"""
1317����import�re�
1318�
1319����#�adjust�number�of�context�lines�to�include�the�line�with�the�change
1320����if�context:
1321��������context�+=�1
1322�
1323����#�regular�expression�for�finding�intraline�change�indices
1324����change_re�=�re.compile('(\++|\-+|\^+)')
1325����
1326����#�regular�expression�to�find�hidden�markers
1327����marker_re�=�re.compile('\0([+-^])(.*?)\1',re.DOTALL)
1328�
1329����#�create�the�difference�iterator�to�generate�the�differences
1330����diff_lines_iterator�=�ndiff(fromlines,tolines,linejunk,charjunk)
1331�
1332����def�_make_line(lines,�format_key,�side,�num_lines=[0,0]):
1333��������"""Returns�line�of�text�with�user's�change�markup�and�line�formatting.
1334�
1335��������lines�--�list�of�lines�from�the�ndiff�generator�to�produce�a�line�of
1336�����������������text�from.��When�producing�the�line�of�text�to�return,�the
1337�����������������lines�used�are�removed�from�this�list.
1338��������format_key�--�'+'�return�first�line�in�list�with�"add"�markup�around
1339�������������������������the�entire�line.
1340���������������������'-'�return�first�line�in�list�with�"delete"�markup�around
1341�������������������������the�entire�line.
1342���������������������'?'�return�first�line�in�list�with�add/delete/change
1343�������������������������intraline�markup�(indices�obtained�from�second�line)
1344���������������������None�return�first�line�in�list�with�no�markup
1345��������side�--�indice�into�the�num_lines�list�(0=from,1=to)
1346��������num_lines�--�from/to�current�line�number.��This�is�NOT�intended�to�be�a
1347���������������������passed�parameter.��It�is�present�as�a�keyword�argument�to
1348���������������������maintain�memory�of�the�current�line�numbers�between�calls
1349���������������������of�this�function.
1350�
1351��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
1352��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
1353��������is�defined)�does�not�need�to�be�of�module�scope.
1354��������"""
1355��������num_lines[side]�+=�1
1356��������#�Handle�case�where�no�user�markup�is�to�be�added,�just�return�line�of
1357��������#�text�with�user's�line�format�to�allow�for�usage�of�the�line�number.
1358��������if�format_key�is�None:
1359������������return�linefmt(side,num_lines[side],lines.pop(0)[2:])
1360��������#�Handle�case�of�intraline�changes
1361��������if�format_key�==�'?':
1362������������text,�markers�=�lines.pop(0),�lines.pop(0)
1363������������#�find�intraline�changes�(store�change�type�and�indices�in�tuples)
1364������������sub_info�=�[]
1365������������def�record_sub_info(match_object,sub_info=sub_info):
1366����������������sub_info.append([match_object.group(1)[0],match_object.span()])
1367����������������return�match_object.group(1)
1368������������change_re.sub(record_sub_info,markers)
1369������������#�process�each�tuple�inserting�our�special�marks�that�won't�be
1370������������#�noticed�by�an�xml/html�escaper.
1371������������for�key,(begin,end)�in�sub_info[::-1]:
1372����������������text�=�text[0:begin]+'\0'+key+text[begin:end]+'\1'+text[end:]
1373������������text�=�text[2:]
1374��������#�Handle�case�of�add/delete�entire�line
1375��������else:
1376������������text�=�lines.pop(0)[2:]
1377������������#�if�line�of�text�is�just�a�newline,�insert�a�space�so�there�is
1378������������#�something�for�the�user�to�highlight�and�see.
1379������������if�len(text)�<=�1:
1380����������������text�=�'�'+text
1381������������#�insert�marks�that�won't�be�noticed�by�an�xml/html�escaper.
1382������������text�=�'\0'�+�format_key�+�text�+�'\1'
1383��������#�Return�line�of�text,�first�allow�user's�line�formatter�to�do�it's
1384��������#�thing�(such�as�adding�the�line�number)�then�replace�the�special
1385��������#�marks�with�what�the�user's�change�markup.
1386��������line_num�=�num_lines[side]
1387��������replacer�=�lambda�m�:�chgfmt(side,line_num,m.group(2),m.group(1))
1388��������return�marker_re.sub(replacer,linefmt(side,line_num,text))
1389����
1390����def�_line_iterator():
1391��������"""Yields�from/to�lines�of�text�with�a�change�indication.
1392�
1393��������This�function�is�an�iterator.��It�itself�pulls�lines�from�a
1394��������differencing�iterator,�processes�them�and�yields�them.��When�it�can
1395��������it�yields�both�a�"from"�and�a�"to"�line,�otherwise�it�will�yield�one
1396��������or�the�other.��Processing�includes�formatting�the�line�with�the�user's
1397��������line�formatter�(for�adding�line�numbering)�and�formatting�differences
1398��������using�the�user's�change�format�function.��In�addition�to�yielding�the
1399��������lines�of�from/to�text,�a�boolean�flag�is�yielded�to�indicate�if�the
1400��������text�line(s)�have�differences�in�them.
1401�
1402��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
1403��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
1404��������is�defined)�does�not�need�to�be�of�module�scope.
1405��������"""
1406��������lines�=�[]
1407��������num_blanks_pending,�num_blanks_to_yield�=�0,�0
1408��������while�True:��������
1409������������#�Load�up�next�4�lines�so�we�can�look�ahead,�create�strings�which
1410������������#�are�a�concatenation�of�the�first�character�of�each�of�the�4�lines
1411������������#�so�we�can�do�some�very�readable�comparisons.
1412������������while�len(lines)�<�4:
1413����������������try:
1414��������������������lines.append(diff_lines_iterator.next())
1415����������������except�StopIteration:
1416��������������������lines.append('X')
1417������������s�=�''.join([line[0]�for�line�in�lines])
1418������������if�s.startswith('X'):
1419����������������#�When�no�more�lines,�pump�out�any�remaining�blank�lines�so�the
1420����������������#�corresponding�add/delete�lines�get�a�matching�blank�line�so
1421����������������#�all�line�pairs�get�yielded�at�the�next�level.
1422����������������num_blanks_to_yield�=�num_blanks_pending
1423������������elif�s.startswith('-?+?'):
1424����������������#�simple�intraline�change
1425����������������yield�_make_line(lines,'?',0),�_make_line(lines,'?',1),�True
1426����������������continue
1427������������elif�s.startswith('--++'):
1428����������������#�in�delete�block,�add�block�coming:�we�do�NOT�want�to�get
1429����������������#�caught�up�on�blank�lines�yet,�just�process�the�delete�line
1430����������������num_blanks_pending�-=�1
1431����������������yield�_make_line(lines,'-',0),�None,�True
1432����������������continue
1433������������elif�s.startswith('--?+')�or�s.startswith('--+')�or�\
1434�����������������s.startswith('-�'):
1435����������������#�in�delete�block�and�see�a�intraline�change�or�unchanged�line
1436����������������#�coming:�yield�the�delete�line�and�then�blanks
1437����������������from_line,to_line�=�_make_line(lines,'-',0),�None
1438����������������num_blanks_to_yield,num_blanks_pending�=�num_blanks_pending-1,0
1439������������elif�s.startswith('-+?'):
1440����������������#�intraline�change
1441����������������yield�_make_line(lines,None,0),�_make_line(lines,'?',1),�True
1442����������������continue
1443������������elif�s.startswith('-?+'):
1444����������������#�intraline�change
1445����������������yield�_make_line(lines,'?',0),�_make_line(lines,None,1),�True
1446����������������continue
1447������������elif�s.startswith('-'):
1448����������������#�delete�FROM�line
1449����������������num_blanks_pending�-=�1
1450����������������yield�_make_line(lines,'-',0),�None,�True
1451����������������continue
1452������������elif�s.startswith('+--'):
1453����������������#�in�add�block,�delete�block�coming:�we�do�NOT�want�to�get
1454����������������#�caught�up�on�blank�lines�yet,�just�process�the�add�line
1455����������������num_blanks_pending�+=�1
1456����������������yield�None,�_make_line(lines,'+',1),�True
1457����������������continue
1458������������elif�s.startswith('+�')�or�s.startswith('+-'):
1459����������������#�will�be�leaving�an�add�block:�yield�blanks�then�add�line
1460����������������from_line,�to_line�=�None,�_make_line(lines,'+',1)
1461����������������num_blanks_to_yield,num_blanks_pending�=�num_blanks_pending+1,0
1462������������elif�s.startswith('+'):
1463����������������#�inside�an�add�block,�yield�the�add�line
1464����������������num_blanks_pending�+=�1
1465����������������yield�None,�_make_line(lines,'+',1),�True
1466����������������continue
1467������������elif�s.startswith('�'):
1468����������������#�unchanged�text,�yield�it�to�both�sides
1469����������������yield�_make_line(lines[:],None,0),_make_line(lines,None,1),False
1470����������������continue
1471������������#�Catch�up�on�the�blank�lines�so�when�we�yield�the�next�from/to
1472������������#�pair,�they�are�lined�up.
1473������������while(num_blanks_to_yield�<�0):
1474����������������num_blanks_to_yield�+=�1
1475����������������yield�None,linefmt(1,None,'\n'),True
1476������������while(num_blanks_to_yield�>�0):
1477����������������num_blanks_to_yield�-=�1
1478����������������yield�linefmt(0,None,'\n'),None,True
1479������������if�s.startswith('X'):
1480����������������raise�StopIteration
1481������������else:
1482����������������yield�from_line,to_line,True
1483�
1484����def�_line_pair_iterator():
1485��������"""Yields�from/to�lines�of�text�with�a�change�indication.
1486�
1487��������This�function�is�an�iterator.��It�itself�pulls�lines�from�the�line
1488��������iterator.��It's�difference�from�that�iterator�is�that�this�function
1489��������always�yields�a�pair�of�from/to�text�lines�(with�the�change
1490��������indication).��If�necessary�it�will�collect�single�from/to�lines
1491��������until�it�has�a�matching�pair�from/to�pair�to�yield.
1492�
1493��������Note,�this�function�is�purposefully�not�defined�at�the�module�scope�so
1494��������that�data�it�needs�from�its�parent�function�(within�whose�context�it
1495��������is�defined)�does�not�need�to�be�of�module�scope.
1496��������"""
1497��������line_iterator�=�_line_iterator()
1498��������fromlines,tolines=[],[]
1499��������while�True:
1500������������#�Collecting�lines�of�text�until�we�have�a�from/to�pair
1501������������while�(len(fromlines)==0�or�len(tolines)==0):
1502����������������from_line,�to_line,�found_diff�=line_iterator.next()
1503����������������if�from_line�is�not�None:
1504��������������������fromlines.append((from_line,found_diff))
1505����������������if�to_line�is�not�None:
1506��������������������tolines.append((to_line,found_diff))
1507������������#�Once�we�have�a�pair,�remove�them�from�the�collection�and�yield�it
1508������������from_line,�fromDiff�=�fromlines.pop(0)
1509������������to_line,�to_diff�=�tolines.pop(0)
1510������������yield�(from_line,to_line,fromDiff�or�to_diff)
1511�
1512����#�Handle�case�where�user�does�not�want�context�differencing,�just�yield
1513����#�them�up�without�doing�anything�else�with�them.
1514����line_pair_iterator�=�_line_pair_iterator()
1515����if�context�is�None�or�context�<=�0:
1516��������while�True:
1517������������yield�line_pair_iterator.next()
1518����#�Handle�case�where�user�wants�context�differencing.��We�must�do�some
1519����#�storage�of�lines�until�we�know�for�sure�that�they�are�to�be�yielded.
1520����else:
1521��������lines_to_write�=�0
1522��������insert_separator�=�False
1523��������while�True:
1524������������#�Store�lines�up�until�we�find�a�difference,�note�use�of�a
1525������������#�circular�queue�because�we�only�need�to�keep�around�what
1526������������#�we�need�for�context.
1527������������index,�contextLines�=�0,�[None]*(context)
1528������������found_diff�=�False
1529������������while(found_diff�is�False):
1530����������������from_line,�to_line,�found_diff�=�line_pair_iterator.next()
1531����������������i�=�index�%�context
1532����������������contextLines[i]�=�(from_line,�to_line,�found_diff)
1533����������������index�+=�1
1534������������#�Yield�lines�that�we�have�collected�so�far,�but�first�yield
1535������������#�the�user's�separator.
1536������������if�insert_separator:
1537����������������yield�sep,�sep,�None
1538������������else:
1539����������������insert_separator�=�True
1540������������if�index�>�context:
1541����������������lines_to_write�=�context
1542������������else:
1543����������������lines_to_write�=�index
1544����������������index�=�0
1545������������while(lines_to_write):
1546����������������i�=�index�%�context
1547����������������index�+=�1
1548����������������yield�contextLines[i]
1549����������������lines_to_write�-=�1
1550������������#�Now�yield�the�context�lines�after�the�change
1551������������lines_to_write�=�context-1
1552������������while(lines_to_write):
1553����������������from_line,�to_line,�found_diff�=�line_pair_iterator.next()
1554����������������#�If�another�change�within�the�context,�extend�the�context
1555����������������if�found_diff:
1556��������������������lines_to_write�=�context
1557����������������else:
1558��������������������lines_to_write�-=�1
1559����������������yield�from_line,�to_line,�found_diff
1560�
1561�
1562class�HtmlDiff(object):
1563����"""For�producing�HTML�side�by�side�comparison�with�change�highlights.
1564�
1565����This�class�can�be�used�to�create�an�HTML�table�(or�a�complete�HTML�file
1566����containing�the�table)�showing�a�side�by�side,�line�by�line�comparision
1567����of�text�with�inter-line�and�intra-line�change�highlights.��The�table�can�
1568����be�generated�in�either�full�or�contextual�difference�mode.��Additional�
1569����control�of�the�format�of�the�generated�difference�table�can�be�controlled�
1570����by�subclassing�and�overriding�the�appropriate�template�or�method.
1571�
1572����The�following�templates�and�methods�are�intended�for�subclass�overriding:
1573�
1574����file_template�--�controls�HTML�file�format
1575����styles�--�style�specifications�for�change�highlights
1576����table_template�--�controls�difference�table�format
1577����linenum_template�--�controls�format�of�line�number�column
1578����legend�--�legend�table�content
1579�
1580����format_line�--�method�to�markup�each�line
1581����format_change�--�method�to�provide�change�highlight�markup
1582�
1583����The�following�methods�are�provided�for�HTML�generation:
1584�
1585����make_table�--�generates�HTML�for�a�single�side�by�side�table
1586����make_file�--�generates�complete�HTML�file�with�a�single�side�by�side�table
1587�
1588����See�tools/scripts/diff.py�for�an�example�usage�of�this�class.����
1589����"""
1590�
1591����file_template�=�"""
1592��������<!DOCTYPE�html�PUBLIC�"-//W3C//DTD�XHTML�1.0�Transitional//EN"
1593������������"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1594��������<html>
1595��������<head>
1596��������<meta�http-equiv="Content-Type"�
1597��������������content="text/html;�charset=ISO-8859-1"�/>
1598��������<title>%(title)s</title>
1599��������<style�type="text/css">%(styles)s
1600��������</style>
1601��������</head>
1602��������<body>
1603��������%(header)s
1604��������%(table)s%(legend)s
1605��������</body>
1606��������</html>"""
1607�
1608����styles�=�"""
1609��������.diff_header�{background-color:#e0e0e0}
1610��������td.diff_header�{text-align:right}
1611��������.diff_next�{background-color:#c0c0c0}
1612��������.diff_add�{background-color:#aaffaa}
1613��������.diff_chg�{background-color:#ffff77}
1614��������.diff_sub�{background-color:#ffaaaa}"""
1615�
1616����linenum_template�=�"%d"
1617�
1618����table_template�=�"""
1619��������<table�summary="%(summary)s"�id="difflib_chg_%(prefix)s_top"�border="3"�
1620���������������cellspacing="0"�cellpadding="0"�rules="groups"�
1621���������������style="font-family:Courier">
1622������������<colgroup></colgroup>�<colgroup></colgroup>�<colgroup></colgroup>�
1623������������<colgroup></colgroup>�<colgroup></colgroup>
1624������������%(header_row)s<tbody>%(data_rows)s</tbody>
1625��������</table>"""
1626�
1627����legend�=�"""
1628��������<table�summary="Legends"�style="font-family:Courier">
1629������������<tr>�<th�colspan="2">�Legends�</th>�</tr>
1630������������<tr>�<td>�<table�border=""�summary="Colors">
1631��������������������������<tr><th>�Colors�</th>�</tr>
1632��������������������������<tr><td�class="diff_add">&nbsp;Added&nbsp;</td></tr>
1633��������������������������<tr><td�class="diff_chg">Changed</td>�</tr>
1634��������������������������<tr><td�class="diff_sub">Deleted</td>�</tr>
1635����������������������</table></td>
1636�����������������<td>�<table�border=""�summary="Links">
1637��������������������������<tr><th�colspan="2">�Links�</th>�</tr>
1638��������������������������<tr><td>(f)irst�change</td>�</tr>
1639��������������������������<tr><td>(n)ext�change</td>�</tr>
1640��������������������������<tr><td>(t)op</td>�</tr>
1641����������������������</table></td>�</tr>
1642��������</table>"""
1643�
1644����def�__init__(self,prefix=['from','to'],�linejunk=None,�
1645�����������������charjunk=IS_CHARACTER_JUNK):
1646��������"""HtmlDiff�instance�initializer
1647�
1648��������Arguments:
1649�
1650��������prefix�--�from/to�line�anchors�name�prefix�(useful�for�hyperlinking�to
1651������������specific�lines�in�a�table�when�multiple�tables�exist�so�that�the
1652������������correct�table�can�be�referenced)
1653��������linejunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
1654��������charjunk�--�passed�on�to�ndiff�(see�ndiff�documentation)
1655��������"""
1656��������self._prefix�=�prefix
1657��������self._linejunk�=�linejunk
1658��������self._charjunk�=�charjunk
1659��������import�xml.sax.saxutils�
1660��������self._escape�=�xml.sax.saxutils.escape
1661�
1662����def�make_file(self,fromlines,tolines,fromdesc='',todesc='',context=False,
1663������������������numlines=5,title='',header='',summary=''):
1664��������"""Returns�HTML�file�of�side�by�side�comparison�with�change�highlights
1665�
1666��������Arguments:
1667�
1668��������fromlines�--�list�of�"from"�lines
1669��������tolines�--�list�of�"to"�lines
1670��������fromdesc�--�"from"�file�column�header�string
1671��������todesc�--�"to"�file�column�header�string
1672��������context�--�set�to�True�for�contextual�differences
1673��������numlines�--�number�of�context�lines�(needed�for�full�differences�to
1674������������place�the�"next"�anchor�a�few�lines�ahead�of�the�next�change)
1675��������title�--�window�title�string
1676��������header�--�header�HTML�string�to�be�placed�above�table
1677��������summary�--�summary�attribute�of�table�string
1678��������"""
1679����������������
1680��������return�self.file_template�%�dict(
1681������������styles�=�self.styles,
1682������������legend�=�self.legend,
1683������������title�=�title,
1684������������header�=�header,
1685������������table�=�self.make_table(fromlines,tolines,fromdesc,todesc,
1686������������������������������������context=context,numlines=numlines,
1687������������������������������������summary=summary))
1688����
1689����def�make_table(self,fromlines,tolines,fromdesc='',todesc='',context=False,
1690�������������������numlines=5,summary=''):
1691��������"""Returns�HTML�table�of�side�by�side�comparison�with�change�highlights
1692�
1693��������Arguments:
1694��������fromlines�--�list�of�"from"�lines
1695��������tolines�--�list�of�"to"�lines
1696��������fromdesc�--�"from"�file�column�header�string
1697��������todesc�--�"to"�file�column�header�string
1698��������context�--�set�to�True�for�contextual�differences
1699��������numlines�--�number�of�context�lines�(needed�for�full�differences�to
1700������������place�the�"next"�anchor�a�few�lines�ahead�of�the�next�change)
1701��������summary�--�summary�attribute�of�table
1702��������"""
1703��������
1704��������if�context:
1705������������context�=�numlines
1706��������else:
1707������������context�=�0
1708��������prefix�=�self._prefix[1]
1709��������#�collect�up�from/to�lines�in�string,�difference�flags�in�a�list
1710��������from_text,�to_text,�diff_flags�=�[],[],[]
1711��������diffs�=�mdiff(fromlines,tolines,self.format_change,self.format_line,
1712����������������������context,None,linejunk=self._linejunk,
1713����������������������charjunk=self._charjunk)
1714��������for�from_line,�to_line,�found_diff�in�diffs:
1715������������from_text.append(from_line)
1716������������to_text.append(to_line)
1717������������diff_flags.append(found_diff)
1718��������#�process�change�flags,�generating�middle�column�of�next�anchors/links
1719��������next_id�=�['']*len(diff_flags)
1720��������next_href�=�['']*len(diff_flags)
1721��������num_chg,�in_change�=�0,�False
1722��������last�=�0
1723��������for�i,flag�in�enumerate(diff_flags):
1724������������if�flag:
1725����������������if�not�in_change:
1726��������������������in_change�=�True
1727��������������������last�=�i
1728��������������������#�at�the�beginning�of�a�change,�drop�an�anchor�a�few�lines
1729��������������������#�(the�context�lines)�before�the�change�for�the�previous�
1730��������������������#�link
1731��������������������i�=�max([0,i-numlines])
1732��������������������next_id[i]�=�'�id="difflib_chg_%s_%d"'�%�(prefix,num_chg)
1733��������������������#�at�the�beginning�of�a�change,�drop�a�link�to�the�next�
1734��������������������#�change
1735��������������������num_chg�+=�1
1736��������������������next_href[last]�=�'<a�href="#difflib_chg_%s_%d">n</a>'�%�(
1737�������������������������prefix,num_chg)
1738������������else:
1739����������������in_change�=�False
1740��������#�if�not�a�change�on�first�line,�drop�a�link
1741��������if�not�diff_flags[0]:
1742������������next_href[0]�=�'<a�href="#difflib_chg_%s_0">f</a>'�%�prefix
1743��������#�redo�the�last�link�to�link�to�the�top
1744��������next_href[last]�=�'<a�href="#difflib_chg_%s_top">t</a>'�%�(prefix)
1745��������import�cStringIO
1746��������s�=�cStringIO.StringIO()
1747��������for�i�in�range(len(diff_flags)):
1748������������if�diff_flags[i]�is�None:
1749����������������#�mdiff�yields�None�on�separator�lines
1750����������������s.write('</tbody><tbody>\n')
1751������������else:
1752����������������s.write('<tr>%s\n<td�class="diff_next"%s>%s</td>\n%s\n</tr>'�%
1753������������������������(from_text[i],next_id[i],next_href[i],to_text[i]))
1754��������if�fromdesc�or�todesc:
1755������������header_row�=�'<thead><tr>%s%s%s</tr></thead>'�%�(
1756����������������'<th�colspan="2"�class="diff_header">%s</th>'�%�fromdesc,
1757����������������'<th�class="diff_next"><br�/></th>',
1758����������������'<th�colspan="2"�class="diff_header">%s</th>'�%�todesc)
1759��������else:
1760������������header_row�=�''
1761��������return�self.table_template�%�dict(
1762������������summary=summary,
1763������������data_rows=s.getvalue(),
1764������������header_row=header_row,
1765������������prefix=prefix)
1766�
1767����def�format_line(self,side,linenum,text):
1768��������"""Returns�marked�up�"from"�or�"to"�text�line
1769�
1770��������mdiff()�will�call�this�function�with�the�following�arguments:
1771�
1772��������side�--�0�or�1�indicating�"from"�or�"to"�text
1773��������linenum�--�line�number�(used�for�line�number�column)
1774��������text�--�line�text�to�be�marked�up
1775��������"""
1776��������try:
1777������������linenum�=�self.linenum_template�%�linenum
1778������������id�=�'�id="%s%s"'�%�(self._prefix[side],linenum)
1779��������except�TypeError:
1780������������#�handle�blank�lines�where�linenum�is�None
1781������������linenum�=�''
1782������������id�=�''�
1783��������text�=�self._escape(text)
1784��������text�=�text.replace('�','&nbsp;')
1785��������fmt�=�'<td�class="diff_header"%s>%s</td><td�nowrap="nowrap">%s</td>'
1786��������return�fmt�%�(id,linenum,text)
1787�
1788����def�format_change(self,side,linenum,text,type):
1789��������"""Returns�HTML�highlighted�text
1790�
1791��������mdiff()�will�call�this�function�with�the�following�arguments:
1792��������side�--�0�or�1�indicating�"from"�or�"to"�text
1793��������linenum�--�line�number�that�contains�the�text�(used�for�creating�
1794�������������������"next"�links)
1795��������text�--�text�to�be�highlighted
1796��������type�--�+/-/^�indicating�type�of�change
1797��������"""
1798��������if�type�==�'+':
1799������������return�'<span�class="diff_add">%s</span>'�%�text
1800��������elif�type�==�'-':
1801������������return�'<span�class="diff_sub">%s</span>'�%�text
1802��������#�must�be�'^':
1803��������return�'<span�class="diff_chg">%s</span>'�%�text
1804�
1805del�re
1279 1806
1280def�restore(delta,�which): 1807def�restore(delta,�which):
1281����r""" 1808����r"""
1282����Generate�one�of�the�two�sequences�that�generated�a�delta. 1809����Generate�one�of�the�two�sequences�that�generated�a�delta.
1283 1810
1284����Given�a�`delta`�produced�by�`Differ.compare()`�or�`ndiff()`,�extract 1811����Given�a�`delta`�produced�by�`Differ.compare()`�or�`ndiff()`,�extract
1285����lines�originating�from�file�1�or�2�(parameter�`which`),�stripping�off�line 1812����lines�originating�from�file�1�or�2�(parameter�`which`),�stripping�off�line
1286����prefixes. 1813����prefixes.
1287 1814
1288����Examples: 1815����Examples:
1289 1816
Legends
Colors
�Added�
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op