cinap_lenrekadd hg and python4bb9e2215 years agoHistory
2019 lines · 78.9 KB · Python
RawDownload
            eqi = None         # a[best_i] very similar to b[best_j]; eqi is None iff they're not        # identical         # pump out diffs from before the synch point        for line in self._fancy_helper(a, alo, best_i, b, blo, best_j):            yield line         # do intraline marking on the synch pair        aelt, belt = a[best_i], b[best_j]        if eqi is None:            # pump out a '-', '?', '+', '?' quad for the synched lines            atags = btags = ""            cruncher.set_seqs(aelt, belt)            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():                la, lb = ai2 - ai1, bj2 - bj1                if tag == 'replace':                    atags += '^' * la                    btags += '^' * lb                elif tag == 'delete':                    atags += '-' * la                elif tag == 'insert':                    btags += '+' * lb                elif tag == 'equal':                    atags += ' ' * la                    btags += ' ' * lb                else:                    raise ValueError, 'unknown tag %r' % (tag,)            for line in self._qformat(aelt, belt, atags, btags):                yield line        else:            # the synch pair is identical            yield '  ' + aelt         # pump out diffs from after the synch point        for line in self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi):            yield line     def _fancy_helper(self, a, alo, ahi, b, blo, bhi):        g = []        if alo < ahi:            if blo < bhi:                g = self._fancy_replace(a, alo, ahi, b, blo, bhi)            else:                g = self._dump('-', a, alo, ahi)        elif blo < bhi:            g = self._dump('+', b, blo, bhi)         for line in g:            yield line     def _qformat(self, aline, bline, atags, btags):        r"""        Format "?" output and deal with leading tabs.         Example:         >>> d = Differ()        >>> results = d._qformat('\tabcDefghiJkl\n', '\t\tabcdefGhijkl\n',        ...                      '  ^ ^  ^      ', '+  ^ ^  ^      ')        >>> for line in results: print repr(line)        ...        '- \tabcDefghiJkl\n'        '? \t ^ ^  ^\n'        '+ \t\tabcdefGhijkl\n'        '? \t  ^ ^  ^\n'        """         # Can hurt, but will probably help most of the time.        common = min(_count_leading(aline, "\t"),                     _count_leading(bline, "\t"))        common = min(common, _count_leading(atags[:common], " "))        atags = atags[common:].rstrip()        btags = btags[common:].rstrip()         yield "- " + aline        if atags:            yield "? %s%s\n" % ("\t" * common, atags)         yield "+ " + bline        if btags:            yield "? %s%s\n" % ("\t" * common, btags) # With respect to junk, an earlier version of ndiff simply refused to# *start* a match with a junk element.  The result was cases like this:#     before: private Thread currentThread;#     after:  private volatile Thread currentThread;# If you consider whitespace to be junk, the longest contiguous match# not starting with junk is "e Thread currentThread".  So ndiff reported# that "e volatil" was inserted between the 't' and the 'e' in "private".# While an accurate view, to people that's absurd.  The current version# looks for matching blocks that are entirely junk-free, then extends the# longest one of those as far as possible but only with matching junk.# So now "currentThread" is matched, then extended to suck up the# preceding blank; then "private" is matched, and extended to suck up the# following blank; then "Thread" is matched; and finally ndiff reports# that "volatile " was inserted before "Thread".  The only quibble# remaining is that perhaps it was really the case that " volatile"# was inserted after "private".  I can live with that <wink>. import re def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match):    r"""    Return 1 for ignorable line: iff `line` is blank or contains a single '#'.     Examples:     >>> IS_LINE_JUNK('\n')    True    >>> IS_LINE_JUNK('  #   \n')    True    >>> IS_LINE_JUNK('hello\n')    False    """     return pat(line) is not None def IS_CHARACTER_JUNK(ch, ws=" \t"):    r"""    Return 1 for ignorable character: iff `ch` is a space or tab.     Examples:     >>> IS_CHARACTER_JUNK(' ')    True    >>> IS_CHARACTER_JUNK('\t')    True    >>> IS_CHARACTER_JUNK('\n')    False    >>> IS_CHARACTER_JUNK('x')    False    """     return ch in ws  def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',                 tofiledate='', n=3, lineterm='\n'):    r"""    Compare two sequences of lines; generate the delta as a unified diff.     Unified diffs are a compact way of showing line changes and a few    lines of context.  The number of context lines is set by 'n' which    defaults to three.     By default, the diff control lines (those with ---, +++, or @@) are    created with a trailing newline.  This is helpful so that inputs    created from file.readlines() result in diffs that are suitable for    file.writelines() since both the inputs and outputs have trailing    newlines.     For inputs that do not have trailing newlines, set the lineterm    argument to "" so that the output will be uniformly newline free.     The unidiff format normally has a header for filenames and modification    times.  Any or all of these may be specified using strings for    'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.  The modification    times are normally expressed in the format returned by time.ctime().     Example:     >>> for line in unified_diff('one two three four'.split(),    ...             'zero one tree four'.split(), 'Original', 'Current',    ...             'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:20:52 2003',    ...             lineterm=''):    ...     print line    --- Original Sat Jan 26 23:30:50 1991    +++ Current Fri Jun 06 10:20:52 2003    @@ -1,4 +1,4 @@    +zero     one    -two    -three    +tree     four    """     started = False    for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n):        if not started:            yield '--- %s %s%s' % (fromfile, fromfiledate, lineterm)            yield '+++ %s %s%s' % (tofile, tofiledate, lineterm)            started = True        i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]        yield "@@ -%d,%d +%d,%d @@%s" % (i1+1, i2-i1, j1+1, j2-j1, lineterm)        for tag, i1, i2, j1, j2 in group:            if tag == 'equal':                for line in a[i1:i2]:                    yield ' ' + line                continue            if tag == 'replace' or tag == 'delete':                for line in a[i1:i2]:                    yield '-' + line            if tag == 'replace' or tag == 'insert':                for line in b[j1:j2]:                    yield '+' + line # See http://www.unix.org/single_unix_specification/def context_diff(a, b, fromfile='', tofile='',                 fromfiledate='', tofiledate='', n=3, lineterm='\n'):    r"""    Compare two sequences of lines; generate the delta as a context diff.     Context diffs are a compact way of showing line changes and a few    lines of context.  The number of context lines is set by 'n' which    defaults to three.     By default, the diff control lines (those with *** or ---) are    created with a trailing newline.  This is helpful so that inputs    created from file.readlines() result in diffs that are suitable for    file.writelines() since both the inputs and outputs have trailing    newlines.     For inputs that do not have trailing newlines, set the lineterm    argument to "" so that the output will be uniformly newline free.     The context diff format normally has a header for filenames and    modification times.  Any or all of these may be specified using    strings for 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.    The modification times are normally expressed in the format returned    by time.ctime().  If not specified, the strings default to blanks.     Example:     >>> print ''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(1),    ...       'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current',    ...       'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:22:46 2003')),    *** Original Sat Jan 26 23:30:50 1991    --- Current Fri Jun 06 10:22:46 2003    ***************    *** 1,4 ****      one    ! two    ! three      four    --- 1,4 ----    + zero      one    ! tree      four    """     started = False    prefixmap = {'insert':'+ ', 'delete':'- ', 'replace':'! ', 'equal':'  '}    for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n):        if not started:            yield '*** %s %s%s' % (fromfile, fromfiledate, lineterm)            yield '--- %s %s%s' % (tofile, tofiledate, lineterm)            started = True         yield '***************%s' % (lineterm,)        if group[-1][2] - group[0][1] >= 2:            yield '*** %d,%d ****%s' % (group[0][1]+1, group[-1][2], lineterm)        else:            yield '*** %d ****%s' % (group[-1][2], lineterm)        visiblechanges = [e for e in group if e[0] in ('replace', 'delete')]        if visiblechanges:            for tag, i1, i2, _, _ in group:                if tag != 'insert':                    for line in a[i1:i2]:                        yield prefixmap[tag] + line         if group[-1][4] - group[0][3] >= 2:            yield '--- %d,%d ----%s' % (group[0][3]+1, group[-1][4], lineterm)        else:            yield '--- %d ----%s' % (group[-1][4], lineterm)        visiblechanges = [e for e in group if e[0] in ('replace', 'insert')]        if visiblechanges:            for tag, _, _, j1, j2 in group:                if tag != 'delete':                    for line in b[j1:j2]:                        yield prefixmap[tag] + line def ndiff(a, b, linejunk=None, charjunk=IS_CHARACTER_JUNK):    r"""    Compare `a` and `b` (lists of strings); return a `Differ`-style delta.     Optional keyword parameters `linejunk` and `charjunk` are for filter    functions (or None):     - linejunk: A function that should accept a single string argument, and      return true iff the string is junk.  The default is None, and is      recommended; as of Python 2.3, an adaptive notion of "noise" lines is      used that does a good job on its own.     - charjunk: A function that should accept a string of length 1. The      default is module-level function IS_CHARACTER_JUNK, which filters out      whitespace characters (a blank or tab; note: bad idea to include newline      in this!).     Tools/scripts/ndiff.py is a command-line front-end to this function.     Example:     >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),    ...              'ore\ntree\nemu\n'.splitlines(1))    >>> print ''.join(diff),    - one    ?  ^    + ore    ?  ^    - two    - three    ?  -    + tree    + emu    """    return Differ(linejunk, charjunk).compare(a, b) def _mdiff(fromlines, tolines, context=None, linejunk=None,           charjunk=IS_CHARACTER_JUNK):    r"""Returns generator yielding marked up from/to side by side differences.     Arguments:    fromlines -- list of text lines to compared to tolines    tolines -- list of text lines to be compared to fromlines    context -- number of context lines to display on each side of difference,               if None, all from/to text lines will be generated.    linejunk -- passed on to ndiff (see ndiff documentation)    charjunk -- passed on to ndiff (see ndiff documentation)     This function returns an interator which returns a tuple:    (from line tuple, to line tuple, boolean flag)     from/to line tuple -- (line num, line text)        line num -- integer or None (to indicate a context seperation)        line text -- original line text with following markers inserted:            '\0+' -- marks start of added text            '\0-' -- marks start of deleted text            '\0^' -- marks start of changed text            '\1' -- marks end of added/deleted/changed text     boolean flag -- None indicates context separation, True indicates        either "from" or "to" line contains a change, otherwise False.     This function/iterator was originally developed to generate side by side    file difference for making HTML pages (see HtmlDiff class for example    usage).     Note, this function utilizes the ndiff function to generate the side by    side difference markup.  Optional ndiff arguments may be passed to this    function and they in turn will be passed to ndiff.    """    import re     # regular expression for finding intraline change indices    change_re = re.compile('(\++|\-+|\^+)')     # create the difference iterator to generate the differences    diff_lines_iterator = ndiff(fromlines,tolines,linejunk,charjunk)     def _make_line(lines, format_key, side, num_lines=[0,0]):        """Returns line of text with user's change markup and line formatting.         lines -- list of lines from the ndiff generator to produce a line of                 text from.  When producing the line of text to return, the                 lines used are removed from this list.        format_key -- '+' return first line in list with "add" markup around                          the entire line.                      '-' return first line in list with "delete" markup around                          the entire line.                      '?' return first line in list with add/delete/change                          intraline markup (indices obtained from second line)                      None return first line in list with no markup        side -- indice into the num_lines list (0=from,1=to)        num_lines -- from/to current line number.  This is NOT intended to be a                     passed parameter.  It is present as a keyword argument to                     maintain memory of the current line numbers between calls                     of this function.         Note, this function is purposefully not defined at the module scope so        that data it needs from its parent function (within whose context it        is defined) does not need to be of module scope.        """        num_lines[side] += 1        # Handle case where no user markup is to be added, just return line of        # text with user's line format to allow for usage of the line number.        if format_key is None:            return (num_lines[side],lines.pop(0)[2:])        # Handle case of intraline changes        if format_key == '?':            text, markers = lines.pop(0), lines.pop(0)            # find intraline changes (store change type and indices in tuples)            sub_info = []            def record_sub_info(match_object,sub_info=sub_info):                sub_info.append([match_object.group(1)[0],match_object.span()])                return match_object.group(1)            change_re.sub(record_sub_info,markers)            # process each tuple inserting our special marks that won't be            # noticed by an xml/html escaper.            for key,(begin,end) in sub_info[::-1]:                text = text[0:begin]+'\0'+key+text[begin:end]+'\1'+text[end:]            text = text[2:]        # Handle case of add/delete entire line        else:            text = lines.pop(0)[2:]            # if line of text is just a newline, insert a space so there is            # something for the user to highlight and see.            if not text:                text = ' '            # insert marks that won't be noticed by an xml/html escaper.            text = '\0' + format_key + text + '\1'        # Return line of text, first allow user's line formatter to do its        # thing (such as adding the line number) then replace the special        # marks with what the user's change markup.        return (num_lines[side],text)     def _line_iterator():        """Yields from/to lines of text with a change indication.         This function is an iterator.  It itself pulls lines from a        differencing iterator, processes them and yields them.  When it can        it yields both a "from" and a "to" line, otherwise it will yield one        or the other.  In addition to yielding the lines of from/to text, a        boolean flag is yielded to indicate if the text line(s) have        differences in them.         Note, this function is purposefully not defined at the module scope so        that data it needs from its parent function (within whose context it        is defined) does not need to be of module scope.        """        lines = []        num_blanks_pending, num_blanks_to_yield = 0, 0        while True:            # Load up next 4 lines so we can look ahead, create strings which            # are a concatenation of the first character of each of the 4 lines            # so we can do some very readable comparisons.            while len(lines) < 4:                try:                    lines.append(diff_lines_iterator.next())                except StopIteration:                    lines.append('X')            s = ''.join([line[0] for line in lines])            if s.startswith('X'):                # When no more lines, pump out any remaining blank lines so the                # corresponding add/delete lines get a matching blank line so                # all line pairs get yielded at the next level.                num_blanks_to_yield = num_blanks_pending            elif s.startswith('-?+?'):                # simple intraline change                yield _make_line(lines,'?',0), _make_line(lines,'?',1), True                continue            elif s.startswith('--++'):                # in delete block, add block coming: we do NOT want to get                # caught up on blank lines yet, just process the delete line                num_blanks_pending -= 1                yield _make_line(lines,'-',0), None, True                continue            elif s.startswith(('--?+', '--+', '- ')):                # in delete block and see a intraline change or unchanged line                # coming: yield the delete line and then blanks                from_line,to_line = _make_line(lines,'-',0), None                num_blanks_to_yield,num_blanks_pending = num_blanks_pending-1,0            elif s.startswith('-+?'):                # intraline change                yield _make_line(lines,None,0), _make_line(lines,'?',1), True                continue            elif s.startswith('-?+'):                # intraline change                yield _make_line(lines,'?',0), _make_line(lines,None,1), True                continue            elif s.startswith('-'):                # delete FROM line                num_blanks_pending -= 1                yield _make_line(lines,'-',0), None, True                continue            elif s.startswith('+--'):                # in add block, delete block coming: we do NOT want to get                # caught up on blank lines yet, just process the add line                num_blanks_pending += 1                yield None, _make_line(lines,'+',1), True                continue            elif s.startswith(('+ ', '+-')):                # will be leaving an add block: yield blanks then add line                from_line, to_line = None, _make_line(lines,'+',1)                num_blanks_to_yield,num_blanks_pending = num_blanks_pending+1,0            elif s.startswith('+'):                # inside an add block, yield the add line                num_blanks_pending += 1                yield None, _make_line(lines,'+',1), True                continue            elif s.startswith(' '):                # unchanged text, yield it to both sides                yield _make_line(lines[:],None,0),_make_line(lines,None,1),False                continue            # Catch up on the blank lines so when we yield the next from/to            # pair, they are lined up.            while(num_blanks_to_yield < 0):                num_blanks_to_yield += 1                yield None,('','\n'),True            while(num_blanks_to_yield > 0):                num_blanks_to_yield -= 1                yield ('','\n'),None,True            if s.startswith('X'):                raise StopIteration            else:                yield from_line,to_line,True 

Lines 1001–1500 of 2019. Previous lines