..\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