Path 1: 45 calls (0.45)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2', 'line 3', 'line 4 changed', 'line 5 changed', 'line 6 changed', 'line 7', '...

1 (13) 11 (7) 16 (4) 31 (4) 0 (4) 5 (3) 6 (3) 7 (3) 3 (2) 26 (1)

8 (9) 3 (7) 2 (5) 18 (4) 33 (4) 4 (4) 15 (3) 14 (3) 5 (2) 13 (1)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2 added', 'line 3', 'line 4 chanGEd', 'line 5a chanGed', 'line 6a changEd', 'l...

1 (13) 11 (7) 16 (4) 31 (4) 0 (4) 5 (3) 6 (3) 7 (3) 3 (2) 26 (1)

2 (9) 8 (9) 17 (4) 32 (4) 4 (4) 1 (4) 15 (3) 14 (3) 5 (2) 12 (1)

None (45) '? ^^\n' (34) '- 1. Beautiful is beTTer than ugly.' (18) '? ^ ^\n' (18) '+ 1. Beautiful is better tha...

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 2: 19 calls (0.19)

['', ' 1. Beautiful is beTTer than ugly.', ' 2. Explicit is better than implicit.', ' 3. Simple is better than complex.', ' 4. Complex is bett...

1 (7) 16 (4) 31 (4) 0 (1) 11 (1) 26 (1) 41 (1)

5 (6) 20 (4) 35 (4) 1 (1) 15 (1) 30 (1) 45 (1) 4 (1)

['', ' 1. Beautiful is better than ugly.', ' 3. Simple is better than complex.', ' 4. Complicated is better than complex.', ' 5. Flat is bet...

1 (7) 16 (4) 31 (4) 0 (1) 11 (1) 26 (1) 41 (1)

5 (6) 20 (4) 35 (4) 1 (1) 15 (1) 30 (1) 45 (1) 4 (1)

'? ^^\n' (34) None (19) '- 1. Beautiful is beTTer than ugly.' (17) '+ 1. Beautiful is better than ugly.' (17) '- 2. Expl...

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 3: 18 calls (0.18)

['', ' 1. Beautiful is beTTer than ugly.', ' 2. Explicit is better than implicit.', ' 3. Simple is better than complex.', ' 4. Complex is bett...

4 (7) 19 (4) 34 (4) 14 (1) 29 (1) 44 (1)

5 (7) 20 (4) 35 (4) 15 (1) 30 (1) 45 (1)

['', ' 1. Beautiful is better than ugly.', ' 3. Simple is better than complex.', ' 4. Complicated is better than complex.', ' 5. Flat is bet...

3 (7) 18 (4) 33 (4) 13 (1) 28 (1) 43 (1)

5 (7) 20 (4) 35 (4) 15 (1) 30 (1) 45 (1)

'- 4. Complex is better than complicated.' (18) '+ 5. Flat is better than nested.' (18) '+ 4. Complicated is better than complex.' (17) '+ ...

None (18)

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 4: 6 calls (0.06)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2', 'line 3', 'line 4 changed', 'line 5 changed', 'line 6 changed', 'line 7', '...

3 (3) 9 (3)

4 (3) 10 (3)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2 added', 'line 3', 'line 4 chanGEd', 'line 5a chanGed', 'line 6a changEd', 'l...

3 (3) 9 (3)

4 (3) 10 (3)

'- line 2' (3) '+ line 2 added' (3) '- line 8 subtracted' (3) '+ line 8' (3)

None (6)

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 5: 5 calls (0.05)

['one\n', 'two\n', 'three\n'] (3) ['', '\t\t\t\tLine 1: preceded by from:[tt] to:[ssss]', ' \t\t\t\tLine 2: preceded by from:[sstt] to:[sssst]', ' \...

0 (3) 1 (2)

3 (3) 6 (2)

['ore\n', 'tree\n', 'emu\n'] (3) ['', ' Line 1: preceded by from:[tt] to:[ssss]', ' \t\tLine 2: preceded by from:[sstt] to:[sssst]', ' Line...

0 (3) 1 (2)

3 (3) 6 (2)

'? ^\n' (6) None (5) '? ^^^^\n' (3) '? ^^\n' (3) '? ^^^^\n' (3) '? ^\n' (3) '- one\n' (3) '+ ore\n' (3) '- two\n' (3) '- three\n' (3)

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 6: 4 calls (0.04)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2', 'line 3', 'line 4 changed', 'line 5 changed', 'line 6 changed', 'line 7', '...

11 (3) 0 (1)

13 (3) 1 (1)

['line 0', '1234567890123456789012345689012345', 'line 1', 'line 2 added', 'line 3', 'line 4 chanGEd', 'line 5a chanGed', 'line 6a changEd', 'l...

11 (3) 0 (1)

13 (3) 1 (1)

'- 1234567890123456789012345689012345' (3) '- short line' (3) '+ 1234567890' (3) '+ another long line that needs to be wrapped' (3) '- 2' (1) '+ 3' (1...

None (4)

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 7: 1 calls (0.01)

['\tI am a buggy'] (1)

0 (1)

1 (1)

['\t\tI am a bug'] (1)

0 (1)

1 (1)

'- \tI am a buggy' (1) '? \t --\n' (1) '+ \t\tI am a bug' (1) '? +\n' (1) None (1)

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 8: 1 calls (0.01)

['', ' 1. Beautiful is beTTer than ugly.', ' 2. Explicit is better than ımplıcıt.', ' 3. Simple is better than complex.', ' 4. Complex is bett...

1 (1)

5 (1)

['', ' 1. Beautiful is better than ügly.', ' 3. Sımple is better than complex.', ' 4. Complicated is better than cömplex.', ' 5. Flat is bet...

1 (1)

5 (1)

'? ^^ ^\n' (2) '- 1. Beautiful is beTTer than ugly.' (1) '+ 1. Beautiful is better than ügly.' (1) '- 2. Explicit...

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
            

Path 9: 1 calls (0.01)

[' 1. Beautiful is better than ugly.\n', ' 2. Explicit is better than implicit.\n', ' 3. Simple is better than complex.\n', ' 4. Complex is better...

3 (1)

4 (1)

[' 1. Beautiful is better than ugly.\n', ' 3. Simple is better than complex.\n', ' 4. Complicated is better than complex.\n', ' 5. Flat is bette...

2 (1)

4 (1)

'- 4. Complex is better than complicated.\n' (1) '? ^ ---- ^\n' (1) '+ 4. Complicated is better than complex.\n' (1...

1def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2        r"""
3        When replacing one block of lines with another, search the blocks
4        for *similar* lines; the best-matching pair (if any) is used as a
5        synch point, and intraline difference marking is done on the
6        similar pair. Lots of work, but often worth it.
7
8        Example:
9
10        >>> d = Differ()
11        >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
12        ...                            ['abcdefGhijkl\n'], 0, 1)
13        >>> print(''.join(results), end="")
14        - abcDefghiJkl
15        ?    ^  ^  ^
16        + abcdefGhijkl
17        ?    ^  ^  ^
18        """
19
20        # don't synch up unless the lines have a similarity score of at
21        # least cutoff; best_ratio tracks the best score seen so far
22        best_ratio, cutoff = 0.74, 0.75
23        cruncher = SequenceMatcher(self.charjunk)
24        eqi, eqj = None, None   # 1st indices of equal lines (if any)
25
26        # search for the pair that matches best without being identical
27        # (identical lines must be junk lines, & we don't want to synch up
28        # on junk -- unless we have to)
29        for j in range(blo, bhi):
30            bj = b[j]
31            cruncher.set_seq2(bj)
32            for i in range(alo, ahi):
33                ai = a[i]
34                if ai == bj:
35                    if eqi is None:
36                        eqi, eqj = i, j
37                    continue
38                cruncher.set_seq1(ai)
39                # computing similarity is expensive, so use the quick
40                # upper bounds first -- have seen this speed up messy
41                # compares by a factor of 3.
42                # note that ratio() is only expensive to compute the first
43                # time it's called on a sequence pair; the expensive part
44                # of the computation is cached by cruncher
45                if cruncher.real_quick_ratio() > best_ratio and \
46                      cruncher.quick_ratio() > best_ratio and \
47                      cruncher.ratio() > best_ratio:
48                    best_ratio, best_i, best_j = cruncher.ratio(), i, j
49        if best_ratio < cutoff:
50            # no non-identical "pretty close" pair
51            if eqi is None:
52                # no identical pair either -- treat it as a straight replace
53                yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
54                return
55            # no close pair, but an identical pair -- synch up on that
56            best_i, best_j, best_ratio = eqi, eqj, 1.0
57        else:
58            # there's a close pair, so forget the identical pair (if any)
59            eqi = None
60
61        # a[best_i] very similar to b[best_j]; eqi is None iff they're not
62        # identical
63
64        # pump out diffs from before the synch point
65        yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
66
67        # do intraline marking on the synch pair
68        aelt, belt = a[best_i], b[best_j]
69        if eqi is None:
70            # pump out a '-', '?', '+', '?' quad for the synched lines
71            atags = btags = ""
72            cruncher.set_seqs(aelt, belt)
73            for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
74                la, lb = ai2 - ai1, bj2 - bj1
75                if tag == 'replace':
76                    atags += '^' * la
77                    btags += '^' * lb
78                elif tag == 'delete':
79                    atags += '-' * la
80                elif tag == 'insert':
81                    btags += '+' * lb
82                elif tag == 'equal':
83                    atags += ' ' * la
84                    btags += ' ' * lb
85                else:
86                    raise ValueError('unknown tag %r' % (tag,))
87            yield from self._qformat(aelt, belt, atags, btags)
88        else:
89            # the synch pair is identical
90            yield '  ' + aelt
91
92        # pump out diffs from after the synch point
93        yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)