class_docstr: Differ is a class for comparing sequences of lines of text, and
  producing human-readable differences or deltas.  Differ uses
  SequenceMatcher both to compare sequences of lines, and to compare
  sequences of characters within similar (near-matching) lines.
  
  Each line of a Differ delta begins with a two-letter code:
  
      '- '    line unique to sequence 1
      '+ '    line unique to sequence 2
      '  '    line common to both sequences
      '? '    line not present in either input sequence
  
  Lines beginning with '? ' attempt to guide the eye to intraline
  differences, and were not present in either input sequence.  These lines
  can be confusing if the sequences contain tab characters.
  
  Note that Differ makes no claim to produce a *minimal* diff.  To the
  contrary, minimal diffs are often counter-intuitive, because they synch
  up anywhere possible, sometimes accidental matches 100 pages apart.
  Restricting synch points to contiguous matches preserves some notion of
  locality, at the occasional cost of producing a longer diff.
  
  Example: Comparing two texts.
  
  First we set up the texts, sequences of individual single-line strings
  ending with newlines (such sequences can also be obtained from the
  `readlines()` method of file-like objects):
  
  >>> text1 = '''  1. Beautiful is better than ugly.
  ...   2. Explicit is better than implicit.
  ...   3. Simple is better than complex.
  ...   4. Complex is better than complicated.
  ... '''.splitlines(keepends=True)
  >>> len(text1)
  4
  >>> text1[0][-1]
  '\n'
  >>> text2 = '''  1. Beautiful is better than ugly.
  ...   3.   Simple is better than complex.
  ...   4. Complicated is better than complex.
  ...   5. Flat is better than nested.
  ... '''.splitlines(keepends=True)
  
  Next we instantiate a Differ object:
  
  >>> d = Differ()
  
  Note that when instantiating a Differ object we may pass functions to
  filter out line and character 'junk'.  See Differ.__init__ for details.
  
  Finally, we compare the two:
  
  >>> result = list(d.compare(text1, text2))
  
  'result' is a list of strings, so let's pretty-print it:
  
  >>> from pprint import pprint as _pprint
  >>> _pprint(result)
  ['    1. Beautiful is better than ugly.\n',
   '-   2. Explicit is better than implicit.\n',
   '-   3. Simple is better than complex.\n',
   '+   3.   Simple is better than complex.\n',
   '?     ++\n',
   '-   4. Complex is better than complicated.\n',
   '?            ^                     ---- ^\n',
   '+   4. Complicated is better than complex.\n',
   '?           ++++ ^                      ^\n',
   '+   5. Flat is better than nested.\n']
  
  As a single multi-line string it looks like this:
  
  >>> print(''.join(result), end="")
      1. Beautiful is better than ugly.
  -   2. Explicit is better than implicit.
  -   3. Simple is better than complex.
  +   3.   Simple is better than complex.
  ?     ++
  -   4. Complex is better than complicated.
  ?            ^                     ---- ^
  +   4. Complicated is better than complex.
  ?           ++++ ^                      ^
  +   5. Flat is better than nested.
docstr: When replacing one block of lines with another, search the blocks
  for *similar* lines; the best-matching pair (if any) is used as a
  synch point, and intraline difference marking is done on the
  similar pair. Lots of work, but often worth it.
  
  Example:
  
  >>> d = Differ()
  >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
  ...                            ['abcdefGhijkl\n'], 0, 1)
  >>> print(''.join(results), end="")
  - abcDefghiJkl
  ?    ^  ^  ^
  + abcdefGhijkl
  ?    ^  ^  ^
docstr_tok: ["When","replacing","one","block","of","lines","with","another",",","search","the","blocks","for","*","similar","*","lines",";","the","best-matching","pair","(","if","any",")","is","used","as","a","synch","point",",","and","intraline","difference","marking","is","done","on","the","similar","pair",".","Lots","of","work",",","but","often","worth","it",".","Example",":",">",">",">","d","=","Differ","(",")",">",">",">","results","=","d._fancy_replace","(","[","'abcDefghiJkl\\n","'","]",",","0",",","1",",","...","[","'abcdefGhijkl\\n","'","]",",","0",",","1",")",">",">",">","print","(","``",".join","(","results",")",",","end=","''","''",")","-","abcDefghiJkl","?","^","^","^","+","abcdefGhijkl","?","^","^","^"]
code: def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
      r"""
      When replacing one block of lines with another, search the blocks
      for *similar* lines; the best-matching pair (if any) is used as a
      synch point, and intraline difference marking is done on the
      similar pair. Lots of work, but often worth it.
  
      Example:
  
      >>> d = Differ()
      >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
      ...                            ['abcdefGhijkl\n'], 0, 1)
      >>> print(''.join(results), end="")
      - abcDefghiJkl
      ?    ^  ^  ^
      + abcdefGhijkl
      ?    ^  ^  ^
      """
  
      # don't synch up unless the lines have a similarity score of at
      # least cutoff; best_ratio tracks the best score seen so far
      best_ratio, cutoff = 0.74, 0.75
      cruncher = SequenceMatcher(self.charjunk)
      eqi, eqj = None, None  # 1st indices of equal lines (if any)
  
      # search for the pair that matches best without being identical
      # (identical lines must be junk lines, & we don't want to synch up
      # on junk -- unless we have to)
      for j in range(blo, bhi):
          bj = b[j]
          cruncher.set_seq2(bj)
          for i in range(alo, ahi):
              ai = a[i]
              if ai == bj:
                  if eqi is None:
                      eqi, eqj = i, j
                  continue
              cruncher.set_seq1(ai)
              # computing similarity is expensive, so use the quick
              # upper bounds first -- have seen this speed up messy
              # compares by a factor of 3.
              # note that ratio() is only expensive to compute the first
              # time it's called on a sequence pair; the expensive part
              # of the computation is cached by cruncher
              if (
                  cruncher.real_quick_ratio() > best_ratio
                  and cruncher.quick_ratio() > best_ratio
                  and cruncher.ratio() > best_ratio
              ):
                  best_ratio, best_i, best_j = cruncher.ratio(), i, j
      if best_ratio < cutoff:
          # no non-identical "pretty close" pair
          if eqi is None:
              # no identical pair either -- treat it as a straight replace
              yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
              return
          # no close pair, but an identical pair -- synch up on that
          best_i, best_j, best_ratio = eqi, eqj, 1.0
      else:
          # there's a close pair, so forget the identical pair (if any)
          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
      yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
  
      # 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,))
          yield from self._qformat(aelt, belt, atags, btags)
      else:
          # the synch pair is identical
          yield "  " + aelt
  
      # pump out diffs from after the synch point
      yield from self._fancy_helper(
          a, best_i + 1, ahi, b, best_j + 1, bhi
      )
  
imports: from heapq import nlargest
  from collections import namedtuple
  from types import GenericAlias
  import re
label_desc: Use image node_id 5 for calling the Differ obj's underlying member method code with example usage: obj._fancy_replace(a, alo, ahi, b, blo, bhi) without return types